pax_global_header00006660000000000000000000000064132175054640014521gustar00rootroot0000000000000052 comment=280bb1c2dffdbb2dd7d465af47422eb3e631cc37 DarkRadiant-2.5.0/000077500000000000000000000000001321750546400137115ustar00rootroot00000000000000DarkRadiant-2.5.0/.gitattributes000066400000000000000000000007431321750546400166100ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp *.sln merge=union *.csproj merge=union *.vbproj merge=union *.fsproj merge=union *.dbproj merge=union # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain DarkRadiant-2.5.0/.gitignore000066400000000000000000000013301321750546400156760ustar00rootroot00000000000000# Linux build files *.o *.lo *.la Makefile .libs .deps # Editor temporary files *.swp # Windows directories w32deps w64deps build # Windows build files *.dll *.pdb *.exe *.exp *.lib # Windows Visual Studio stuff *.user DarkRadiant.opensdf DarkRadiant.sdf gtkrc testsuite.ilk # VS 2012 /winbuild /tools\msvc2012/ipch /tools\msvc2012/Debug *.psess *.suo /tools\msvc2010/ipch /tools\msvc2012/ipch *.ipch *.dirstamp /tools/innosetup/*.7z *.vspx *.bak *.opendb tools/msvc2015/DarkRadiant.VC.db *.iobj *.ipdb # Mac OS X Stuff .DS_Store xcuserdata Makefile.in /config.h.in /configure /ltmain.sh /aclocal.m4 /install/*.zip /tools/msvc/.vs /tools/msvc/DarkRadiant.VC.db /tools/innosetup/*.zip /tools/lwo_analyse/.vs /radiant/*.aps DarkRadiant-2.5.0/AUTHORS000066400000000000000000000032261321750546400147640ustar00rootroot00000000000000 OrbWeaver greebo STiFU mohij angua Crispy Gildoran Jesps (Bulge patch algorithm) GtkRadiant CONTRIBUTORS and CREDITS last update: 09/12/2004 ======================= Loki ---- Leonardo Zide leo@lokigames.com Mike Phillips (Loki QA) Bernd Kreimeier (overall coordination) QER.com ------- TTimo timo@idsoftware.com ^Fishman (Pablo Zurita) fish@gamedesign.net RR2DO2 rr2do2@q3f.com SmallPileofGibs spog@planetquake.com Curry plugin ------------ Mike "mickey" Jackman Tim "Maj" Rennie PrtView plugin, various bug fixes and q3map guru ------------------------------------------------ Geoffrey DeWan Gensurf plugin -------------- David Hyde PicoModel --------- seaw0lf with assist by ydnar Q3Map2 ------ Randy 'ydnar' Reddig Updated shader files, textures, entities.def, keyboard shortcut list overall testing and feedback ---------------------------- Jean-Francois "Eutectic" Groleau Improvements and bug fixing --------------------------- Jan Paul "MrElusive" van Waveren Robert Duffy Forest "LordHavoc" Wroncy-Hale Nurail AcidDeath Chronos Michael Schlueter Jamie Wilkinson Robert "Tr3B" Beckebans Web --- Dave "Bargle" Koenig Jason "Wolfen" Spencer Shawn "EvilTypeGuy" Walker Thanks to John Hutton, AstroCreep and W2k for web help FAQ --- Equim and Wex Testing/Feedback --- Black_Dog, d0nkey, Fjoggis, Jago, jetscreamer, gibbie, Godmil, Gom Jabbar, Mindlink, mslaf, necros, Promit, Ravo, RPG, scampie, sock, sponge, thiste, voodoochopsticks, Zwiffle Misc ---- Thanks to everyone on the beta mailing list and irc.telefragged.com #qeradiant for testing and feedback. Updated icons by AstroCreep! Bitch-slapping by RaYGunn! Last minute bugs by SPoG! (SPoG--) DarkRadiant-2.5.0/Doxyfile000066400000000000000000001515111321750546400154230ustar00rootroot00000000000000# Doxyfile 1.5.3 # 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 = DarkRadiant # 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 = dox # 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 = YES # 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 = # 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 = # 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 = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = 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 # 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 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be extracted # and appear in the documentation as a namespace called 'anonymous_namespace{file}', # where file will be replaced with the base name of the file that contains the anonymous # namespace. By default anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = 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 = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = 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 , where is the value of # the FILE_VERSION_FILTER tag, and 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 = YES # 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 = 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 # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = include libs radiant plugins # 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 FILE_PATTERNS = # 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 # 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 = plugins/dm.d3hook/boost \ plugins/dm.d3hook/RCF \ plugins/dm.d3hook/SF # 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 , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. 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 = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = 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 = a4 # 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 = 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 # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = 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 = # 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 = YES # 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 = NO # 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 # 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 = NO # 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, 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 = NO # 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 = 0 # 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 DarkRadiant-2.5.0/GPL000066400000000000000000000431331321750546400142620ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. DarkRadiant-2.5.0/LICENSE000066400000000000000000000040421321750546400147160ustar00rootroot00000000000000DarkRadiant License (last update: 2017-09-12) ---------------------------------------------------------------------------------------------- DarkRadiant is free software originally based on GtkRadiant, which has been licensed under the GNU General Public License (GPLv2) http://www.gnu.org/licenses/gpl-2.0.html Therefore all of the DarkRadiant source code (save a few exceptions noted below) is published under the same GPLv2 license. Unless stated otherwise in the source file header, the following applies to the DarkRadiant source code: DarkRadiant - Open Source Level Editor for Doom 3 and The Dark Mod Copyright (C) 2017 Matthias Baumann (on behalf of the DarkRadiant Team, see AUTHORS file) ========================================================================================== 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA ========================================================================================== Exceptions to the above ---------------------------------------------------------------------------------------------- PicoModel Library - published under the modified Berkeley Software Distribution (BSD) license DDS Library - published under the modified Berkeley Software Distribution (BSD) license pybind11 Library - see the LICENSE file in the libs/pybind folder fmtlib Library - see the LICENSE file in the libs/libfmt folder (BSD 2-clause "Simplified" License) DarkRadiant-2.5.0/Makefile.am000066400000000000000000000024571321750546400157550ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 SUBDIRS = libs radiant plugins install/i18n # Install data and stuff gamedir = $(pkgdatadir)/games game_DATA = $(top_srcdir)/install/games/*.game bitmapsdir = $(pkgdatadir)/bitmaps bitmaps_DATA = $(top_srcdir)/install/bitmaps/* applicationsdir = $(datadir)/applications applications_DATA = $(top_builddir)/install/darkradiant.desktop # Install XML config files pkgdata_DATA = $(top_srcdir)/install/colours.xml \ $(top_srcdir)/install/commandsystem.xml \ $(top_srcdir)/install/user.xml \ $(top_srcdir)/install/menu.xml \ $(top_srcdir)/install/input.xml \ $(top_srcdir)/install/debug.xml # Install GL programs glprogsdir = $(pkgdatadir)/gl glprogs_DATA = $(top_srcdir)/install/gl/* # Install fonts fontsdir = $(pkgdatadir)/ui/fonts fonts_DATA = $(top_srcdir)/install/ui/fonts/* # Install UI files uifilesdir = $(pkgdatadir)/ui uifiles_DATA = $(top_srcdir)/install/ui/*.xrc # Install python scripts scriptsdir = $(pkglibdir)/scripts scripts_DATA = $(top_srcdir)/install/scripts/*.py commandsdir = $(scriptsdir)/commands commands_DATA = $(top_srcdir)/install/scripts/commands/*.py # Install sourceviewer synax and colour files #sourceviewerdir = $(pkglibdir)/sourceviewer #sourceviewer_DATA = $(top_srcdir)/install/sourceviewer/* EXTRA_DIST = config.rpath DarkRadiant-2.5.0/NEWS000066400000000000000000000000001321750546400143760ustar00rootroot00000000000000DarkRadiant-2.5.0/PKGBUILD000066400000000000000000000011671321750546400150420ustar00rootroot00000000000000# Maintainer: codereader pkgname=darkradiant pkgver=2.5.0 pkgrel=1 pkgdesc="Level Editor for Doom 3 (idTech4) and The Dark Mod" arch=("x86_64") url="http://www.darkradiant.net/" license=("GPL") depends=(wxgtk ftgl glew boost-libs freealut libvorbis python libsigc++) makedepends=(git boost webkitgtk2) source=("$pkgname::git+https://github.com/codereader/DarkRadiant.git#tag=2.5.0") md5sums=("SKIP") build() { cd "$pkgname" ./autogen.sh ./configure --prefix=/usr --enable-darkmod-plugins make } check() { cd "$pkgname" make -k check } package() { cd "$pkgname" make DESTDIR="$pkgdir/" install } DarkRadiant-2.5.0/README.md000066400000000000000000000122711321750546400151730ustar00rootroot00000000000000# DarkRadiant DarkRadiant is a level (map) editor for the **The Dark Mod**, an open-source Doom 3 modification which is available at www.thedarkmod.com # Getting started DarkRadiant requires game resources to work with, these resources are not installed by this editor. You'll need to point DarkRadiant to one of these games (The Dark Mod, Doom 3, Quake 4, etc.) before you can start to work on your map. Visit www.thedarkmod.com for download instructions, then proceed with one of the tutorials available on the web. For The Dark Mod mappers, there's an extensive [Beginner's Guide](http://wiki.thedarkmod.com/index.php?title=A_-_Z_Beginner_Full_Guide_Start_Here!) on the project's wiki, which covers a lot of steps almost from scratch. Moreover, a couple of excellent video tutorials are available, linked from the top of that wiki article above. # Compiling on Windows ## Prerequisites DarkRadiant is built on Windows using *Microsoft Visual C++ 2017*. The free Community Edition can be obtained here: *VC++ 2017:* https://www.visualstudio.com/downloads/ (Choose Visual Studio Community) Since DarkRadiant uses a couple of open-source libraries that are not available on Windows by default, you will also need to download and install the dependencies. 7-Zip packages of the dependencies are available at the following URL(s). [Get 7-zip here](http://www.7-zip.org/) https://github.com/codereader/DarkRadiant/releases/download/2.5.0/windeps.7z The dependencies packages need to be extracted into the main DarkRadiant source directory, i.e. alongside the `include/` and `radiant/` directories. Just drop the windeps.7z in the DarkRadiant folder and use 7-zip's "Extract to here" ## Build The main Visual C++ solution file is: Visual Studio 2017: `tools/msvc/DarkRadiant.sln` Open this file with Visual Studio and start a build by right-clicking on the top-level "Solution 'DarkRadiant'" item and choosing Build Solution. The DarkRadiant.exe file will be placed into the `install/` folder. # Compiling on Linux ## Prerequisites To compile DarkRadiant, a number of libraries (with development headers) and a standards-compliant C++11 compiler (GCC 5.3+) are required. On an Ubuntu system, the requirements may include any or all of the following packages: * zlib1g-dev * libjpeg62-dev * libwxgtk3.0-dev * libxml2-dev * libsigc++-2.0-dev * libpng-dev * libftgl-dev * libglew-dev * libalut-dev * libvorbis-dev * pybind11-dev (Ubuntu 17 and later) This does not include core development tools such as g++ or the git client to download the sources (use sudo apt-get install git for that). One possible set of packages might be: `sudo apt-get install git automake libtool g++ gettext pkg-config` More required package lists for various Linux distributions are [listed in the Wiki Article](http://wiki.thedarkmod.com/index.php?title=DarkRadiant_-_Compiling_in_Linux). ## Build To build DarkRadiant the standard Autotools build process is used: ``` ./autogen.sh ./configure make sudo make install ``` The available configure options are listed with `./configure --help`. There are options for debug builds, and enabling or disabling various optional components such as audio support and the Dark Mod-specific plugins (`--enable-darkmod-plugins`). # Compiling on OS X ## Prerequisites To compile DarkRadiant, a number of libraries (with development headers) are required. You can obtain them by using [MacPorts](https://distfiles.macports.org/MacPorts/): Install MacPorts, then open a fresh console and issue these commands: ``` sudo port install jpeg wxwidgets-3.0 pkgconfig libsigcxx2 freetype ftgl glew sudo port install boost libxml2 freealut libvorbis libogg openal ``` ## Build Start Xcode and open the project file in `tools/xcode/DarkRadiant.xcodeproj`. Hit CMD-B to start the build, the output files will be placed to a folder similar to this: `~/Library/Developer/Xcode/DerivedData/DarkRadiant-somethingsomething/Build/Products/Release` The `DarkRadiant.app` package in that folder can be launched right away or copied to some location of your preference. # More Build Information A more detailed compilation guide can be found on The Dark Mod's wiki: http://wiki.thedarkmod.com/index.php?title=DarkRadiant_-_Compilation_Guide # Contact / Discussion DarkRadiant Website: http://www.darkradiant.net All discussion is ongoing primarily at [The Dark Mod Forums](http://forums.thedarkmod.com/forum/51-darkradiant-feedback-and-development/), where you can get in touch with knowledgeable people and discuss changes or issues. If you happen to run into a bug, you're encouraged to report it to us, especially when running into application crashes (see also [How to record a crashdump](http://wiki.thedarkmod.com/index.php?title=Save_a_Memory_Dump_for_debugging_Crashes)). The issue tracker for DarkRadiant is also run by the Dark Mod folks: [DarkRadiant Bugtracker](http://bugs.thedarkmod.com/view_all_bug_page.php?project_id=1). # License The DarkRadiant source code is published under the [GNU General Public License 2.0 (GPLv2)](http://www.gnu.org/licenses/gpl-2.0.html ), except for a few libraries which are using the BSD license, see the [LICENSE](https://raw.githubusercontent.com/codereader/DarkRadiant/master/LICENSE) file for further notes.DarkRadiant-2.5.0/autogen.sh000077500000000000000000000003571321750546400157170ustar00rootroot00000000000000#!/usr/bin/env bash aclocal -I m4 # Use the glibtoolize command in OSX case "$OSTYPE" in darwin*) glibtoolize --force --copy ;; *) libtoolize --force --copy ;; esac autoheader autoconf automake --add-missing --copy --foreign DarkRadiant-2.5.0/compile000077500000000000000000000162451321750546400152770ustar00rootroot00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey . # # 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 2, 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 . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: DarkRadiant-2.5.0/config.guess000077500000000000000000001270611321750546400162400ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, # Inc. timestamp='2007-07-22' # This file 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 2 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, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner . # Please send patches to . Submit a context # diff and a properly formatted ChangeLog entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # The plan is that this can be called by configure scripts if you # don't specify an explicit build system type. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep __ELF__ >/dev/null then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` exit ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm:riscos:*:*|arm:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[45]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep __LP64__ >/dev/null then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) case ${UNAME_MACHINE} in pc98) echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:[3456]*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; EM64T | authenticamd) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; arm*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; cris:Linux:*:*) echo cris-axis-linux-gnu exit ;; crisv32:Linux:*:*) echo crisv32-axis-linux-gnu exit ;; frv:Linux:*:*) echo frv-unknown-linux-gnu exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; mips:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef mips #undef mipsel #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=mipsel #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=mips #else CPU= #endif #endif EOF eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' /^CPU/{ s: ::g p }'`" test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef mips64 #undef mips64el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=mips64el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=mips64 #else CPU= #endif #endif EOF eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' /^CPU/{ s: ::g p }'`" test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; or32:Linux:*:*) echo or32-unknown-linux-gnu exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit ;; xtensa:Linux:*:*) echo xtensa-unknown-linux-gnu exit ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so # first see if it will tell us. cd to the root directory to prevent # problems with other programs or directories called `ld' in the path. # Set LC_ALL=C to ensure ld outputs messages in English. ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ | sed -ne '/supported targets:/!d s/[ ][ ]*/ /g s/.*supported targets: *// s/ .*// p'` case "$ld_supported_targets" in elf32-i386) TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" ;; a.out-i386-linux) echo "${UNAME_MACHINE}-pc-linux-gnuaout" exit ;; coff-i386) echo "${UNAME_MACHINE}-pc-linux-gnucoff" exit ;; "") # Either a pre-BFD a.out linker (linux-gnuoldld) or # one that does not give us useful --help. echo "${UNAME_MACHINE}-pc-linux-gnuoldld" exit ;; esac # Determine whether the default compiler is a.out or elf eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include #ifdef __ELF__ # ifdef __GLIBC__ # if __GLIBC__ >= 2 LIBC=gnu # else LIBC=gnulibc1 # endif # else LIBC=gnulibc1 # endif #else #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) LIBC=gnu #else LIBC=gnuaout #endif #endif #ifdef __dietlibc__ LIBC=dietlibc #endif EOF eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' /^LIBC/{ s: ::g p }'`" test x"${LIBC}" != x && { echo "${UNAME_MACHINE}-pc-linux-${LIBC}" exit } test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i386. echo i386-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NSE-?:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: DarkRadiant-2.5.0/config.rpath000077500000000000000000000440121321750546400162220ustar00rootroot00000000000000#! /bin/sh # Output a system dependent set of variables, describing how to set the # run time search path of shared libraries in an executable. # # Copyright 1996-2010 Free Software Foundation, Inc. # Taken from GNU libtool, 2001 # Originally by Gordon Matzigkeit , 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # The first argument passed to this file is the canonical host specification, # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld # should be set by the caller. # # The set of defined variables is at the end of this script. # Known limitations: # - On IRIX 6.5 with CC="cc", the run time search patch must not be longer # than 256 bytes, otherwise the compiler driver will dump core. The only # known workaround is to choose shorter directory names for the build # directory and/or the installation directory. # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a shrext=.so host="$1" host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` # Code taken from libtool.m4's _LT_CC_BASENAME. for cc_temp in $CC""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` # Code taken from libtool.m4's _LT_COMPILER_PIC. wl= if test "$GCC" = yes; then wl='-Wl,' else case "$host_os" in aix*) wl='-Wl,' ;; darwin*) case $cc_basename in xlc*) wl='-Wl,' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) ;; hpux9* | hpux10* | hpux11*) wl='-Wl,' ;; irix5* | irix6* | nonstopux*) wl='-Wl,' ;; newsos6) ;; linux* | k*bsd*-gnu) case $cc_basename in ecc*) wl='-Wl,' ;; icc* | ifort*) wl='-Wl,' ;; lf95*) wl='-Wl,' ;; pgcc | pgf77 | pgf90) wl='-Wl,' ;; ccc*) wl='-Wl,' ;; como) wl='-lopt=' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) wl='-Wl,' ;; esac ;; esac ;; osf3* | osf4* | osf5*) wl='-Wl,' ;; rdos*) ;; solaris*) wl='-Wl,' ;; sunos4*) wl='-Qoption ld ' ;; sysv4 | sysv4.2uw2* | sysv4.3*) wl='-Wl,' ;; sysv4*MP*) ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) wl='-Wl,' ;; unicos*) wl='-Wl,' ;; uts4*) ;; esac fi # Code taken from libtool.m4's _LT_LINKER_SHLIBS. hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_direct=no hardcode_minus_L=no case "$host_os" in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; esac ld_shlibs=yes if test "$with_gnu_ld" = yes; then # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. # Unlike libtool, we use -rpath here, not --rpath, since the documented # option of GNU ld is called -rpath, not --rpath. hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' case "$host_os" in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # Samuel A. Falvo II reports # that the semantics of dynamic libraries on AmigaOS, at least up # to version 4, is to share data among multiple programs linked # with the same dynamic library. Since this doesn't match the # behavior of shared libraries on other platforms, we cannot use # them. ld_shlibs=no ;; beos*) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then : else ld_shlibs=no fi ;; interix[3-9]*) hardcode_direct=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; gnu* | linux* | k*bsd*-gnu) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; netbsd*) ;; solaris*) if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then ld_shlibs=no elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' else ld_shlibs=no fi ;; esac ;; sunos4*) hardcode_direct=yes ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then hardcode_libdir_flag_spec= fi else case "$host_os" in aix3*) # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac fi hardcode_direct=yes hardcode_libdir_separator=':' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && \ strings "$collect2name" | grep resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac fi # Begin _LT_AC_SYS_LIBPATH_AIX. echo 'int main () { return 0; }' > conftest.c ${CC} ${LDFLAGS} conftest.c -o conftest aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` fi if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib" fi rm -f conftest.c conftest # End _LT_AC_SYS_LIBPATH_AIX. if test "$aix_use_runtimelinking" = yes; then hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' else hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" fi fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # see comment about different semantics on the GNU ld section ld_shlibs=no ;; bsdi[45]*) ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec=' ' libext=lib ;; darwin* | rhapsody*) hardcode_direct=no if test "$GCC" = yes ; then : else case $cc_basename in xlc*) ;; *) ld_shlibs=no ;; esac fi ;; dgux*) hardcode_libdir_flag_spec='-L$libdir' ;; freebsd1*) ld_shlibs=no ;; freebsd2.2*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; freebsd2*) hardcode_direct=yes hardcode_minus_L=yes ;; freebsd* | dragonfly*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; hpux9*) hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; hpux10*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no ;; *) hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; netbsd*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; newsos6) hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then hardcode_libdir_flag_spec='${wl}-rpath,$libdir' else case "$host_os" in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) hardcode_libdir_flag_spec='-R$libdir' ;; *) hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; osf3*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) if test "$GCC" = yes; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else # Both cc and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi hardcode_libdir_separator=: ;; solaris*) hardcode_libdir_flag_spec='-R$libdir' ;; sunos4*) hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes ;; sysv4) case $host_vendor in sni) hardcode_direct=yes # is this really true??? ;; siemens) hardcode_direct=no ;; motorola) hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac ;; sysv4.3*) ;; sysv4*MP*) if test -d /usr/nec; then ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) ;; sysv5* | sco3.2v5* | sco5v6*) hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' hardcode_libdir_separator=':' ;; uts4*) hardcode_libdir_flag_spec='-L$libdir' ;; *) ld_shlibs=no ;; esac fi # Check dynamic linker characteristics # Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER. # Unlike libtool.m4, here we don't care about _all_ names of the library, but # only about the one the linker finds when passed -lNAME. This is the last # element of library_names_spec in libtool.m4, or possibly two of them if the # linker has special search rules. library_names_spec= # the last element of library_names_spec in libtool.m4 libname_spec='lib$name' case "$host_os" in aix3*) library_names_spec='$libname.a' ;; aix[4-9]*) library_names_spec='$libname$shrext' ;; amigaos*) library_names_spec='$libname.a' ;; beos*) library_names_spec='$libname$shrext' ;; bsdi[45]*) library_names_spec='$libname$shrext' ;; cygwin* | mingw* | pw32* | cegcc*) shrext=.dll library_names_spec='$libname.dll.a $libname.lib' ;; darwin* | rhapsody*) shrext=.dylib library_names_spec='$libname$shrext' ;; dgux*) library_names_spec='$libname$shrext' ;; freebsd1*) ;; freebsd* | dragonfly*) case "$host_os" in freebsd[123]*) library_names_spec='$libname$shrext$versuffix' ;; *) library_names_spec='$libname$shrext' ;; esac ;; gnu*) library_names_spec='$libname$shrext' ;; hpux9* | hpux10* | hpux11*) case $host_cpu in ia64*) shrext=.so ;; hppa*64*) shrext=.sl ;; *) shrext=.sl ;; esac library_names_spec='$libname$shrext' ;; interix[3-9]*) library_names_spec='$libname$shrext' ;; irix5* | irix6* | nonstopux*) library_names_spec='$libname$shrext' case "$host_os" in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; *) libsuff= shlibsuff= ;; esac ;; esac ;; linux*oldld* | linux*aout* | linux*coff*) ;; linux* | k*bsd*-gnu) library_names_spec='$libname$shrext' ;; knetbsd*-gnu) library_names_spec='$libname$shrext' ;; netbsd*) library_names_spec='$libname$shrext' ;; newsos6) library_names_spec='$libname$shrext' ;; nto-qnx*) library_names_spec='$libname$shrext' ;; openbsd*) library_names_spec='$libname$shrext$versuffix' ;; os2*) libname_spec='$name' shrext=.dll library_names_spec='$libname.a' ;; osf3* | osf4* | osf5*) library_names_spec='$libname$shrext' ;; rdos*) ;; solaris*) library_names_spec='$libname$shrext' ;; sunos4*) library_names_spec='$libname$shrext$versuffix' ;; sysv4 | sysv4.3*) library_names_spec='$libname$shrext' ;; sysv4*MP*) library_names_spec='$libname$shrext' ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) library_names_spec='$libname$shrext' ;; uts4*) library_names_spec='$libname$shrext' ;; esac sed_quote_subst='s/\(["`$\\]\)/\\\1/g' escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` shlibext=`echo "$shrext" | sed -e 's,^\.,,'` escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <. Submit a context # diff and a properly formatted ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray) os= basic_machine=$1 ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ | bfin \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | mcore | mep \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64vr | mips64vrel \ | mips64orion | mips64orionel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | mt \ | msp430 \ | nios | nios2 \ | ns16k | ns32k \ | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ | score \ | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu | strongarm \ | tahoe | thumb | tic4x | tic80 | tron \ | v850 | v850e \ | we32k \ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ | z8k) basic_machine=$basic_machine-unknown ;; m6811 | m68hc11 | m6812 | m68hc12) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64vr-* | mips64vrel-* \ | mips64orion-* | mips64orionel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nios-* | nios2-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ | romp-* | rs6000-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ | tahoe-* | thumb-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tron-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ | xstormy16-* | xtensa-* \ | ymp-* \ | z8k-*) ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; c90) basic_machine=c90-cray os=-unicos ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; # I'm not sure what "Sysv32" means. Should this be sysv3.2? i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; mingw32) basic_machine=i386-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; mvs) basic_machine=i370-ibm os=-mvs ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc) basic_machine=powerpc-unknown ;; ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tic54x | c54x*) basic_machine=tic54x-unknown os=-coff ;; tic55x | c55x*) basic_machine=tic55x-unknown os=-coff ;; tic6x | c6x*) basic_machine=tic6x-unknown os=-coff ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* \ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -kaos*) os=-kaos ;; -zvmoe) os=-zvmoe ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 # This also exists in the configure program, but was not the # default. # os=-sunos4 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: DarkRadiant-2.5.0/configure.ac000066400000000000000000000317611321750546400162070ustar00rootroot00000000000000AC_INIT([darkradiant], [2.5.0]) AM_INIT_AUTOMAKE([subdir-objects]) AM_SILENT_RULES([yes]) AC_CONFIG_MACRO_DIR([m4]) # Get info about the OS we're on AC_CANONICAL_HOST build_linux=no build_osx=no case $host_os in linux*) build_linux=yes ;; darwin* ) build_osx=yes ;; esac # Initialise Gettext AM_GNU_GETTEXT_VERSION(0.18.1) AM_GNU_GETTEXT([external]) # greebo: Require that the gettext binaries are available AC_CHECK_PROG(HAS_MSGFMT, msgfmt, [yes], [no]) AC_CHECK_PROG(HAS_MSGMERGE, msgmerge, [yes], [no]) AC_CHECK_PROG(HAS_XGETTEXT, xgettext, [yes], [no]) if test x"$HAS_MSGFMT" == x"no" || test x"$HAS_MSGMERGE" == x"no" || test x"$HAS_XGETTEXT" == x"no" then AC_MSG_ERROR([Please make sure msgfmt, msgmerge and xgettext are available (probably through the gettext package).]) fi # Save user flags USER_CFLAGS=$CFLAGS USER_CXXFLAGS=$CXXFLAGS USER_CPPFLAGS=$CPPFLAGS USER_LDFLAGS=$LDFLAGS # Setup and defaults AC_LANG(C++) AC_PROG_CXX AC_DISABLE_STATIC AC_PROG_LIBTOOL # We require a C++11-compliant compiler, without nonstandard extensions AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory]) # We require the cstdint header to be present AC_CHECK_HEADER([cstdint], [], [AC_MSG_ERROR([Could not find the header.])]) # Optional features AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable debug build])], [debug_build=$enableval], [debug_build='no']) AC_ARG_ENABLE([darkmod-plugins], [AS_HELP_STRING([--enable-darkmod-plugins], [build Dark Mod plugins such as S/R editor])], [darkmod_plugins=$enableval], [darkmod_plugins='no']) AC_ARG_ENABLE([python], [AS_HELP_STRING([--disable-python], [disable Python scripting functionality])], [python_scripting=$enableval], [python_scripting='yes']) AC_ARG_WITH(pybind11, [--with-pybind11=/path/to/pybind11/include pybind11 include path to use (optional)], pybind11_include_path="$withval") if [[ "$darkmod_plugins" != 'no' ]] then # Enable conditional directories (used by plugins/Makefile.am) sr_plugin="dm.stimresponse" obj_plugin="dm.objectives" difficulty_plugin="dm.difficulty" conv_plugin="dm.conversation" gui_plugin="dm.gui" editing_plugin="dm.editing" fi AC_SUBST([sr_plugin]) AC_SUBST([obj_plugin]) AC_SUBST([difficulty_plugin]) AC_SUBST([conv_plugin]) AC_SUBST([gui_plugin]) AC_SUBST([editing_plugin]) # Check for required libraries # Zlib AC_CHECK_HEADER([zlib.h], [], [AC_MSG_ERROR([ZLib not found])]) AC_CHECK_LIB([z], [inflateEnd], [Z_LIBS='-lz'], [AC_MSG_ERROR([ZLib not found])]) AC_SUBST([Z_LIBS]) # JPEG AC_CHECK_HEADER([jpeglib.h], [], [AC_MSG_ERROR([libjpeg header not found])]) AC_CHECK_LIB([jpeg], [jpeg_start_compress], [JPEG_LIBS='-ljpeg'], [AC_MSG_ERROR([libjpeg library not found])]) AC_SUBST([JPEG_LIBS]) # wxWidgets WX_CONFIG_OPTIONS WX_CONFIG_CHECK([3.0.0], [wxWin=1], [wxWin=0], [std,gl,stc]) if test "$wxWin" != 1; then AC_MSG_ERROR([ wxWidgets must be installed on your system but wx-config script could not be found. Please check that wx-config is in path, the directory where wxWidgets libraries are installed (returned by 'wx-config --libs' command) is in LD_LIBRARY_PATH or equivalent variable and wxWidgets version is 2.3.4 or above. ]) fi # Support libraries PKG_CHECK_MODULES([XML], [libxml-2.0]) PKG_CHECK_MODULES([LIBSIGC], [sigc++-2.0]) PKG_CHECK_MODULES([PNG], [libpng]) PKG_CHECK_MODULES([FTGL], [ftgl]) # GLEW AC_CHECK_HEADER([GL/glew.h], [], [AC_MSG_ERROR([GLEW not found])]) AC_CHECK_LIB([GLEW], [main], [GLEW_LIBS='-lGLEW'], [AC_MSG_ERROR([GLEW not found])]) AC_SUBST([GLEW_LIBS]) # In OSX, OpenGL and GLU are handled through the -framework OpenGL switch # In Linux we need to check for the libs if test "$build_osx" != "no"; then [GL_LIBS='-framework OpenGL'] # We need the libintl library in OSX AC_CHECK_LIB([intl], [main], [INTL_LIBS='-lintl'], [AC_MSG_ERROR([intl library not found])]) AC_SUBST([INTL_LIBS]) fi if test "$build_linux" != "no"; then AC_CHECK_LIB([GL], [main], [GL_LIBS='-lGL'], [AC_MSG_ERROR([GL library not found.])]) AC_CHECK_LIB([GLU], [gluBuild2DMipmaps], [GLU_LIBS="-lGLU"], [AC_MSG_ERROR([GLU library not found.])]) AC_SUBST([GL_LIBS]) AC_SUBST([GLU_LIBS]) fi # By default, we rely on boost.filesystem, unless we have the C++17 libs use_boost_filesystem=yes # Check for the regular C++17 header AC_CHECK_HEADER([filesystem], [found_std_filesystem=yes], [found_std_filesystem=no]) # if no is present, check for the pre-C++17 header if test "$found_std_filesystem" = "yes" then AC_DEFINE([HAVE_STD_FILESYSTEM], [1], [Define this to indicate that the header is available for inclusion.]) use_boost_filesystem=no else AC_CHECK_HEADER([experimental/filesystem], [found_experimental_filesystem=yes], [found_experimental_filesystem=no]) if test "$found_experimental_filesystem" = "yes" then AC_DEFINE([HAVE_EXPERIMENTAL_FILESYSTEM], [1], [Define this to indicate that the header is available for inclusion.]) use_boost_filesystem=no fi fi FILESYSTEM_LIBS='' # Require Boost Filesystem if the C++17-style header is not present if test "$use_boost_filesystem" = "yes" then AC_MSG_NOTICE([Will check for boost libraries since std::filesystem is not available]) # Boost BOOST_REQUIRE([1.46.1]) BOOST_FILESYSTEM BOOST_SYSTEM FILESYSTEM_LIBS="$(BOOST_FILESYSTEM_LIBS) $(BOOST_SYSTEM_LIBS)" else AC_MSG_NOTICE([Will use std::filesystem instead of boost.filesystem]) FILESYSTEM_LIBS="-lstdc++fs" fi AC_SUBST([FILESYSTEM_LIBS]) # PyBind11 if required if test "$python_scripting" = 'yes' then AM_CONDITIONAL([SYSTEM_HAS_PYBIND11], [false]) # default to "not found" AC_CHECK_PROGS([PYTHON_CONFIG], [python-config], []) if test ! -z "$PYTHON_CONFIG" then PYTHON_LIBS=`$PYTHON_CONFIG --libs` PYTHON_CPPFLAGS=`$PYTHON_CONFIG --includes` PYBIND11_CPPFLAGS=`` # Add the path to the pybind11 library if we got one from the arguments if test "x$pybind11_include_path" != x ; then PYBIND11_CPPFLAGS="-I$pybind11_include_path" fi # pybind11.h is including Python.h, need to help it find the Python.h header CPPFLAGS="$USER_CPPFLAGS $PYTHON_CPPFLAGS $PYBIND11_CPPFLAGS" AC_MSG_NOTICE([Checking for pybind11 headers...]) found_local_pybind11=no # Check for a system-provided pybind11, otherwise fall back on # the pybind11 headers we ship in the libs/pybind folder AC_CHECK_HEADER([pybind11/pybind11.h], [found_local_pybind11=yes], [found_local_pybind11=no]) if test "$found_local_pybind11" = 'yes' then AC_MSG_NOTICE([Using the system-provided pybind11 headers]) else AC_MSG_NOTICE([Using the pybind11 headers shipped with the sources]) fi AM_CONDITIONAL([SYSTEM_HAS_PYBIND11], [test "$found_local_pybind11" = 'yes']) script_module='script' AC_SUBST([script_module]) AC_SUBST([PYTHON_CPPFLAGS]) AC_SUBST([PYTHON_LIBS]) AC_SUBST([PYBIND11_CPPFLAGS]) AC_SUBST([SYSTEM_HAS_PYBIND11]) # Used by Makefile.am in plugins/script/ fi else AM_CONDITIONAL([SYSTEM_HAS_PYBIND11], [false]) fi # greebo: Don't check for the system-provided fmtlib, since linking # against the libfmt.a produces a relocation linker error, complaining about # a missing -fPIC switch. I won't bother with that right now, let's use the # headers shipped with the repository. #AC_MSG_NOTICE([Checking for fmtlib headers...]) found_local_fmtlib=no # Check for a system-provided fmtlib, otherwise fall back on # the headers we ship in the libs/libfmt folder #AC_CHECK_HEADER([fmt/format.h], [found_local_fmtlib=yes], [found_local_fmtlib=no]) if test "$found_local_fmtlib" = 'yes' then FMTLIB_CPPFLAGS="" # system got one FMTLIB_LDFLAGS="-lfmt" AC_MSG_NOTICE([Using the system-provided fmtlib headers]) else # Switch to header-only mode when using our own shipped sources FMTLIB_CPPFLAGS="-I`pwd`/libs/libfmt -DFMT_HEADER_ONLY" FMTLIB_LDFLAGS="" AC_MSG_NOTICE([Using the fmtlib headers shipped with the sources (header-only mode)]) fi # dynamic link library AC_CHECK_LIB([dl], [main], [DL_LIBS='-ldl'], [AC_MSG_ERROR([DL library not found])]) AC_SUBST([DL_LIBS]) # OpenAL and vorbis AC_CHECK_HEADER([AL/alut.h], [sound_module='sound'], [sound_module='']) AC_CHECK_LIB([vorbisfile], [ov_clear], [VORBIS_LIBS='-lvorbisfile'], [sound_module='']) # Use the OpenAL framework in OSX if test "$build_osx" != "no"; then [AL_LIBS='-framework OpenAL'] else AC_CHECK_LIB([openal], [alGetError], [AL_LIBS='-lopenal'], [sound_module='']) fi AC_CHECK_LIB([alut], [main], [ALUT_LIBS='-lalut'], [sound_module=''], [$AL_LIBS]) AC_SUBST([sound_module]) AC_SUBST([ALUT_LIBS]) AC_SUBST([VORBIS_LIBS]) AC_SUBST([AL_LIBS]) # Configure global flags, cancelling any modifications we may have made during # configuration WARNING_FLAGS="-Wall -Wno-unused-variable -Werror=return-type" if test "$build_osx" != "no"; then WARNING_FLAGS="$WARNING_FLAGS -Wno-inconsistent-missing-override -Wno-potentially-evaluated-expression" fi CFLAGS="$USER_CFLAGS $WARNING_FLAGS $WX_CFLAGS" CXXFLAGS="$USER_CXXFLAGS $WARNING_FLAGS $WX_CXXFLAGS_ONLY" CPPFLAGS="$USER_CPPFLAGS -DPOSIX -fPIC $WX_CPPFLAGS $LIBSIGC_CFLAGS -DWXINTL_NO_GETTEXT_MACRO $FMTLIB_CPPFLAGS" LDFLAGS="$USER_LDFLAGS $LIBSIGC_LIBS $FMTLIB_LDFLAGS" # Debug/optimisation if test "$debug_build" != 'no' then CPPFLAGS="-D_DEBUG $CPPFLAGS" CFLAGS="-g -O0 $CFLAGS" CXXFLAGS="-g -O0 $CXXFLAGS" else CFLAGS="-g -O2 -DNDEBUG $CFLAGS" CXXFLAGS="-g -O2 -DNDEBUG $CXXFLAGS" fi AC_SUBST([CPPFLAGS]) AC_SUBST([CFLAGS]) AC_SUBST([CXXFLAGS]) AC_SUBST([LDFLAGS]) # Generate output files AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([install/darkradiant.desktop install/i18n//Makefile.in Makefile radiant/Makefile install/i18n/Makefile.in libs/Makefile libs/ddslib/Makefile libs/wxutil/Makefile libs/math/Makefile libs/picomodel/Makefile libs/scene/Makefile libs/xmlutil/Makefile plugins/Makefile plugins/archivezip/Makefile plugins/commandsystem/Makefile plugins/eclassmgr/Makefile plugins/eclasstree/Makefile plugins/entity/Makefile plugins/entitylist/Makefile plugins/eventmanager/Makefile plugins/filetypes/Makefile plugins/filters/Makefile plugins/fonts/Makefile plugins/image/Makefile plugins/mapdoom3/Makefile plugins/md5model/Makefile plugins/model/Makefile plugins/particles/Makefile plugins/scenegraph/Makefile plugins/script/Makefile plugins/shaders/Makefile plugins/skins/Makefile plugins/sound/Makefile plugins/uimanager/Makefile plugins/undo/Makefile plugins/vfspk3/Makefile plugins/xmlregistry/Makefile plugins/dm.stimresponse/Makefile plugins/dm.objectives/Makefile plugins/dm.difficulty/Makefile plugins/dm.gui/Makefile plugins/dm.editing/Makefile plugins/dm.conversation/Makefile]) AC_OUTPUT # Display configured options echo " Configuration options:" echo " Installation prefix: $prefix" if test -n "$sound_module" then echo " Sound: yes" else echo " Sound: no" fi if test "$darkmod_plugins" != 'no' then echo " Darkmod plugins: yes" else echo " Darkmod plugins: no" fi if test -z "$script_module" then echo " Python scripting: no" else echo " Python scripting: yes" if test "$found_local_pybind11" = "yes" then echo " Use system pybind11: yes" else echo " Use system pybind11: no" fi fi #if test "$found_local_fmtlib" = "yes" #then # echo " Use system fmtlib: yes" #else # echo " Use system fmtlib: no" #fi echo " Use boost.filesystem: $use_boost_filesystem" DarkRadiant-2.5.0/darkradiant.spec000066400000000000000000000031151321750546400170510ustar00rootroot00000000000000Name: darkradiant Version: 0.9.12 Release: 2%{?dist} Summary: Level editor for Doom 3 and The Dark Mod Group: Applications/Editors License: GPLv2 and LGPLv2 and BSD URL: http://darkradiant.sourceforge.net/ Source0: %{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: automake, autoconf, libtool, desktop-file-utils %description DarkRadiant is a 3D level editor for Doom 3 and The Dark Mod, based on the GPL release of GtkRadiant. %prep %setup -q %build %configure --enable-darkmod-plugins --enable-debug --prefix=/usr make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT desktop-file-install \ --dir=${RPM_BUILD_ROOT}%{_datadir}/applications \ ${RPM_BUILD_ROOT}%{_datadir}/applications/darkradiant.desktop %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %doc README %{_bindir}/* %{_libdir}/darkradiant/lib* %{_libdir}/darkradiant/modules %{_libdir}/darkradiant/scripts %{_libdir}/darkradiant/plugins/eclasstree* %{_datadir}/* %package plugins-darkmod Summary: DarkMod-specific plugins for DarkRadiant Group: Applications/Editors Requires: darkradiant %description plugins-darkmod These plugins are used for editing Dark Mod missions. %files plugins-darkmod %defattr(-,root,root,-) %doc README.linux %{_libdir}/darkradiant/plugins/dm_* %changelog * Tue Mar 26 2009 ibix - 0.9.12-2 - patches upstream. Removed here. * Tue Mar 24 2009 ibix - 0.9.12-1 - spec file. - patch for sound detection on fedora. - patch for valid desktop entry. DarkRadiant-2.5.0/debian/000077500000000000000000000000001321750546400151335ustar00rootroot00000000000000DarkRadiant-2.5.0/debian/README000066400000000000000000000002441321750546400160130ustar00rootroot00000000000000The Debian Package darkradiant ---------------------------- Comments regarding the Package -- orbweaver Sat, 21 Jun 2008 15:25:20 +0100 DarkRadiant-2.5.0/debian/changelog000066400000000000000000000065551321750546400170200ustar00rootroot00000000000000darkradiant (2.0.2-2~vivid1) vivid; urgency=medium * Entity list now shows entity names, and no longer crashes when an entity is selected. -- Matthew Mott Thu, 24 Sep 2015 22:58:44 +0100 darkradiant (2.0.2-1~vivid1) vivid; urgency=medium * 2.0.2 release for Vivid. -- Matthew Mott Fri, 28 Aug 2015 22:43:48 +0100 darkradiant (2.0.2) trusty; urgency=medium * New upstream release. -- Matthew Mott Mon, 12 Jan 2015 18:48:28 +0000 darkradiant (2.0.1-1~trusty1) trusty; urgency=medium * Fix for crash/assertion when trying to show Add Property dialog. * Added .game file for The Dark Mod standalone that does not include a base/ subdirectory. * Various other bugfixes. -- Matthew Mott Wed, 24 Dec 2014 14:41:12 +0000 darkradiant (2.0.1~trusty2) trusty; urgency=low * 2.0 series release for Linux, including the switch from GTK to wxWidgets. * 2.0.1 contains numerous bug-fixes over the original 2.0 version. -- Matthew Mott Mon, 17 Nov 2014 19:55:17 +0000 darkradiant (1.8.0~trusty1) trusty; urgency=low * This release contains numerous bug and stability fixes as well as one new feature for flooring objects. Some improvements have also been made to better support different game types. * Fix for patch mesh rendering on nVidia hardware with recent drivers. -- Matthew Mott Sat, 28 Jun 2014 10:02:35 +0100 darkradiant (1.7.3~raring1) raring; urgency=low * Fixed progressive corruption of non-worldspawn brush geometry over multiple save operations (#3272) -- Matthew Mott Sat, 02 Feb 2013 09:15:03 +0000 darkradiant (1.7.2~raring1) raring; urgency=low * Fixed incorrect renaming of entities when importing a map (#2733). -- Matthew Mott Mon, 22 Oct 2012 18:35:36 +0100 darkradiant (1.7.1~raring2) raring; urgency=low * Fixed black render artifacts in model preview window (#2939). * Fixed lighting view turning black when certain models are in view (#2969). * Fixed crash on exit after changing view layout (#2954). * Fixed sky box textures failing to render. * Fixed inability to move vertices with arrow keys in camera view (#2938). * Fixed crash after Save As in Particle Editor (now renamed to Copy to better reflect its behaviour). * Fixed crash in Readables editor if certain font resources were missing from the mod assets tree. * Re-designed model chooser allows control over visibility of model's component materials. * Improved performance of map loading and population of certain asset tree views (#2987). * Splitter position in certain dialogs is persistent. * Linux build does not fail if GtkSourceView is not available, this is now an optional dependency. * Updated GTK and related library versions on Windows (#2945) -- Matthew Mott Sun, 13 May 2012 11:25:18 +0100 darkradiant (1.7.0.1~raring1) raring; urgency=low * Fixed possible crash in displaying particle editor on Linux. * Fixed failure to find user-local mod assets in (e.g.) ~/.doom3/darkmod -- Matthew Mott Tue, 29 Nov 2011 19:51:12 +0000 darkradiant (1.7.0~raring1) raring; urgency=low * 1.7.0 release. -- Matthew Mott Mon, 14 Nov 2011 11:13:41 +0000 DarkRadiant-2.5.0/debian/compat000066400000000000000000000000021321750546400163310ustar00rootroot000000000000005 DarkRadiant-2.5.0/debian/control000066400000000000000000000023421321750546400165370ustar00rootroot00000000000000Source: darkradiant Section: editors Priority: extra Maintainer: Matthew Mott Build-Depends: debhelper (>= 5), autotools-dev, libgtkmm-2.4-dev, libgtkglextmm-x11-1.2-dev, libxml2-dev, libglew-dev, libboost-regex-dev, libboost-filesystem-dev, libboost-serialization-dev, libboost-test-dev, libboost-python-dev, python-dev, libvorbis-dev, libopenal-dev, libalut-dev, libjpeg-dev, ftgl-dev, libwxbase3.0-dev, libwxgtk3.0-dev Standards-Version: 3.9.1 Package: darkradiant Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Level editor for Doom 3 and The Dark Mod DarkRadiant is a 3D level editor for Doom 3 and The Dark Mod, based on the GPL release of GtkRadiant. Package: darkradiant-plugins-darkmod Architecture: any Depends: darkradiant, ${shlibs:Depends}, ${misc:Depends} Description: DarkMod-specific plugins for DarkRadiant These plugins are used for editing Dark Mod missions. Package: darkradiant-i18n Architecture: any Depends: darkradiant, ${shlibs:Depends}, ${misc:Depends} Description: Internationalisation files for DarkRadiant. This package includes the translated strings for localising DarkRadiant into different languages. The currently-available languages are: . de (German) DarkRadiant-2.5.0/debian/copyright000066400000000000000000000036551321750546400170770ustar00rootroot00000000000000Copyright Notice: © 2006 - 2010 OrbWeaver , greebo & other DarkRadiant authors listed below. © 2006 GtkRadiant authors listed below. Authors: DarkRadiant portions: OrbWeaver greebo STiFU mohij angua Crispy Gildoran Jesps (Bulge patch algorithm) GtkRadiant portions: Leonardo Zide leo@lokigames.com Mike Phillips (Loki QA) Bernd Kreimeier (overall coordination) TTimo timo@idsoftware.com ^Fishman (Pablo Zurita) fish@gamedesign.net RR2DO2 rr2do2@q3f.com SmallPileofGibs spog@planetquake.com Mike "mickey" Jackman Tim "Maj" Rennie Geoffrey DeWan David Hyde seaw0lf with assist by ydnar Randy 'ydnar' Reddig Jean-Francois "Eutectic" Groleau Jan Paul "MrElusive" van Waveren Robert Duffy Forest "LordHavoc" Wroncy-Hale Nurail AcidDeath Chronos Michael Schlueter Jamie Wilkinson Robert "Tr3B" Beckebans Licence: This package 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 2 of the License, or (at your option) any later version. This package 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. DarkRadiant-2.5.0/debian/darkradiant-i18n.install000066400000000000000000000000211321750546400215550ustar00rootroot00000000000000usr/share/locale DarkRadiant-2.5.0/debian/darkradiant-plugins-darkmod.install000066400000000000000000000000411321750546400241000ustar00rootroot00000000000000usr/lib/darkradiant/plugins/dm_* DarkRadiant-2.5.0/debian/darkradiant.install000066400000000000000000000002561321750546400210120ustar00rootroot00000000000000usr/bin usr/lib/darkradiant/lib* usr/lib/darkradiant/modules usr/lib/darkradiant/scripts usr/lib/darkradiant/plugins/eclasstree* usr/share/darkradiant usr/share/applications DarkRadiant-2.5.0/debian/darkradiant.manpages000066400000000000000000000000221321750546400211260ustar00rootroot00000000000000man/darkradiant.1 DarkRadiant-2.5.0/debian/dirs000066400000000000000000000000321321750546400160120ustar00rootroot00000000000000usr/bin usr/lib usr/share DarkRadiant-2.5.0/debian/docs000066400000000000000000000000071321750546400160030ustar00rootroot00000000000000README DarkRadiant-2.5.0/debian/rules000077500000000000000000000033041321750546400162130ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # Handle some DEB_BUILD_OPTIONS standards ifneq (,$(filter noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 endif INSTALL_TARGET=install-strip ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) INSTALL_TARGET=install endif config.status: configure dh_testdir ./configure --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info --enable-darkmod-plugins build: build-stamp build-stamp: config.status dh_testdir # Add here commands to compile the package. $(MAKE) touch $@ clean: dh_testdir dh_testroot rm -f build-stamp # Add here commands to clean up after the build process. [ ! -f Makefile ] || $(MAKE) distclean dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs $(MAKE) DESTDIR=$(CURDIR)/debian/tmp $(INSTALL_TARGET) # Build architecture-independent files here. binary-indep: build install # Nothing here # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installchangelogs dh_installdocs dh_installman dh_install --sourcedir=debian/tmp dh_strip dh_compress dh_fixperms dh_makeshlibs --noscripts dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install DarkRadiant-2.5.0/debian/source/000077500000000000000000000000001321750546400164335ustar00rootroot00000000000000DarkRadiant-2.5.0/debian/source/format000066400000000000000000000000151321750546400176420ustar00rootroot000000000000003.0 (native) DarkRadiant-2.5.0/depcomp000077500000000000000000000560161321750546400152760ustar00rootroot00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2013-05-30.07; # UTC # Copyright (C) 1999-2014 Free Software Foundation, Inc. # 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 2, 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 . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: DarkRadiant-2.5.0/include/000077500000000000000000000000001321750546400153345ustar00rootroot00000000000000DarkRadiant-2.5.0/include/Bounded.h000066400000000000000000000005731321750546400170720ustar00rootroot00000000000000#ifndef BOUNDED_H_ #define BOUNDED_H_ #include /* FOWARD DECLS */ class AABB; /** * Interface for bounded objects, which maintain a local AABB. */ class Bounded { public: virtual ~Bounded() {} /** * Return the local AABB for this object. */ virtual const AABB& localAABB() const = 0; }; typedef std::shared_ptr BoundedPtr; #endif /*BOUNDED_H_*/ DarkRadiant-2.5.0/include/GLProgramAttributes.h000066400000000000000000000004711321750546400214100ustar00rootroot00000000000000#ifndef GLPROGRAMATTRIBUTES_H_ #define GLPROGRAMATTRIBUTES_H_ /** * Enumeration for vertex attributes to be bound to a GLProgram, to avoid using * magic numbers. */ enum GLProgramAttributes { ATTR_TEXCOORD = 8, ATTR_TANGENT = 9, ATTR_BITANGENT = 10, ATTR_NORMAL = 11 }; #endif /*GLPROGRAMATTRIBUTES_H_*/ DarkRadiant-2.5.0/include/ModResource.h000066400000000000000000000013001321750546400177260ustar00rootroot00000000000000#ifndef MODRESOURCE_H_ #define MODRESOURCE_H_ #include /** * Interface for objects representing resources which were defined within * either a directory or a PK4 archive belonging to a particular mod. Provides * a single method to retrieve the name of the mod that contains this particular * resource. */ class ModResource { public: /** * Destructor */ virtual ~ModResource() {} /** * Return the name of the mod which owns this resource object. Objects that * are not part of a separate mod are found in the base/ directory, and will * return "base" from this function. */ virtual std::string getModName() const = 0; }; #endif /*MODRESOURCE_H_*/ DarkRadiant-2.5.0/include/Rotatable.h000066400000000000000000000004041321750546400174200ustar00rootroot00000000000000#pragma once #include "math/Quaternion.h" /// Interface for an object which can be rotated class Rotatable { public: virtual ~Rotatable() {} /// Rotate the object by the given Quaternion virtual void rotate(const Quaternion& rotation) = 0; }; DarkRadiant-2.5.0/include/Scalable.h000066400000000000000000000003641321750546400172160ustar00rootroot00000000000000#pragma once #include "math/Vector3.h" /// Interface for an object which can be scaled class Scalable { public: virtual ~Scalable() {} /// Scale the object by the given vector virtual void scale(const Vector3& scaling) = 0; }; DarkRadiant-2.5.0/include/ShaderLayer.h000066400000000000000000000143361321750546400177170ustar00rootroot00000000000000#pragma once #include "Texture.h" #include #include #include "math/Vector2.h" #include "math/Vector4.h" #include "render/Colour4.h" class IRenderEntity; // Texture repeat types enum ClampType { CLAMP_REPEAT = 1 << 0, // default = no clamping CLAMP_NOREPEAT = 1 << 1, // "clamp" CLAMP_ZEROCLAMP = 1 << 2, // "zeroclamp" CLAMP_ALPHAZEROCLAMP = 1 << 3, // "alphazeroclamp" }; /** * \brief * Representation of a GL blend function. * * A GL blend function consists of two GLenums representing the operations that * should be performed on the source and destination pixel colours respectively, * before the two results are added together into a final pixel colour. */ class BlendFunc { public: // Source pixel function GLenum src; // Destination pixel function GLenum dest; // Constructor BlendFunc(GLenum s, GLenum d) : src(s), dest(d) { } }; /** * \brief * A single layer of a material shader. * * Each shader layer contains an image texture, a blend mode (e.g. add, * modulate) and various other data. */ class ShaderLayer { public: /** * \brief * Enumeration of layer types. */ enum Type { DIFFUSE, BUMP, SPECULAR, BLEND }; // Stage flags enum Flags { FLAG_IGNORE_ALPHATEST = 1 << 0, FLAG_FILTER_NEAREST = 1 << 1, FLAG_FILTER_LINEAR = 1 << 2, FLAG_HIGHQUALITY = 1 << 3, // "uncompressed" FLAG_FORCE_HIGHQUALITY = 1 << 4, FLAG_NO_PICMIP = 1 << 5, FLAG_MASK_RED = 1 << 6, FLAG_MASK_GREEN = 1 << 7, FLAG_MASK_BLUE = 1 << 8, FLAG_MASK_ALPHA = 1 << 9, FLAG_MASK_DEPTH = 1 << 10, FLAG_CENTERSCALE = 1 << 11, // whether to translate -0.5, scale and translate +0.5 }; enum TexGenType { TEXGEN_NORMAL = 1 << 0, TEXGEN_REFLECT = 1 << 1, TEXGEN_SKYBOX = 1 << 2, TEXGEN_WOBBLESKY = 1 << 3, }; /** * \brief * Destructor */ virtual ~ShaderLayer() {} /** * \brief * Return the layer type. */ virtual Type getType() const = 0; /** * \brief * Return the Texture object corresponding to this layer (may be NULL). */ virtual TexturePtr getTexture() const = 0; /** * Evaluate all shader expressions used in this stage. Call this once (each frame) * before requesting things like getAlphaTest(), getColour() or isVisible() */ virtual void evaluateExpressions(std::size_t time) = 0; /** * Like evaluateExpressions(time), but with an additional renderentity as argument * to give this stage the ability to resolve parm0..parm11 values. */ virtual void evaluateExpressions(std::size_t time, const IRenderEntity& entity) = 0; /** * The flags set on this stage. */ virtual int getStageFlags() const = 0; /** * Each stage can have its own clamp type, overriding the per-material one. */ virtual ClampType getClampType() const = 0; /** * Returns the texgen type: normal, reflect, skybox, etc. * Use getTexGenParam(i) to retrieve the wobblesky parameters [0..2] */ virtual TexGenType getTexGenType() const = 0; /** * TexGen type wobblesky has 3 parameters, get them here, with index in [0..2] */ virtual float getTexGenParam(std::size_t index) const = 0; /** * \brief * Return the GL blend function for this layer. * * Only layers of type BLEND use a BlendFunc. Layers of type DIFFUSE, BUMP * and SPECULAR do not use blend functions. */ virtual BlendFunc getBlendFunc() const = 0; /** * \brief * Multiplicative layer colour (set with "red 0.6", "green 0.2" etc) */ virtual Colour4 getColour() const = 0; /** * \brief * Vertex colour blend mode enumeration. */ enum VertexColourMode { VERTEX_COLOUR_NONE, // no vertex colours VERTEX_COLOUR_MULTIPLY, // "vertexColor" VERTEX_COLOUR_INVERSE_MULTIPLY // "inverseVertexColor" }; /** * \brief * Get the vertex colour mode for this layer. */ virtual VertexColourMode getVertexColourMode() const = 0; /** * \brief * Enumeration of cube map modes for this layer. */ enum CubeMapMode { CUBE_MAP_NONE, CUBE_MAP_CAMERA, // cube map in camera space ("cameraCubeMap") CUBE_MAP_OBJECT // cube map in object space ("cubeMap") }; /** * \brief * Get the cube map mode for this layer. */ virtual CubeMapMode getCubeMapMode() const = 0; /** * Returns the value of the scale expressions of this stage. */ virtual Vector2 getScale() = 0; /** * Returns the value of the translate expressions of this stage. */ virtual Vector2 getTranslation() = 0; /** * Returns the value of the rotate expression of this stage. */ virtual float getRotation() = 0; /** * Returns the value of the 'shear' expressions of this stage. */ virtual Vector2 getShear() = 0; /** * \brief * Get the alpha test value for this layer. * * \return * The alpha test value, within (0..1] if it is set. If no alpha test * value is set, 0 will be returned. */ virtual float getAlphaTest() const = 0; /** * Whether this stage is active. Unconditional stages always return true, * conditional ones return the result of the most recent condition expression evaluation. */ virtual bool isVisible() const = 0; /** * Returns the name of this stage's fragment program. */ virtual const std::string& getVertexProgram() = 0; /** * Returns the name of this stage's fragment program. */ virtual const std::string& getFragmentProgram() = 0; /** * Returns the 4 parameter values for the vertexParm index . */ virtual Vector4 getVertexParm(int parm) = 0; /** * Returns the number of fragment maps in this stage. */ virtual std::size_t getNumFragmentMaps() = 0; /** * Returns the fragment map with the given index. */ virtual TexturePtr getFragmentMap(int index) = 0; /** * Stage-specific polygon offset, overriding the "global" one defined on the material. */ virtual float getPrivatePolygonOffset() = 0; }; /** * \brief * Shader pointer for ShaderLayer, */ typedef std::shared_ptr ShaderLayerPtr; /** * \brief * Vector of ShaderLayer pointers. */ typedef std::vector ShaderLayerVector; DarkRadiant-2.5.0/include/StringSerialisable.h000066400000000000000000000011141321750546400212700ustar00rootroot00000000000000#pragma once #include #include /** * \brief * Interface for an object which can serialise itself to/from a string. */ class StringSerialisable { public: /** * \brief * Destructor */ virtual ~StringSerialisable() {} /** * \brief * Export this object's state to a string. */ virtual std::string exportToString() const = 0; /** * \brief * Import this object's state from a given string. */ virtual void importFromString(const std::string& str) = 0; }; typedef std::shared_ptr StringSerialisablePtr; DarkRadiant-2.5.0/include/Texture.h000066400000000000000000000024121321750546400171440ustar00rootroot00000000000000#pragma once #include #include #include /** * \brief * Basic interface for all GL textures. * * This interface represents a texture which is bound to OpenGL, with an OpenGL * texture number (as returned from glGenTextures()). This may be a 2D, 3D, cube * map or any other kind of texture object supported by OpenGL. */ class Texture { public: /** * \brief * Destructor */ virtual ~Texture() {} /** * \brief * Constant indicating an invalid texture size. */ const static std::size_t INVALID_SIZE = 0; /** * \brief * Return the string name of this texture. */ virtual std::string getName() const = 0; /** * \brief * Return the GL texture identifier for this texture. */ virtual GLuint getGLTexNum() const = 0; /** * \brief * Return the width of this texture in pixels. May return INVALID_SIZE if * this texture does not have a valid size. */ virtual std::size_t getWidth() const = 0; /** * \brief * Return the height of this texture in pixels. May return INVALID_SIZE if * this texture does not have a valid size. */ virtual std::size_t getHeight() const = 0; }; typedef std::shared_ptr TexturePtr; DarkRadiant-2.5.0/include/Translatable.h000066400000000000000000000004151321750546400201210ustar00rootroot00000000000000#pragma once #include "math/Vector3.h" /// Interface for an object which can be translated class Translatable { public: virtual ~Translatable() {} /// Translate this object by the given vector virtual void translate(const Vector3& translation) = 0; }; DarkRadiant-2.5.0/include/VolumeIntersectionValue.h000066400000000000000000000003031321750546400223340ustar00rootroot00000000000000#pragma once /** * \brief * Enumeration of possible intersection results between two volumes. */ enum VolumeIntersectionValue { VOLUME_OUTSIDE, VOLUME_INSIDE, VOLUME_PARTIAL }; DarkRadiant-2.5.0/include/editable.h000066400000000000000000000017541321750546400172650ustar00rootroot00000000000000#pragma once class AABB; #include "inode.h" class Snappable { public: virtual ~Snappable() {} virtual void snapto(float snap) = 0; }; typedef std::shared_ptr SnappablePtr; inline SnappablePtr Node_getSnappable(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } class ComponentEditable { public: virtual ~ComponentEditable() {} virtual const AABB& getSelectedComponentsBounds() const = 0; }; typedef std::shared_ptr ComponentEditablePtr; inline ComponentEditablePtr Node_getComponentEditable(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } class ComponentSnappable { public: virtual ~ComponentSnappable() {} virtual void snapComponents(float snap) = 0; }; typedef std::shared_ptr ComponentSnappablePtr; inline ComponentSnappablePtr Node_getComponentSnappable(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } DarkRadiant-2.5.0/include/i18n.h000066400000000000000000000007321321750546400162660ustar00rootroot00000000000000#pragma once #define GETTEXT_PACKAGE "darkradiant" // Redefine the _() macro to return a std::string for convenience #ifndef WXINTL_NO_GETTEXT_MACRO #define WXINTL_NO_GETTEXT_MACRO #endif #include #include // Custom translation macros inline std::string _(const char* s) { return wxGetTranslation((s)).ToStdString(); } //#define _(s) (wxGetTranslation((s)).ToStdString()) #define N_(str) str #ifndef C_ #define C_(context,text) _(text) #endif DarkRadiant-2.5.0/include/iaasfile.h000066400000000000000000000120061321750546400172610ustar00rootroot00000000000000#pragma once #include #include "imodule.h" #include #include "math/Plane3.h" #include "math/Vector3.h" #include "math/AABB.h" namespace map { // An AAS type is defined by an entityDef block // Each AAS type has its own file extension struct AasType { std::string entityDefName; std::string fileExtension; }; typedef std::list AasTypeList; /** * Representation of a Area Awareness System file. * Provides read-only access to Area and Portal information. * Use the GlobalAasFileManager() to acquire an instance of * this class. */ class IAasFile { public: virtual std::size_t getNumPlanes() const = 0; virtual const Plane3& getPlane(std::size_t planeNum) const = 0; virtual std::size_t getNumVertices() const = 0; virtual const Vector3& getVertex(std::size_t vertexNum) const = 0; // An edge references two vertices by index struct Edge { int vertexNumber[2]; }; virtual std::size_t getNumEdges() const = 0; virtual const Edge& getEdge(std::size_t index) const = 0; virtual std::size_t getNumEdgeIndexes() const = 0; virtual int getEdgeByIndex(int edgeIdx) const = 0; struct Face { int planeNum; // number of the plane this face is on unsigned short flags; // face flags int numEdges; // number of edges in the boundary of the face int firstEdge; // first edge in the edge index short areas[2]; // area at the front and back of this face }; virtual std::size_t getNumFaces() const = 0; virtual const Face& getFace(int faceIndex) const = 0; virtual std::size_t getNumFaceIndexes() const = 0; virtual int getFaceByIndex(int faceIdx) const = 0; struct Area { int numFaces; // number of faces used for the boundary of the area int firstFace; // first face in the face index used for the boundary of the area AABB bounds; // bounds of the area Vector3 center; // center of the area an AI can move towards unsigned short flags; // several area flags unsigned short contents; // contents of the area short cluster; // cluster the area belongs to, if negative it's a portal short clusterAreaNum; // number of the area in the cluster int travelFlags; // travel flags for traveling through this area }; virtual std::size_t getNumAreas() const = 0; virtual const Area& getArea(int areaNum) const = 0; }; typedef std::shared_ptr IAasFilePtr; /** * A loader class capable of constructing an IAasFile instance from a token stream. */ class IAasFileLoader : public RegisterableModule { public: /** * Get the display name of this AAS file loader, e.g. "Doom 3", "Quake 4", etc. */ virtual const std::string& getAasFormatName() const = 0; /** * Each MapFormat can have a certain game type it is designed for, * a value which conincides with the type attribute in the game tag * found in the .game file, e.g. "doom3" or "quake4". */ virtual const std::string& getGameType() const = 0; /** * greebo: Returns true if this loader is able to parse * the contents of this file. Usually this includes a version * check of the file header. */ virtual bool canLoad(std::istream& stream) const = 0; /** * Load the AAS file contents from the given stream. */ virtual IAasFilePtr loadFromStream(std::istream& stream) = 0; }; typedef std::shared_ptr IAasFileLoaderPtr; // Info structure representing a single AAS file on disk struct AasFileInfo { std::string absolutePath; AasType type; }; class IAasFileManager : public RegisterableModule { public: virtual ~IAasFileManager() {} // Register a loader which is considered by all future AAS file load attempts virtual void registerLoader(const IAasFileLoaderPtr& loader) = 0; // Unregister a previously registered loader instance virtual void unregisterLoader(const IAasFileLoaderPtr& loader) = 0; // Get a loader capable of loading the given stream virtual IAasFileLoaderPtr getLoaderForStream(std::istream& stream) = 0; // Get the list of valid AAS types virtual AasTypeList getAasTypes() = 0; // Returns a specific AAS type. Will throw a std::runtime_error if the // type is not valid. virtual AasType getAasTypeByName(const std::string& typeName) = 0; // Returns a list of AAS files for the given map (absolute) map path virtual std::list getAasFilesForMap(const std::string& mapPath) = 0; }; } // namespace const char* const MODULE_AASFILEMANAGER("ZAasFileManager"); // Application-wide Accessor to the global AAS file manager inline map::IAasFileManager& GlobalAasFileManager() { // Cache the reference locally static map::IAasFileManager& _manager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_AASFILEMANAGER)) ); return _manager; } DarkRadiant-2.5.0/include/ianimationchooser.h000066400000000000000000000014211321750546400212160ustar00rootroot00000000000000#pragma once #include namespace ui { class IAnimationChooser { public: virtual ~IAnimationChooser() {} struct Result { // The selected model std::string model; // The selected animation std::string anim; bool cancelled() { return model.empty() && anim.empty(); } }; // Run the dialog and return the selected model/anim combination. // The dialog will enter a new event loop and block the UI until it's closed again. // The returned Result will be empty if the user clicks cancel. virtual Result runDialog(const std::string& preselectModel = std::string(), const std::string& preselectAnim = std::string()) = 0; // Destroys the window. Don't rely on the destructor, it won't call Destroy() for you. virtual void destroyDialog() = 0; }; } DarkRadiant-2.5.0/include/iarchive.h000066400000000000000000000100001321750546400172660ustar00rootroot00000000000000#pragma once /** * \file iarchive.h * Types relating to the use of ZIP archives (PK4 files) and their contents. * \ingroup vfs */ #include "ModResource.h" #include "imodule.h" #include #include "itextstream.h" #include class InputStream; /** * A file opened in binary mode. * \ingroup vfs */ class ArchiveFile { public: /// \brief destructor virtual ~ArchiveFile() {} /// \brief Returns the size of the file data in bytes. virtual std::size_t size() const = 0; /// \brief Returns the path to this file (relative to the filesystem root) virtual const std::string& getName() const = 0; /// \brief Returns the stream associated with this file. /// Subsequent calls return the same stream. /// The stream may be read forwards until it is exhausted. /// The stream remains valid for the lifetime of the file. virtual InputStream& getInputStream() = 0; }; typedef std::shared_ptr ArchiveFilePtr; /** * A file opened in text mode. * \ingroup vfs */ class ArchiveTextFile : public ModResource { public: /// \brief Returns the path to this file (relative to the filesystem root) virtual const std::string& getName() const = 0; /// \brief Returns the stream associated with this file. /// Subsequent calls return the same stream. /// The stream may be read forwards until it is exhausted. /// The stream remains valid for the lifetime of the file. virtual TextInputStream& getInputStream() = 0; }; typedef std::shared_ptr ArchiveTextFilePtr; /** * Representation of an archive in the virtual filesystem. * This might be a PK4/ZIP file or a regular mod directory. * * \ingroup vfs */ class Archive { public: class Visitor { public: virtual ~Visitor() {} // Invoked for each file in an Archive virtual void visitFile(const std::string& name) = 0; // Invoked for each directory in an Archive. Return true to skip the directory. virtual bool visitDirectory(const std::string& name, std::size_t depth) = 0; }; enum EMode { eFiles = 0x01, eDirectories = 0x02, eFilesAndDirectories = 0x03, }; /// \brief destructor virtual ~Archive() {} /// \brief Returns a new object associated with the file identified by \p name, or 0 if the file cannot be opened. /// Name comparisons are case-insensitive. virtual ArchiveFilePtr openFile(const std::string& name) = 0; /// \brief Returns a new object associated with the file identified by \p name, or 0 if the file cannot be opened. /// Name comparisons are case-insensitive. virtual ArchiveTextFilePtr openTextFile(const std::string& name) = 0; /// Returns true if the file identified by \p name can be opened. /// Name comparisons are case-insensitive. virtual bool containsFile(const std::string& name) = 0; /// \brief Performs a depth-first traversal of the archive tree starting at \p root. /// Traverses the entire tree if \p root is "". /// When a file is encountered, calls \c visitor.file passing the file name. /// When a directory is encountered, calls \c visitor.directory passing the directory name. /// Skips the directory if \c visitor.directory returned true. /// Root comparisons are case-insensitive. /// Names are mixed-case. virtual void traverse(Visitor& visitor, const std::string& root) = 0; }; typedef std::shared_ptr ArchivePtr; const std::string MODULE_ARCHIVE("Archive"); /** * Loader module for ZIP archives. * * \ingroup vfs */ class ArchiveLoader : public RegisterableModule { public: // greebo: Returns the opened file or NULL if failed. virtual ArchivePtr openArchive(const std::string& name) = 0; // get the supported file extension virtual const std::string& getExtension() = 0; }; /** * Return an ArchiveLoader module for the specified filetype. * * \ingroup vfs */ inline ArchiveLoader& GlobalArchive(const std::string& fileType) { // Cache the reference locally static ArchiveLoader& _archive( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_ARCHIVE + fileType) // e.g. ArchivePK4 ) ); return _archive; } DarkRadiant-2.5.0/include/ibrush.h000066400000000000000000000145071321750546400170100ustar00rootroot00000000000000#pragma once #include "inode.h" #include "imodule.h" #include "math/Vector2.h" #include "math/Vector3.h" #include class Matrix4; class Plane3; const std::string RKEY_ENABLE_TEXTURE_LOCK("user/ui/brush/textureLock"); class BrushCreator : public RegisterableModule { public: virtual scene::INodePtr createBrush() = 0; }; // The structure defining a single corner point of an IWinding struct WindingVertex { Vector3 vertex; // The 3D coordinates of the point Vector2 texcoord; // The UV coordinates Vector3 tangent; // The tangent Vector3 bitangent; // The bitangent Vector3 normal; // The normals std::size_t adjacent; // The index of the adjacent WindingVertex // greebo: This operator is needed to enable scripting support // using boost::python's vector_indexing_suite. bool operator==(const WindingVertex& other) const { return (vertex == other.vertex && texcoord == other.texcoord && tangent == other.tangent && bitangent == other.bitangent && normal == other.normal && adjacent == other.adjacent); } }; // A Winding consists of several connected WindingVertex objects, // each of which holding information about a single corner point. typedef std::vector IWinding; // Interface for a face plane class IFace { public: // Destructor virtual ~IFace() {} // Submits the current state to the UndoSystem, to make further actions undo-able virtual void undoSave() = 0; // Shader accessors virtual const std::string& getShader() const = 0; virtual void setShader(const std::string& name) = 0; // Shifts the texture by the given s,t amount in texture space virtual void shiftTexdef(float s, float t) = 0; // Scales the tex def by the given factors in texture space virtual void scaleTexdef(float s, float t) = 0; // Rotates the texture by the given angle virtual void rotateTexdef(float angle) = 0; // Fits the texture on this face virtual void fitTexture(float s_repeat, float t_repeat) = 0; // Flips the texture by the given flipAxis (0 == x-axis, 1 == y-axis) virtual void flipTexture(unsigned int flipAxis) = 0; // This translates the texture as much towards the origin in texture space as possible without changing the world appearance. virtual void normaliseTexture() = 0; // Get access to the actual Winding object virtual IWinding& getWinding() = 0; virtual const IWinding& getWinding() const = 0; virtual const Plane3& getPlane3() const = 0; /** * Returns the 3x3 texture matrix for this face, containing shift, scale and rotation. * * xx, yx, xy and yy hold the scale and rotation * tx and ty hold the shift */ virtual Matrix4 getTexDefMatrix() const = 0; }; // Brush Interface class IBrush { public: virtual ~IBrush() {} // Returns the number of faces for this brush virtual std::size_t getNumFaces() const = 0; // Get a reference to the face by index in [0..getNumFaces). virtual IFace& getFace(std::size_t index) = 0; // Const variant of the above virtual const IFace& getFace(std::size_t index) const = 0; // Add a new face to this brush, using the given plane object, returns a reference to the new face virtual IFace& addFace(const Plane3& plane) = 0; // Add a new face to this brush, using the given plane, texdef matrix and shader name virtual IFace& addFace(const Plane3& plane, const Matrix4& texDef, const std::string& shader) = 0; // Returns true when this brush has no faces virtual bool empty() const = 0; // Returns true if any face of the brush contributes to the final B-Rep. virtual bool hasContributingFaces() const = 0; // Removes faces that do not contribute to the brush. // This is useful for cleaning up after CSG operations on the brush. // Note: removal of empty faces is not performed during direct brush manipulations, // because it would make a manipulation irreversible if it created an empty face. virtual void removeEmptyFaces() = 0; // Sets the shader of all faces to the given name virtual void setShader(const std::string& newShader) = 0; // Returns TRUE if any of the faces has the given shader virtual bool hasShader(const std::string& name) = 0; // Returns TRUE if any of the brush's faces has a visible material, FALSE if all faces are effectively hidden virtual bool hasVisibleMaterial() const = 0; /** * greebo: This is used by the filter system (for example) to trigger * an update of the cached visibility flags. This enables a brush * to quickly cull its hidden faces without issuing lots of internal calls. */ virtual void updateFaceVisibility() = 0; // Saves the current state to the undo stack. // Call this before manipulating the brush to make your action undo-able. virtual void undoSave() = 0; enum DetailFlag { Structural = 0, Detail = 1 << 27, // 134217728 }; /** * Q3-compatibility feature, get the value of the detail/structural flag */ virtual DetailFlag getDetailFlag() const = 0; /** * Q3-compatibility feature, set the detail/structural flag */ virtual void setDetailFlag(DetailFlag newValue) = 0; }; // Forward-declare the Brush object, only accessible from main binary class Brush; class IBrushNode { public: virtual ~IBrushNode() {} /** greebo: Retrieves the contained Brush from the BrushNode */ virtual Brush& getBrush() = 0; // Returns the IBrush interface virtual IBrush& getIBrush() = 0; }; typedef std::shared_ptr IBrushNodePtr; inline bool Node_isBrush(const scene::INodePtr& node) { return node->getNodeType() == scene::INode::Type::Brush; //return std::dynamic_pointer_cast(node) != NULL; } // Casts the node onto a BrushNode and returns the Brush pointer inline Brush* Node_getBrush(const scene::INodePtr& node) { IBrushNodePtr brushNode = std::dynamic_pointer_cast(node); if (brushNode != NULL) { return &brushNode->getBrush(); } return NULL; } // Casts the node onto a BrushNode and returns the IBrush pointer inline IBrush* Node_getIBrush(const scene::INodePtr& node) { IBrushNodePtr brushNode = std::dynamic_pointer_cast(node); if (brushNode != NULL) { return &brushNode->getIBrush(); } return NULL; } const std::string MODULE_BRUSHCREATOR("Doom3BrushCreator"); inline BrushCreator& GlobalBrushCreator() { // Cache the reference locally static BrushCreator& _brushCreator( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_BRUSHCREATOR) ) ); return _brushCreator; } DarkRadiant-2.5.0/include/icamera.h000066400000000000000000000021461321750546400171110ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "math/Vector3.h" namespace ui { enum { CAMERA_PITCH = 0, // up / down CAMERA_YAW = 1, // left / right CAMERA_ROLL = 2, // fall over }; /** * The "global" interface of DarkRadiant's camera module. */ class ICamera : public RegisterableModule { public: /** * greebo: Sets the camera origin to the given using the given . */ virtual void focusCamera(const Vector3& point, const Vector3& angles) = 0; }; typedef std::shared_ptr ICameraPtr; } // namespace const std::string MODULE_CAMERA("Camera"); // Accessor // (this is named CameraView to avoid name conflicts with the existing GlobalCamera() accessor) inline ui::ICamera& GlobalCameraView() { // Cache the reference locally static ui::ICamera& _camera( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_CAMERA) ) ); return _camera; } class Matrix4; class CameraView { public: virtual ~CameraView() {} virtual void setModelview(const Matrix4& modelview) = 0; virtual void setFieldOfView(float fieldOfView) = 0; }; DarkRadiant-2.5.0/include/icameraview.h000066400000000000000000000014431321750546400200030ustar00rootroot00000000000000#pragma once #include "iinteractiveview.h" #include "math/Vector3.h" // Abstract class used when handling mouse events // see also: class IOrthoView in iorthoview.h class ICameraView : public IInteractiveView { public: virtual ~ICameraView() {} // Move the camera's origin virtual Vector3 getCameraOrigin() const = 0; virtual void setCameraOrigin(const Vector3& newOrigin) = 0; // Returns the vector pointing to the "right" virtual Vector3 getRightVector() const = 0; // Returns the vector pointing "up" virtual Vector3 getUpVector() const = 0; // Returns the vector pointing "forward" virtual Vector3 getForwardVector() const = 0; // Freemove mode virtual void enableFreeMove() = 0; virtual void disableFreeMove() = 0; virtual bool freeMoveEnabled() const = 0; }; DarkRadiant-2.5.0/include/iclipper.h000066400000000000000000000047701321750546400173240ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "iorthoview.h" #include "math/Vector3.h" // The possible split modes enum EBrushSplit { eFront, eBack, eFrontAndBack, }; enum PlaneClassification { ePlaneFront = 0, ePlaneBack = 1, ePlaneOn = 2, }; struct BrushSplitType { std::size_t counts[3]; BrushSplitType() { counts[0] = 0; counts[1] = 0; counts[2] = 0; } BrushSplitType& operator+=(const BrushSplitType& other) { counts[0] += other.counts[0]; counts[1] += other.counts[1]; counts[2] += other.counts[2]; return *this; } }; class ClipPoint; const char* const MODULE_CLIPPER("Clipper"); const char* const RKEY_CLIPPER_CAULK_SHADER("user/ui/clipper/caulkTexture"); /* greebo: This is the interface the clipper module has to provide. */ class IClipper : public RegisterableModule { public: // Gets called if the clip mode is toggled on/off virtual void onClipMode(bool enabled) = 0; // Returns true if the clip mode is enabled virtual bool clipMode() const = 0; // Methods to clip the selected brush or to split it (keeping both parts) virtual void clip() = 0; virtual void splitClip() = 0; // Inverts the clip plane "direction" to determine which part of the brush is kept after clipping virtual void flipClip() = 0; // True when new faces should get caulked virtual bool useCaulkForNewFaces() const = 0; // Returns the name of the caulk shader to be used for clip-created planes virtual const std::string& getCaulkShader() const = 0; // Return or set the view type of the xy view (needed for the projections). virtual EViewType getViewType() const = 0; virtual void setViewType(EViewType viewType) = 0; // Returns the pointer to the currently moved clip point virtual ClipPoint* getMovingClip() = 0; virtual void setMovingClip(ClipPoint* clipPoint) = 0; // Retrieves the reference to the coordinates of the currently "selected" clip point virtual Vector3& getMovingClipCoords() = 0; virtual ClipPoint* find(const Vector3& point, EViewType viewtype, float scale) = 0; // Adds the given point as new clip point. virtual void newClipPoint(const Vector3& point) = 0; // Draws the clip points into the XYView virtual void draw(float scale) = 0; // Updates the clip plane information virtual void update() = 0; }; // The accessor for the clipper module inline IClipper& GlobalClipper() { // Cache the reference locally static IClipper& _clipper( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_CLIPPER) ) ); return _clipper; } DarkRadiant-2.5.0/include/icommandsystem.h000066400000000000000000000213571321750546400205510ustar00rootroot00000000000000#pragma once #include #include #include "math/Vector2.h" #include "math/Vector3.h" #include "imodule.h" #include "string/convert.h" namespace cmd { // Use these to define argument types enum ArgumentTypeFlags { ARGTYPE_VOID = 0, ARGTYPE_STRING = 1 << 0, ARGTYPE_INT = 1 << 1, ARGTYPE_DOUBLE = 1 << 2, ARGTYPE_VECTOR3 = 1 << 3, ARGTYPE_VECTOR2 = 1 << 4, // future types go here ARGTYPE_OPTIONAL = 1 << 16, }; // One command argument, provides several getter methods class Argument { std::string _strValue; double _doubleValue; int _intValue; Vector3 _vector3Value; Vector2 _vector2Value; // The type flags std::size_t _type; public: Argument() : _doubleValue(0), _intValue(0), _vector3Value(0,0,0), _vector2Value(0,0), _type(ARGTYPE_VOID) {} // String => Argument constructor Argument(const std::string& str) : _strValue(str), _doubleValue(string::convert(str)), _intValue(string::convert(str)), _vector3Value(string::convert(str)), _vector2Value(Vector2(str)), _type(ARGTYPE_STRING) { tryNumberConversion(); tryVectorConversion(); } // Double => Argument constructor Argument(const double d) : _strValue(string::to_string(d)), _doubleValue(d), _intValue(static_cast(d)), _vector3Value(d,d,d), _vector2Value(d,d), _type(ARGTYPE_DOUBLE) { // Enable INT flag if double value is rounded if (lrint(_doubleValue) == _intValue) { _type |= ARGTYPE_INT; } } // Int => Argument constructor Argument(const int i) : _strValue(string::to_string(i)), _doubleValue(static_cast(i)), _intValue(i), _vector3Value(i,i,i), _vector2Value(i,i), _type(ARGTYPE_INT|ARGTYPE_DOUBLE) // INT can be used as DOUBLE too {} // Vector3 => Argument constructor Argument(const Vector3& v) : _strValue(string::to_string(v[0]) + " " + string::to_string(v[1]) + " " + string::to_string(v[2])), _doubleValue(v.getLength()), _intValue(static_cast(v.getLength())), _vector3Value(v), _vector2Value(v[0], v[1]), _type(ARGTYPE_VECTOR3) {} // Vector2 => Argument constructor Argument(const Vector2& v) : _strValue(string::to_string(v[0]) + " " + string::to_string(v[1]) + " " + string::to_string(v[2])), _doubleValue(v.getLength()), _intValue(static_cast(v.getLength())), _vector3Value(v[0], v[1], 0), _vector2Value(v), _type(ARGTYPE_VECTOR2) {} // Copy Constructor Argument(const Argument& other) : _strValue(other._strValue), _doubleValue(other._doubleValue), _intValue(other._intValue), _vector3Value(other._vector3Value), _vector2Value(other._vector2Value), _type(other._type) {} std::size_t getType() const { return _type; } std::string getString() const { return _strValue; } int getInt() const { return _intValue; } double getDouble() const { return _doubleValue; } Vector3 getVector3() const { return _vector3Value; } Vector2 getVector2() const { return _vector2Value; } private: void tryNumberConversion() { // Try to cast the string value to numbers try { _intValue = std::stoi(_strValue); // cast succeeded _type |= ARGTYPE_INT; } catch (std::logic_error&) {} try { _doubleValue = std::stod(_strValue); // cast succeeded _type |= ARGTYPE_DOUBLE; } catch (std::invalid_argument&) {} } void tryVectorConversion() { // Use a stringstream to parse the string std::stringstream strm(_strValue); strm << std::skipws; // Try converting the first two values strm >> _vector2Value.x(); strm >> _vector2Value.y(); if (!strm.fail()) { _type |= ARGTYPE_VECTOR2; // Try to parse the third value strm >> _vector3Value.z(); if (!strm.fail()) { // Third value successfully parsed _type |= ARGTYPE_VECTOR3; // Copy the two values from the parsed Vector2 _vector3Value.x() = _vector2Value.x(); _vector3Value.y() = _vector2Value.y(); } } } }; typedef std::vector ArgumentList; /** * greebo: A command target must take an ArgumentList argument, like this: * * void doSomething(const ArgumentList& args); * * This can be both a free function and a member function. */ typedef std::function Function; // A command signature consists just of arguments, no return types class Signature : public std::vector { public: Signature() {} // Additional convenience constructors Signature(std::size_t type1) { push_back(type1); } Signature(std::size_t type1, std::size_t type2) { push_back(type1); push_back(type2); } Signature(std::size_t type1, std::size_t type2, std::size_t type3) { push_back(type1); push_back(type2); push_back(type3); } Signature(std::size_t type1, std::size_t type2, std::size_t type3, std::size_t type4) { push_back(type1); push_back(type2); push_back(type3); push_back(type4); } Signature(std::size_t type1, std::size_t type2, std::size_t type3, std::size_t type4, std::size_t type5) { push_back(type1); push_back(type2); push_back(type3); push_back(type4); push_back(type5); } }; /** * greebo: Auto-completion information returned by the CommandSystem * when the user is entering a partial command. */ struct AutoCompletionInfo { // The command prefix this info is referring to std::string prefix; // The candidaes, alphabetically ordered, case-insensitively typedef std::vector Candidates; Candidates candidates; }; class ICommandSystem : public RegisterableModule { public: /** * Visit each command/bind using the given lambda. The functor is going to be called * with the command name as argument. */ virtual void foreachCommand(const std::function& functor) = 0; /** * greebo: Declares a new command with the given signature. */ virtual void addCommand(const std::string& name, Function func, const Signature& signature = Signature()) = 0; /** * Remove a named command. */ virtual void removeCommand(const std::string& name) = 0; /** * greebo: Define a new statement, which consists of a name and a * string to execute. * * Consider this as some sort of macro. * * @statementName: The name of the statement, e.g. "exportASE" * @string: The string to execute. * @saveStatementToRegistry: when TRUE (default) this statement/bind * is saved to the registry at program shutdown. Pass FALSE if you * don't want to let this statement persist between sessions. */ virtual void addStatement(const std::string& statementName, const std::string& string, bool saveStatementToRegistry = true) = 0; /** * Visit each statement (bind) using the given lambda. The functor is going to be called * with the statement name as argument. */ virtual void foreachStatement(const std::function& functor, bool customStatementsOnly = false) = 0; /** * Returns the signature for the named command or bind. Statements * always have an empty signature. */ virtual Signature getSignature(const std::string& name) = 0; /** * greebo: Executes the given string as if the user had typed it * in the command console. The passed string can be a sequence of * statements separated by semicolon ';' characters. Each statement * can have zero or more arguments, separated by spaces. * * It is possible to pass string arguments by using * double- or single-quote characters. * e.g. "This; string; will be; treated as a whole". * * The last command needs not to be delimited by a semicolon. * * Example: nudgeLeft; nudgeRight -1 0 0; write "Bla! Test" */ virtual void execute(const std::string& input) = 0; /** * Execute the named command with the given arguments. */ virtual void executeCommand(const std::string& name) = 0; virtual void executeCommand(const std::string& name, const Argument& arg1) = 0; virtual void executeCommand(const std::string& name, const Argument& arg1, const Argument& arg2) = 0; virtual void executeCommand(const std::string& name, const Argument& arg1, const Argument& arg2, const Argument& arg3) = 0; // For more than 3 arguments, use this method to pass a vector of arguments virtual void executeCommand(const std::string& name, const ArgumentList& args) = 0; /** * greebo: Returns autocompletion info for the given prefix. */ virtual AutoCompletionInfo getAutoCompletionInfo(const std::string& prefix) = 0; }; typedef std::shared_ptr ICommandSystemPtr; } // namespace cmd const char* const MODULE_COMMANDSYSTEM("CommandSystem"); // This is the accessor for the commandsystem inline cmd::ICommandSystem& GlobalCommandSystem() { // Cache the reference locally static cmd::ICommandSystem& _cmdSystem( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_COMMANDSYSTEM) ) ); return _cmdSystem; } DarkRadiant-2.5.0/include/icounter.h000066400000000000000000000023231321750546400173350ustar00rootroot00000000000000#ifndef ICOUNTER_H_ #define ICOUNTER_H_ #include #include "imodule.h" class ICounter { public: class Observer { public: virtual ~Observer() {} // Gets called by the Counter class on count change virtual void countChanged() = 0; }; /** Destructor */ virtual ~ICounter() {} /** greebo: Decrements/increments the counter. */ virtual void increment() = 0; virtual void decrement() = 0; /** greebo: Returns the current count. */ virtual std::size_t get() const = 0; }; // Known counters enum CounterType { counterBrushes, counterPatches, counterEntities, }; const std::string MODULE_COUNTER("Counters"); /** greebo: This abstract class defines the interface to the core application. * Use this to access methods from the main codebase in radiant/ */ class ICounterManager : public RegisterableModule { public: // Returns the Counter object of the given type virtual ICounter& getCounter(CounterType counter) = 0; }; inline ICounterManager& GlobalCounters() { // Cache the reference locally static ICounterManager& _counters( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_COUNTER) ) ); return _counters; } #endif /*ICOUNTER_H_*/ DarkRadiant-2.5.0/include/icurve.h000066400000000000000000000020661321750546400170060ustar00rootroot00000000000000#ifndef ICURVE_H_ #define ICURVE_H_ #include "inode.h" class CurveNode { public: /** destructor */ virtual ~CurveNode() {} /** greebo: Returns true if the curve has 0 control points. */ virtual bool hasEmptyCurve() = 0; /** greebo: Appends a control point at the end of the chain. */ virtual void appendControlPoints(unsigned int numPoints) = 0; /** greebo: As the name states, this removes the selected * control points from the curve. */ virtual void removeSelectedControlPoints() = 0; /** greebo: This inserts a control point BEFORE each * selected control point of the curve. * Naturally, this doesn't work if the first vertex * is selected. */ virtual void insertControlPointsAtSelected() = 0; /** greebo: Converts the type of the curve from CatmullRom * to NURBS and vice versa. */ virtual void convertCurveType() = 0; }; typedef std::shared_ptr CurveNodePtr; inline CurveNodePtr Node_getCurve(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } #endif /*ICURVE_H_*/ DarkRadiant-2.5.0/include/idatastream.h000066400000000000000000000046261321750546400200130ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(INCLUDED_IDATASTREAM_H) #define INCLUDED_IDATASTREAM_H #include class StreamBase { public: virtual ~StreamBase() {} typedef std::size_t size_type; typedef unsigned char byte_type; }; /// \brief A read-only byte-stream. class InputStream : public StreamBase { public: /// \brief Attempts to read the next \p length bytes from the stream to \p buffer. /// Returns the number of bytes actually stored in \p buffer. virtual size_type read(byte_type* buffer, size_type length) = 0; }; /// \brief A write-only byte-stream. class OutputStream : public StreamBase { public: /// \brief Attempts to write \p length bytes to the stream from \p buffer. /// Returns the number of bytes actually read from \p buffer. virtual size_type write(const byte_type* buffer, size_type length) = 0; }; class SeekableStream { public: typedef int offset_type; typedef std::size_t position_type; enum seekdir { beg, cur, end, }; virtual ~SeekableStream() {} /// \brief Sets the current \p position of the stream relative to the start. virtual position_type seek(position_type position) = 0; /// \brief Sets the current \p position of the stream relative to either the start, end or current position. virtual position_type seek(offset_type offset, seekdir direction) = 0; /// \brief Returns the current position of the stream. virtual position_type tell() const = 0; }; /// \brief A seekable read-only byte-stream. class SeekableInputStream : public InputStream, public SeekableStream { }; /// \brief A seekable write-only byte-stream. class SeekableOutputStream : public OutputStream, public SeekableStream { }; #endif DarkRadiant-2.5.0/include/idialogmanager.h000066400000000000000000000130461321750546400204540ustar00rootroot00000000000000#pragma once #include "iuimanager.h" #include class wxWindow; namespace ui { class IDialog { public: virtual ~IDialog() {} enum Result { RESULT_CANCELLED = 0, RESULT_OK, RESULT_NO, RESULT_YES, }; /// Possible message types (used for IDialogManager::createMessageBox()) enum MessageType { /// Just a plain message with an OK button MESSAGE_CONFIRM, // Queries Yes//No from the user MESSAGE_ASK, /// Displays a warning message MESSAGE_WARNING, /// Displays an error message MESSAGE_ERROR, /// Has three options: Yes, No or Cancel MESSAGE_YESNOCANCEL, /// Save confirmation as per HIG 2.32/3.4.6.1 MESSAGE_SAVECONFIRMATION }; // Sets the dialog title virtual void setTitle(const std::string& title) = 0; // A handle to access dialog elements after addition typedef std::size_t Handle; typedef std::vector ComboBoxOptions; // ------------------- Elements ----------------------- /** * Adds a simple label at the current position in the dialog. * A unique handle is returned to allow for later value retrieval. * The elements are inserted in the order of calls, top to bottom. * In case of errors an invalid handle (==0) is returned. */ virtual Handle addLabel(const std::string& text) = 0; virtual Handle addComboBox(const std::string& label, const ComboBoxOptions& options) = 0; virtual Handle addEntryBox(const std::string& label) = 0; virtual Handle addPathEntry(const std::string& label, bool foldersOnly = false) = 0; virtual Handle addSpinButton(const std::string& label, double min, double max, double step, unsigned int digits) = 0; virtual Handle addCheckbox(const std::string& label) = 0; // ----------------- Element Value -------------------- // Retrieve or set an element's value by string virtual void setElementValue(const Handle& handle, const std::string& value) = 0; virtual std::string getElementValue(const Handle& handle) = 0; // ---------------------------------------------------- /** * Run the dialog an enter the main loop (block the application). * Returns the Dialog::Result, corresponding to the user's action. */ virtual Result run() = 0; }; typedef std::shared_ptr IDialogPtr; const IDialog::Handle INVALID_HANDLE = 0; class IFileChooser; typedef std::shared_ptr IFileChooserPtr; class IDirChooser; typedef std::shared_ptr IDirChooserPtr; class IResourceChooser; // defined in iresourcechooser.h class IAnimationChooser; // defined in ianimationchooser.h class IDialogManager { public: // Virtual destructor virtual ~IDialogManager() {} /** * Create a new dialog. Note that the DialogManager will hold a reference * to this dialog internally to allow scripts to reference the Dialog class * without holding the shared_ptr on their own or using wrapper classes doing so. * * Every dialog features an OK and a Cancel button by default. * * @title: The string displayed on the dialog's window bar. * @type: the dialog type to create, determines e.g. which buttons are shown. * @parent: optional top-level widget this dialog should be parented to, defaults to * GlobalMainFrame's toplevel window if left at NULL. */ virtual IDialogPtr createDialog(const std::string& title, wxWindow* parent = NULL) = 0; /** * Create a simple message box, which can either notify the user about something, * queries "Yes"/"No" or displays an error message. It usually features * an icon according to the the MessageType passed (exclamation mark, error sign). * * @title: The string displayed on the message box window bar. * @text: The text/question to be displayed. * @type: the message type this dialog represents. * @parent: optional top-level widget this dialog should be parented to, defaults to * GlobalMainFrame().getMainWindow(). */ virtual IDialogPtr createMessageBox(const std::string& title, const std::string& text, IDialog::MessageType type, wxWindow* parent = NULL) = 0; /** * Acquire a new filechooser instance with the given parameters. * * @title: The dialog title. * @open: if TRUE this is asking for "Open" files, FALSE generates a "Save" dialog. * @pattern: the type "map", "prefab", this determines the file extensions. * @defaultExt: The default extension appended when the user enters * filenames without extension. */ virtual IFileChooserPtr createFileChooser(const std::string& title, bool open, const std::string& pattern = "", const std::string& defaultExt = "") = 0; /** * Acquire a new folder chooser instance with the given parameters. * * @title: The dialog title. */ virtual IDirChooserPtr createDirChooser(const std::string& title) = 0; // Creates and returns a new Dialog class for selecting a sound shader. // It's the responsibility of the client code to call destroyDialog() on the returned object. // Optionally specify a parent window the dialog should be a child of. virtual IResourceChooser* createSoundShaderChooser(wxWindow* parent = nullptr) = 0; // Creates and returns a new Dialog class for selecting an MD5 anim. // It's the responsibility of the client code to call destroyDialog() on the returned object. // Optionally specify a parent window the dialog should be a child of. virtual IAnimationChooser* createAnimationChooser(wxWindow* parent = nullptr) = 0; }; } // namespace ui // Shortcut method inline ui::IDialogManager& GlobalDialogManager() { return GlobalUIManager().getDialogManager(); } DarkRadiant-2.5.0/include/ieclass.h000066400000000000000000000272151321750546400171370ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** * \defgroup eclass Entity class manager * \file ieclass.h * \brief Entity Class definition loader API. * \ingroup eclass */ #pragma once #include "ModResource.h" #include "imodule.h" #include "math/Vector3.h" #include #include #include #include #include /* FORWARD DECLS */ class Shader; typedef std::shared_ptr ShaderPtr; class AABB; /** * Data structure representing a single attribute on an entity class. * * \ingroup eclass */ class EntityClassAttribute { private: /** * String references are shared_ptrs to save memory. * The actual string might be owned by another entity class we're inheriting from. */ typedef std::shared_ptr StringPtr; // Reference to the name string StringPtr _typeRef; // Reference to the name string StringPtr _nameRef; // Reference to the attribute value string StringPtr _valueRef; // Reference to the description string StringPtr _descRef; public: /** * The key type (string, bool etc.). */ const std::string& getType() const { return *_typeRef; } const StringPtr& getTypeRef() const { return _typeRef; } void setType(const std::string& type) { _typeRef.reset(new std::string(type)); } void setType(const StringPtr& typeRef) { _typeRef = typeRef; } /// The attribute key name, e.g. "model", "editor_displayFolder" etc const std::string& getName() const { return *_nameRef; } const StringPtr& getNameRef() const { return _nameRef; } /** * Direct reference to the value for easy access to the value. This reference * is pointing directly at the string owned by the ValueRef shared_ptr, * which in turn might be owned by a class we're inheriting from. */ const std::string& getValue() const { return *_valueRef; } const StringPtr& getValueRef() const { return _valueRef; } /** * Sets the value of this entity class attribute. This will break up any * inheritance and make this instance owner of its value string. */ void setValue(const std::string& value) { _valueRef.reset(new std::string(value)); } void setValue(const StringPtr& valueRef) { _valueRef = valueRef; } /** * The help text associated with the key (in the DEF file). */ const std::string& getDescription() const { return *_descRef; } const StringPtr& getDescriptionRef() const { return _descRef; } void setDescription(const std::string& desc) { _descRef.reset(new std::string(desc)); } void setDescription(const StringPtr& descRef) { _descRef = descRef; } /** * Is TRUE for inherited keyvalues. */ bool inherited; /** * Construct a non-inherited EntityClassAttribute, passing the actual strings * which will be owned by this class instance. */ EntityClassAttribute(const std::string& type_, const std::string& name_, const std::string& value_, const std::string& description_ = "") : _typeRef(new std::string(type_)), _nameRef(new std::string(name_)), _valueRef(new std::string(value_)), _descRef(new std::string(description_)), inherited(false) {} /** * Construct a inherited EntityClassAttribute with a true inherited flag. * The strings are taken from the inherited attribute. * Note: this is not a copy-constructor on purpose, to allow STL assignments to * copy the actual instance values. */ EntityClassAttribute(const EntityClassAttribute& parentAttr, bool inherited_) : _typeRef(parentAttr._typeRef), // take type string, _nameRef(parentAttr._nameRef), // name string, _valueRef(parentAttr._valueRef), // value string _descRef(parentAttr._descRef), // and description from the parent attribute inherited(inherited_) {} }; /** * IEntityClass shared pointer. */ class IEntityClass; typedef std::shared_ptr IEntityClassPtr; typedef std::shared_ptr IEntityClassConstPtr; /** * Entity class interface. An entity class represents a single type * of entity that can be created by the EntityCreator. Entity classes are parsed * from .DEF files during startup. * * Entity class attribute names are compared case-insensitively, as in the * Entity class. * * \ingroup eclass */ class IEntityClass : public ModResource { public: /// Signal emitted when entity class contents are changed or reloaded virtual sigc::signal changedSignal() const = 0; /// Get the name of this entity class virtual std::string getName() const = 0; /// Get the parent entity class or NULL if there is no parent virtual const IEntityClass* getParent() const = 0; /// Query whether this entity class represents a light. virtual bool isLight() const = 0; /* ENTITY CLASS SIZE */ /** * Query whether this entity has a fixed size. */ virtual bool isFixedSize() const = 0; /** * Return an AABB representing the declared size of this entity. This is * only valid for fixed size entities. * * @returns * AABB enclosing the "editor_mins" and "editor_maxs" points defined in the * entityDef. */ virtual AABB getBounds() const = 0; /* ENTITY CLASS COLOURS */ /// Return the display colour of this entity class virtual const Vector3& getColour() const = 0; /** * Get the named Shader used for rendering this entity class in * wireframe mode. */ virtual const std::string& getWireShader() const = 0; /** * Get the Shader used for rendering this entity class in * filled mode. */ virtual const std::string& getFillShader() const = 0; /* ENTITY CLASS ATTRIBUTES */ /** * Return a single named EntityClassAttribute from this EntityClass. * * @param name * The name of the EntityClassAttribute to find, interpreted case-insensitively. * * @return * A reference to the named EntityClassAttribute. If the named attribute is * not found, an empty EntityClassAttribute is returned. */ virtual EntityClassAttribute& getAttribute(const std::string& name) = 0; virtual const EntityClassAttribute& getAttribute(const std::string& name) const = 0; /** * Enumerate the EntityClassAttibutes in turn. * * \param visitor * Function that will be invoked for each EntityClassAttibute. * * \param editorKeys * true if editor keys (those which start with "editor_") should be passed * to the visitor, false if they should be skipped. */ virtual void forEachClassAttribute( std::function visitor, bool editorKeys = false ) const = 0; /* MODEL AND SKIN */ /** Retrieve the model path for this entity. * * @returns * The VFS model path, or the empty string if there is no model. */ virtual const std::string& getModelPath() const = 0; /** Get the model skin, or the empty string if there is no skin. */ virtual const std::string& getSkin() const = 0; /** * Returns true if this entity is of type or inherits from the * given entity class name. className is treated case-sensitively. */ virtual bool isOfType(const std::string& className) = 0; }; /** * Structure ontains the information of a model {} block as defined in a * Doom3 .def file. * * \ingroup eclass */ class IModelDef : public ModResource { public: bool resolved; std::string name; std::string mesh; std::string skin; std::string parent; typedef std::map Anims; Anims anims; std::string modName; IModelDef() : resolved(false), modName("base") {} std::string getModName() const { return modName; } }; typedef std::shared_ptr IModelDefPtr; /** * EntityClass visitor interface. * * \ingroup eclass */ class EntityClassVisitor { public: virtual ~EntityClassVisitor() {} virtual void visit(const IEntityClassPtr& eclass) = 0; }; /** * ModelDef visitor interface. * * \ingroup eclass */ class ModelDefVisitor { public: virtual ~ModelDefVisitor() {} virtual void visit(const IModelDefPtr& modelDef) = 0; }; const char* const MODULE_ECLASSMANAGER("EntityClassManager"); /** * EntityClassManager interface. The entity class manager is responsible for * maintaining a list of available entity classes which the EntityCreator can * insert into a map. * * \ingroup eclass */ class IEntityClassManager : public RegisterableModule { public: /// Signal emitted when all DEFs are reloaded virtual sigc::signal defsReloadedSignal() const = 0; /** * Return the IEntityClass corresponding to the given name, creating it if * necessary. If it is created, the has_brushes parameter will be used to * determine whether the new entity class should be brush-based or not. * * @deprecated * Use findClass() instead. */ virtual IEntityClassPtr findOrInsert(const std::string& name, bool has_brushes) = 0; /** * Lookup an entity class by name. If the class is not found, a null pointer * is returned. * * @param name * Name of the entity class to look up. */ virtual IEntityClassPtr findClass(const std::string& name) = 0; /** * Iterate over all entity defs using the given visitor. */ virtual void forEachEntityClass(EntityClassVisitor& visitor) = 0; virtual void realise() = 0; virtual void unrealise() = 0; /** * greebo: This reloads the entityDefs and modelDefs from all files. Does not * change the scenegraph, only the contents of the EClass objects are * re-parsed. All IEntityClassPtrs remain valid, no entityDefs are removed. * * Note: This is NOT the same as unrealise + realise */ virtual void reloadDefs() = 0; /** * greebo: Finds the model def with the given name. Might return NULL if not found. */ virtual IModelDefPtr findModel(const std::string& name) = 0; /** * Iterate over each ModelDef using the given visitor class. */ virtual void forEachModelDef(ModelDefVisitor& visitor) = 0; }; /** * Return the global EntityClassManager to the application. * * \ingroup eclass */ inline IEntityClassManager& GlobalEntityClassManager() { // Cache the reference locally static IEntityClassManager& _eclassMgr( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_ECLASSMANAGER) ) ); return _eclassMgr; } DarkRadiant-2.5.0/include/ientity.h000066400000000000000000000263011321750546400171740ustar00rootroot00000000000000#pragma once #include "inode.h" #include "ipath.h" #include "imodule.h" #include "irender.h" #include "inameobserver.h" #include class IEntityClass; typedef std::shared_ptr IEntityClassPtr; typedef std::shared_ptr IEntityClassConstPtr; // Observes a single entity key value and gets notified on change class KeyObserver { public: virtual ~KeyObserver() {} /** * This event gets called when the observed keyvalue changes. * The new value is passed as argument, which can be an empty string. */ virtual void onKeyValueChanged(const std::string& newValue) = 0; }; class EntityKeyValue : public NameObserver { public: virtual ~EntityKeyValue() {} /** greebo: Retrieves the actual value of this key */ virtual const std::string& get() const = 0; /** greebo: Sets the value of this key */ virtual void assign(const std::string& other) = 0; /** greebo: Attaches/detaches a callback to get notified about * the key change. */ virtual void attach(KeyObserver& observer) = 0; virtual void detach(KeyObserver& observer) = 0; }; typedef std::shared_ptr EntityKeyValuePtr; /** * Interface for a map entity. The Entity is the main building block of a * map, and the uppermost layer in the scenegraph under the root node. Each * entity contains a arbitrary dictionary of strings ("properties" or * "spawnargs") containing information about this entity which is used by the * game engine to modify its behaviour, and may additionally contain child * primitives (brushes and patches) depending on its type. * * At the minimum, each Entity must contain three properties: "name" which * contains a map-unique string identifier, "classname" which identifies the * entity class to the game, and "origin" which stores the location of the * entity in 3-dimensional world space. * * A valid Id Tech 4 map must contain at least one entity: the * "worldspawn" which is the parent of all map geometry primitives. * * greebo: Note that keys are treated case-insensitively in Doom 3, so * the Entity class will return the same result for "MYKeY" as for "mykey". */ class Entity { public: // A container with key => value pairs typedef std::vector< std::pair > KeyValuePairs; /** * \brief * Abstract base class for entity observers. * * An entity observer receives notifications when keyvalues are inserted or * deleted on the entity it is observing. */ class Observer { public: virtual ~Observer() {} /** * \brief * Notification that a new key value has been inserted on the entity. */ virtual void onKeyInsert(const std::string& key, EntityKeyValue& value) { } /** * \brief * Notification that a key value has changed on the entity. */ virtual void onKeyChange(const std::string& key, const std::string& val) { } /** * \brief * Notification that a key value has been removed from the entity. */ virtual void onKeyErase(const std::string& key, EntityKeyValue& value) { } }; // Function typedef to visit keyvalues typedef std::function KeyValueVisitFunctor; // Function typedef to visit actual EntityKeyValue objects, not just the string values typedef std::function EntityKeyValueVisitFunctor; virtual ~Entity() {} /** * Return the entity class object for this entity. */ virtual IEntityClassPtr getEntityClass() const = 0; /** * Enumerate key values on this entity using a function object taking * key and value as string arguments. */ virtual void forEachKeyValue(const KeyValueVisitFunctor& visitor) const = 0; // Similar to above, visiting the EntityKeyValue objects itself, not just the string value. virtual void forEachEntityKeyValue(const EntityKeyValueVisitFunctor& visitor) = 0; /** Set a key value on this entity. Setting the value to "" will * remove the key. * * @param key * The key to set. * * @param value * Value to give the key, or the empty string to remove the key. */ virtual void setKeyValue(const std::string& key, const std::string& value) = 0; /* Retrieve a key value from the entity. * * @param key * The key to retrieve. * * @returns * The current value for this key, or the empty string if it does not * exist. */ virtual std::string getKeyValue(const std::string& key) const = 0; /** * greebo: Checks whether the given key is inherited or not. * * @returns: TRUE if the value is inherited, * FALSE when it is not or when the key doesn't exist at all. */ virtual bool isInherited(const std::string& key) const = 0; /** * Return the list of Key/Value pairs matching the given prefix, case ignored. * * This method performs a search for all spawnargs whose key * matches the given prefix, with a suffix consisting of zero or more * arbitrary characters. For example, if "target" were specified as the * prefix, the list would include "target", "target0", "target127" etc. * * This operation may not have high performance, due to the need to scan * for matching names, therefore should not be used in performance-critical * code. * * @param prefix * The prefix to search for, interpreted case-insensitively. * * @return * A list of KeyValue pairs matching the provided prefix. This * list will be empty if there were no matches. */ virtual KeyValuePairs getKeyValuePairs(const std::string& prefix) const = 0; /** greebo: Returns true if the entity is a model. For Doom3, this is * usually true when the classname == "func_static" and * the non-empty spawnarg "model" != "name". */ virtual bool isModel() const = 0; /** * Returns true if this entity is the worldspawn, which can be game-specific, * but is usually true if this entity's classname equals "worldspawn" */ virtual bool isWorldspawn() const = 0; virtual bool isContainer() const = 0; /** * \brief * Attach an Entity::Observer to this Entity. */ virtual void attachObserver(Observer* observer) = 0; /** * \brief * Detach an Entity::Observer from this Entity. */ virtual void detachObserver(Observer* observer) = 0; /** * Returns true if this entity is of type or inherits from the * given entity class name. className is treated case-sensitively. */ virtual bool isOfType(const std::string& className) = 0; }; /// Interface for a INode subclass that contains an Entity class IEntityNode : public virtual IRenderEntity, public virtual scene::INode { public: virtual ~IEntityNode() {} /// Get a modifiable reference to the contained Entity virtual Entity& getEntity() = 0; /** * greebo: Tells the entity to reload the child model. This usually * includes removal of the child model node and triggering * a "skin changed" event. */ virtual void refreshModel() = 0; }; typedef std::shared_ptr IEntityNodePtr; inline Entity* Node_getEntity(const scene::INodePtr& node) { IEntityNodePtr entityNode = std::dynamic_pointer_cast(node); if (entityNode != NULL) { return &(entityNode->getEntity()); } return NULL; } inline bool Node_isEntity(const scene::INodePtr& node) { //assert(!((std::dynamic_pointer_cast(node) != nullptr) ^ (node->getNodeType() == scene::INode::Type::Entity))); return node->getNodeType() == scene::INode::Type::Entity; } /** * greebo: This is an abstract representation of a target. * In Doom3 maps, a Target can be any entity node, that's * why this object encapsulates a reference to an actual * scene::INode. * * Note: Such a Target object can be empty. That's the case for * entities referring to non-existing entities in their * "target" spawnarg. * * All ITargetableObjects are owned by the TargetManager class. */ class ITargetableObject { public: virtual ~ITargetableObject() {} // Returns the scene node behind this target. If the named target // cannot be resolved in the current scene, an empty pointer is returned. virtual const scene::INode* getNode() const = 0; // Use this method to check whether the node can be resolved virtual bool isEmpty() const = 0; }; typedef std::shared_ptr ITargetableObjectPtr; /** * greebo: The TargetManager keeps track of all ITargetableObjects * in the current scene/map. A TargetManager instance is owned * by the RootNode. TargetManager instances can be acquired through * the EntityCreator interface. * * Clients acquire a named ITargetableObjectPtr by calling getTarget(). This * always succeeds - if the named ITargetableObject is not found, * a new, empty one is created. * * ITargetableObject object (can be empty) * ________ * / \ * Entity | | * TargetKey ----->>| -------->> holds scene::INodePtr (==NULL, if empty) * | | * \________/ */ class ITargetManager { public: /** * Returns the Target with the given name. * This never returns NULL, an ITargetableObject is created if it doesn't exist yet. */ virtual ITargetableObjectPtr getTarget(const std::string name) = 0; /** * greebo: Associates the named Target with the given scene::INode. * The Target will be created if it doesn't exist yet. */ virtual void associateTarget(const std::string& name, const scene::INode& node) = 0; /** * greebo: Disassociates the Target from the given name. The node * must also be passed to allow the manager to check the request. * Otherwise it would be possible for cloned nodes to dissociate * the target from their source node. */ virtual void clearTarget(const std::string& name, const scene::INode& node) = 0; }; typedef std::shared_ptr ITargetManagerPtr; const std::string MODULE_ENTITYCREATOR("Doom3EntityCreator"); /** * \brief * Interface for the entity creator module. */ class EntityCreator : public RegisterableModule { public: virtual ~EntityCreator() {} /// Create an entity node with the given entity class. virtual IEntityNodePtr createEntity(const IEntityClassPtr& eclass) = 0; /// Connect the two given entity nodes using the "target" system. virtual void connectEntities(const scene::INodePtr& source, const scene::INodePtr& target) = 0; // Constructs a new targetmanager instance (used by root nodes) virtual ITargetManagerPtr createTargetManager() = 0; }; inline EntityCreator& GlobalEntityCreator() { // Cache the reference locally static EntityCreator& _entityCreator( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_ENTITYCREATOR) ) ); return _entityCreator; } DarkRadiant-2.5.0/include/ientityinspector.h000066400000000000000000000055421321750546400211270ustar00rootroot00000000000000#pragma once #include "imodule.h" class Entity; class wxPanel; class wxWindow; namespace ui { /** * greebo: Some entity property editors may implement this interface, * to allow modules to call their dialog windows directly. */ class IPropertyEditorDialog { public: virtual ~IPropertyEditorDialog() {} /** * greebo: Run the dialog, returning the new value for the named property. */ virtual std::string runDialog(Entity* entity, const std::string& key) = 0; }; typedef std::shared_ptr IPropertyEditorDialogPtr; class IPropertyEditor; typedef std::shared_ptr IPropertyEditorPtr; /** * Abstract base for a PropertyEditor which provides * a user interface for editing spawnargs (entity keyvalues). */ class IPropertyEditor { public: virtual ~IPropertyEditor() {} /** * greebo: Retrieve the widget for packing this into a parent container. */ virtual wxPanel* getWidget() = 0; /** * Instructs the editor to update its widgets from the edited entity's key values. */ virtual void updateFromEntity() = 0; /** * Clone method for virtual construction. This method must create a new * PropertyEditor of the same type as the derive class which is implementing * the method. * * @param parent * The parent window, needed by the code to pack the widgets of this editor. * * @param entity * The Entity to edit. * * @param key * The key name which this PropertyEditor is displaying. * * @param options * PropertyEditor-specific options string, from the .game file. */ virtual IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity, const std::string& key, const std::string& options) = 0; }; class IEntityInspector : public RegisterableModule { public: /** * greebo: Retrieve the widget for packing this into a parent container. */ virtual wxPanel* getWidget() = 0; /** * Registers the given property editor and associates it with the given entity key. * (The string key is interpreted as regular expression.) */ virtual void registerPropertyEditor(const std::string& key, const IPropertyEditorPtr& editor) = 0; /** * Looks up a property editor for the given key. */ virtual IPropertyEditorPtr getRegisteredPropertyEditor(const std::string& key) = 0; /** * Removes the property editor for the given key. */ virtual void unregisterPropertyEditor(const std::string& key) = 0; // Lets the EntityInspector restore its settings from the Registry virtual void restoreSettings() = 0; }; } // namespace ui const std::string MODULE_ENTITYINSPECTOR("EntityInspector"); inline ui::IEntityInspector& GlobalEntityInspector() { // Cache the reference locally static ui::IEntityInspector& _inspector( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_ENTITYINSPECTOR) ) ); return _inspector; } DarkRadiant-2.5.0/include/ieventmanager.h000066400000000000000000000147221321750546400203400ustar00rootroot00000000000000#pragma once #include #include #include #include #include "imodule.h" #include class wxWindow; class wxMenuItem; class wxToolBarToolBase; class wxButton; class wxToggleButton; class wxMouseEvent; class wxKeyEvent; class wxToolBar; class wxTopLevelWindow; /* greebo: Below are the actual events that are "read" by the views/observers to * interpret the mouseclicks. */ namespace ui { // Enum used for events tracking the key state enum KeyEventType { KeyPressed, KeyReleased, }; typedef std::function KeyStateChangeCallback; } // namespace ui class IAccelerator { public: // destructor virtual ~IAccelerator() {} // Get/set the key value virtual void setKey(const unsigned int key) = 0; virtual unsigned int getKey() const = 0; // Get/Set the modifier flags virtual void setModifiers(const unsigned int modifiers) = 0; virtual unsigned int getModifiers() const = 0; }; class IEvent { public: // destructor virtual ~IEvent() {} // Handles the incoming keyUp / keyDown calls virtual void keyUp() = 0; virtual void keyDown() = 0; // Enables/disables this event virtual void setEnabled(const bool enabled) = 0; // Connect a wxTopLevelWindow to this event virtual void connectTopLevelWindow(wxTopLevelWindow* widget) = 0; virtual void disconnectTopLevelWindow(wxTopLevelWindow* widget) = 0; virtual void connectToolItem(wxToolBarToolBase* item) = 0; virtual void disconnectToolItem(wxToolBarToolBase* item) = 0; virtual void connectMenuItem(wxMenuItem* item) = 0; virtual void disconnectMenuItem(wxMenuItem* item) = 0; virtual void connectButton(wxButton* button) = 0; virtual void disconnectButton(wxButton* button) = 0; virtual void connectToggleButton(wxToggleButton* button) = 0; virtual void disconnectToggleButton(wxToggleButton* button) = 0; // Exports the current state to the widgets virtual void updateWidgets() = 0; // Returns true if this event could be toggled (returns false if the event is not a Toggle). virtual bool setToggled(const bool toggled) = 0; /** greebo: Returns true if the event is a Toggle (or a subtype of a Toggle) */ virtual bool isToggle() const = 0; // Returns true, if this is any empty Event (no command attached) virtual bool empty() const = 0; // Associate an accelerator to this event (this can trigger an update of the associated widgets) virtual void connectAccelerator(IAccelerator& accel) = 0; // Signal to remove the accelerators from this event (might update associated widgets) virtual void disconnectAccelerators() = 0; }; typedef std::shared_ptr IEventPtr; // Event visitor class class IEventVisitor { public: // destructor virtual ~IEventVisitor() {} virtual void visit(const std::string& eventName, const IEventPtr& event) = 0; }; const std::string MODULE_EVENTMANAGER("EventManager"); // The function object invoked when a ToggleEvent is changing states // The passed boolean indicates the new toggle state (true = active/toggled) typedef std::function ToggleCallback; class IEventManager : public RegisterableModule { public: /* Create an accelerator using the given arguments and add it to the list * * @key: The symbolic name of the key, e.g. "A", "Esc" * @modifierStr: A string containing the modifiers, e.g. "Shift+Control" or "Shift" * * @returns: the pointer to the newly created accelerator object */ virtual IAccelerator& addAccelerator(const std::string& key, const std::string& modifierStr) = 0; // The same as above, but with event values as argument (event->keyval, event->state) virtual IAccelerator& addAccelerator(wxKeyEvent& ev) = 0; virtual IAccelerator& findAccelerator(const IEventPtr& event) = 0; virtual std::string getAcceleratorStr(const IEventPtr& event, bool forMenu) = 0; // Loads all accelerator bindings from the defaults in the stock input.xml virtual void resetAcceleratorBindings() = 0; // Add a command and specify the statement to execute when triggered virtual IEventPtr addCommand(const std::string& name, const std::string& statement, bool reactOnKeyUp = false) = 0; // Creates a new keyevent that calls the given callback when invoked virtual IEventPtr addKeyEvent(const std::string& name, const ui::KeyStateChangeCallback& keyStateChangeCallback) = 0; // Creates a new toggle event that calls the given callback when toggled virtual IEventPtr addToggle(const std::string& name, const ToggleCallback& onToggled) = 0; virtual IEventPtr addWidgetToggle(const std::string& name) = 0; virtual IEventPtr addRegistryToggle(const std::string& name, const std::string& registryKey) = 0; // Set the according Toggle command (identified by ) to the bool virtual void setToggled(const std::string& name, const bool toggled) = 0; // Returns the pointer to the command specified by the commandName virtual IEventPtr findEvent(const std::string& name) = 0; virtual IEventPtr findEvent(wxKeyEvent& ev) = 0; // Retrieves the event name for the given IEventPtr virtual std::string getEventName(const IEventPtr& event) = 0; // Connects the given accelerator to the given command (identified by the string) virtual void connectAccelerator(IAccelerator& accelerator, const std::string& command) = 0; // Disconnects the given command from any accelerators virtual void disconnectAccelerator(const std::string& command) = 0; // Before destruction, it's advisable to disconnect any events from a toolbar's items virtual void disconnectToolbar(wxToolBar* toolbar) = 0; // Loads the shortcut->command associations from the XMLRegistry virtual void loadAccelerators() = 0; // Enables/Disables the specified command virtual void enableEvent(const std::string& eventName) = 0; virtual void disableEvent(const std::string& eventName) = 0; // Removes the given event and disconnects all accelerators from it virtual void removeEvent(const std::string& eventName) = 0; // Visit each event with the given class virtual void foreachEvent(IEventVisitor& eventVisitor) = 0; /* greebo: Retrieves the string representation of the given event */ virtual std::string getEventStr(wxKeyEvent& ev) = 0; }; typedef std::shared_ptr IEventManagerPtr; // This is the accessor for the event manager inline IEventManager& GlobalEventManager() { // Cache the reference locally static IEventManager& _eventManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_EVENTMANAGER) ) ); return _eventManager; } DarkRadiant-2.5.0/include/ifilechooser.h000066400000000000000000000032251321750546400201620ustar00rootroot00000000000000#pragma once #include #include class wxWindow; namespace ui { /** * The FileChooser class can be used to query a path from the user. * Use the GlobalDialogManager module to acquire a new instance of this class. */ class IFileChooser { public: virtual ~IFileChooser() {} // Lets the dialog start at a certain path virtual void setCurrentPath(const std::string& path) = 0; // Pre-fills the currently selected file virtual void setCurrentFile(const std::string& file) = 0; /** * Returns the selected filename (default extension * will be added if appropriate). */ virtual std::string getSelectedFileName() = 0; /** * greebo: Displays the dialog and enters the main loop. * Returns the filename or "" if the user hit cancel. * * The returned file name is normalised using the os::standardPath() method. */ virtual std::string display() = 0; }; typedef std::shared_ptr IFileChooserPtr; /** * The DirChooser class can be used to query a directory from the user. * Use the GlobalDialogManager module to acquire a new instance of this class. */ class IDirChooser { public: virtual ~IDirChooser() {} // Lets the dialog start at a certain path virtual void setCurrentPath(const std::string& path) = 0; /** * Returns the selected folder name. */ virtual std::string getSelectedFolderName() = 0; /** * greebo: Displays the dialog and enters the main loop. * Returns the filename or "" if the user hit cancel. * * The returned file name is normalised using the os::standardPath() method. */ virtual std::string display() = 0; }; typedef std::shared_ptr IDirChooserPtr; } // namespace ui DarkRadiant-2.5.0/include/ifilesystem.h000066400000000000000000000121601321750546400200420ustar00rootroot00000000000000#pragma once /** * \defgroup vfs Virtual filesystem * \file ifilesystem.h * Interface types for the VFS module. */ #include #include #include #include #include #include #include "imodule.h" class ArchiveFile; typedef std::shared_ptr ArchiveFilePtr; class ArchiveTextFile; typedef std::shared_ptr ArchiveTextFilePtr; class Archive; namespace vfs { // Extension of std::list to check for existing paths before inserting new ones class SearchPaths : public std::list { public: bool insertIfNotExists(const std::string& path) { if (std::find(begin(), end(), path) != end()) { return false; } push_back(path); return true; } }; /** * Main interface for the virtual filesystem. * * The virtual filesystem provides a unified view of the contents of Doom 3's * base and mod subdirectories, including the contents of PK4 files. Assets can * be retrieved using a single unique path, without needing to know whereabouts * in the physical filesystem the asset is located. * * \ingroup vfs */ class VirtualFileSystem : public RegisterableModule { public: virtual ~VirtualFileSystem() {} // Functor taking the filename as argument. The filename is relative // to the base path passed to the GlobalFileSystem().foreach*() method. typedef std::function VisitorFunc; /** * Interface for VFS observers. * * A VFS observer is automatically notified of events relating to the * VFS, including startup and shutdown. */ class Observer { public: virtual ~Observer() {} /** * Notification of VFS initialisation. * * This method is invoked for all VFS observers when the VFS is * initialised. An empty default implementation is provided. */ virtual void onFileSystemInitialise() {} /** * Notification of VFS shutdown. * * This method is invoked for all VFS observers when the VFS is shut * down. An empty default implementation is provided. */ virtual void onFileSystemShutdown() {} }; /// \brief Adds a root search \p path. /// Called before \c initialise. virtual void initDirectory(const std::string& path) = 0; typedef std::set ExtensionSet; // Initialises the filesystem using the given search order. virtual void initialise(const SearchPaths& vfsSearchPaths, const ExtensionSet& allowedArchiveExtensions) = 0; /// \brief Shuts down the filesystem. virtual void shutdown() = 0; // greebo: Adds/removes observers to/from the VFS virtual void addObserver(Observer& observer) = 0; virtual void removeObserver(Observer& observer) = 0; // Returns the number of files in the VFS matching the given filename virtual int getFileCount(const std::string& filename) = 0; /// \brief Returns the file identified by \p filename opened in binary mode, or 0 if not found. // greebo: Note: expects the filename to be normalised (forward slashes, trailing slash). virtual ArchiveFilePtr openFile(const std::string& filename) = 0; /// \brief Returns the file identified by \p filename opened in binary mode, or 0 if not found. // This is a variant of openFile taking an absolute path as argument. virtual ArchiveFilePtr openFileInAbsolutePath(const std::string& filename) = 0; /// \brief Returns the file identified by \p filename opened in text mode, or 0 if not found. virtual ArchiveTextFilePtr openTextFile(const std::string& filename) = 0; /// \brief Returns the file identified by \p filename opened in text mode, or NULL if not found. /// This is a variant of openTextFile taking an absolute path as argument. virtual ArchiveTextFilePtr openTextFileInAbsolutePath(const std::string& filename) = 0; /// \brief Calls the visitor function for each file under \p basedir matching \p extension. /// Use "*" as \p extension to match all file extensions. virtual void forEachFile(const std::string& basedir, const std::string& extension, const VisitorFunc& visitorFunc, std::size_t depth = 1) = 0; // Similar to forEachFile, this routine traverses an absolute path // searching for files matching a certain extension and invoking // the givne visitor functor on each occurrence. virtual void forEachFileInAbsolutePath(const std::string& path, const std::string& extension, const VisitorFunc& visitorFunc, std::size_t depth = 1) = 0; /// \brief Returns the absolute filename for a relative \p name, or "" if not found. virtual std::string findFile(const std::string& name) = 0; /// \brief Returns the filesystem root for an absolute \p name, or "" if not found. /// This can be used to convert an absolute name to a relative name. virtual std::string findRoot(const std::string& name) = 0; // Returns the list of registered VFS paths, ordered by search priority virtual const SearchPaths& getVfsSearchPaths() = 0; }; } const char* const MODULE_VIRTUALFILESYSTEM("VirtualFileSystem"); inline vfs::VirtualFileSystem& GlobalFileSystem() { // Cache the reference locally static vfs::VirtualFileSystem& _vfs( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_VIRTUALFILESYSTEM) ) ); return _vfs; } DarkRadiant-2.5.0/include/ifiletypes.h000066400000000000000000000054031321750546400176640ustar00rootroot00000000000000#pragma once #include #include #include "imodule.h" /** * Simple structure to store a file pattern (e.g. "*.map") * along with its name (e.g. "Map files") and extension. * * If a module has been registering itself for a certain * filetype/extension combo, its name is in associatedModule. */ struct FileTypePattern { // The user-friendly name ("Doom 3 Map") std::string name; // The extension in lowercase ("map") std::string extension; // The mask pattern ("*.map") std::string pattern; // Constructor with optional initialisation parameters FileTypePattern(const std::string& name_ = "", const std::string& extension_ = "", const std::string& pattern_ = "") : name(name_), extension(extension_), pattern(pattern_) {} }; typedef std::list FileTypePatterns; const char* const MODULE_FILETYPES = "FileTypes"; /** * Interface for the FileType registry module. This module retains a list of * FileTypePattern objects along with their associated module names. */ class IFileTypeRegistry : public RegisterableModule { public: /** * greebo: Registers an extension (e.g. "map") for a certain file type ("prefab"). * Common file types are "map", "prefab" and "model", each of them can have one * or more extensions associated in a certain order ("prefab" => "pfb", "map", "reg", * or "map" => "map", "reg", "pfb"). The order is important e.g. for the file * filters in the Map Open dialog. * * The pattern argument is a structure containing the lowercase extensions as well * as the display name and the filter pattern used in the file chooser dialogs. * * If an extension is already associated with the given filetype, it is ignored. * New extensions are added to the end of the list. * * @param fileType: the file type which an extension should be associated to. * @param pattern: the extension as well as the display name and a pattern ("*.map") */ virtual void registerPattern(const std::string& fileType, const FileTypePattern& pattern) = 0; /** * Retrieve a list of patterns for the given file type (e.g. "prefab" or "map"). * * @returns: a list of FileTypePatterns containing extension, display name, etc. */ virtual FileTypePatterns getPatternsForType(const std::string& fileType) = 0; }; namespace filetype { // Some well-known file type constants const char* const TYPE_MAP = "map"; const char* const TYPE_PREFAB = "prefab"; const char* const TYPE_REGION = "region"; const char* const TYPE_MODEL_EXPORT = "modelexport"; } inline IFileTypeRegistry& GlobalFiletypes() { // Cache the reference locally static IFileTypeRegistry& _fileTypes( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_FILETYPES) ) ); return _fileTypes; } DarkRadiant-2.5.0/include/ifilter.h000066400000000000000000000134461321750546400171530ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "inode.h" #include #include #include #include /** * This structure defines a simple filtercriterion as used by the Filtersystem */ class FilterRule { public: enum Type { TYPE_TEXTURE, TYPE_ENTITYCLASS, TYPE_OBJECT, TYPE_ENTITYKEYVALUE, }; // The rule type Type type; // The entity key, only applies for type "entitykeyvalue" std::string entityKey; // the match expression regex std::string match; // true for action="show", false for action="hide" bool show; private: // Private Constructor, use the named constructors below FilterRule(const Type type_, const std::string& match_, bool show_) : type(type_), match(match_), show(show_) {} // Alternative private constructor for the entityKeyValue type FilterRule(const Type type_, const std::string& entityKey_, const std::string& match_, bool show_) : type(type_), entityKey(entityKey_), match(match_), show(show_) {} public: // Named constructors // Regular constructor for the non-entitykeyvalue types static FilterRule Create(const Type type, const std::string& match, bool show) { assert(type != TYPE_ENTITYKEYVALUE); return FilterRule(type, match, show); } // Constructor for the entity key value type static FilterRule CreateEntityKeyValueRule(const std::string& key, const std::string& match, bool show) { return FilterRule(TYPE_ENTITYKEYVALUE, key, match, show); } }; typedef std::vector FilterRules; /** Visitor interface for evaluating the available filters in the * FilterSystem. */ struct IFilterVisitor { virtual ~IFilterVisitor() {} // Visit function virtual void visit(const std::string& filterName) = 0; }; const char* const MODULE_FILTERSYSTEM = "FilterSystem"; // Forward declaration class Entity; /** * \brief * Interface for the FilterSystem * * The filter system provides a mechanism by which certain objects or materials * can be hidden from rendered views. */ class FilterSystem : public RegisterableModule { public: /// Signal emitted when the state of filters has changed virtual sigc::signal filtersChangedSignal() const = 0; /** * greebo: Updates all the "Filtered" status of all Instances * in the scenegraph based on the current filter settings. */ virtual void update() = 0; /** * greebo: Lets the filtersystem update the specified subgraph only, * which includes the given node and all children. */ virtual void updateSubgraph(const scene::INodePtr& root) = 0; /** Visit the available filters, passing each filter's text * name to the visitor. * * @param visitor * Visitor class implementing the IFilterVisitor interface. */ virtual void forEachFilter(IFilterVisitor& visitor) = 0; /** Set the state of the named filter. * * @param filter * The filter to toggle. * * @param state * true if the filter should be active, false otherwise. */ virtual void setFilterState(const std::string& filter, bool state) = 0; /** greebo: Returns the state of the given filter. * * @returns: true or false, depending on the filter state. */ virtual bool getFilterState(const std::string& filter) = 0; /** * Activates or deactivates all known filters. */ virtual void setAllFilterStates(bool state) = 0; /** greebo: Returns the event name of the given filter. This is needed * to create the toggle event to menus/etc. */ virtual std::string getFilterEventName(const std::string& filter) = 0; /** Test if a given item should be visible or not, based on the currently- * active filters. * * @param item * The filter type to query * * @param name * String name of the item to query. * * @returns * true if the item is visible, false otherwise. */ virtual bool isVisible(const FilterRule::Type type, const std::string& name) = 0; /** * Test if a given entity should be visible or not, based on the currently active filters. * * @param type * The filter type to query * * @param entity * The Entity to test * * @returns * true if the entity is visible, false otherwise. */ virtual bool isEntityVisible(const FilterRule::Type type, const Entity& entity) = 0; // ===== API for Filter management and editing ===== /** * greebo: Returns TRUE if the filter is read-only and can't be deleted. */ virtual bool filterIsReadOnly(const std::string& filter) = 0; /** * greebo: Adds a new filter to the system with the given ruleset. The new filter * is not set to read-only. * * @returns: TRUE on success, FALSE if the filter name already exists. */ virtual bool addFilter(const std::string& filterName, const FilterRules& ruleSet) = 0; /** * greebo: Removes the filter, returns TRUE on success. */ virtual bool removeFilter(const std::string& filter) = 0; /** * greebo: Renames the specified filter. This also takes care of renaming the corresponding command in the * EventManager class. * * @returns: TRUE on success, FALSE if the filter hasn't been found or is read-only. */ virtual bool renameFilter(const std::string& oldFilterName, const std::string& newFilterName) = 0; /** * greebo: Returns the ruleset of this filter, order is important. */ virtual FilterRules getRuleSet(const std::string& filter) = 0; /** * greebo: Applies the given criteria set to the named filter, replacing the existing set. * This applies to non-read-only filters only. * * @returns: TRUE on success, FALSE if filter not found or read-only. */ virtual bool setFilterRules(const std::string& filter, const FilterRules& ruleSet) = 0; }; inline FilterSystem& GlobalFilterSystem() { // Cache the reference locally static FilterSystem& _filterSystem( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_FILTERSYSTEM) ) ); return _filterSystem; } DarkRadiant-2.5.0/include/ifiltermenu.h000066400000000000000000000013561321750546400200350ustar00rootroot00000000000000#pragma once #include #include class wxMenu; namespace ui { /** * A class representing a Filters submenu, with a proper * getWidget() method for packing it into a parent container. * * It's the caller's responsibility to delete the object. * * Use the GlobalUIManager() interface to acquire * a new instance of this filter menu. */ class IFilterMenu { public: virtual ~IFilterMenu() {} // Constructs and returns the widget of a full filters menu // including submenu and the items. This can be packed into an // existing menu bar or toolitem right away. // Caller is responsible of deleting the menu! virtual wxMenu* getMenuWidget() = 0; }; typedef std::shared_ptr IFilterMenuPtr; } // namespace ui DarkRadiant-2.5.0/include/ifonts.h000066400000000000000000000070311321750546400170100ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "irenderable.h" #include class Material; typedef std::shared_ptr MaterialPtr; namespace fonts { namespace q3font { // Default values of Quake 3 sourcecode. Don't change! const std::size_t GLYPH_COUNT_PER_FONT = 256; } // namespace // Container-class for Glyphs (== single font characters). class IGlyphInfo { public: int height; // number of scan lines int top; // top of glyph in buffer int bottom; // bottom of glyph in buffer int pitch; // width for copying int xSkip; // x adjustment int imageWidth; // width of actual image int imageHeight; // height of actual image float s; // x offset in image where glyph starts float t; // y offset in image where glyph starts float s2; float t2; std::string texture; // the texture name without extension, e.g. carleton_1_24 // The shader this glyph is associated with // this is NULL until the font is actually used ShaderPtr shader; }; typedef std::shared_ptr IGlyphInfoPtr; // Each D3 font has three resolutions enum Resolution { Resolution12, Resolution24, Resolution48, NumResolutions }; inline std::ostream& operator<< (std::ostream& os, Resolution res) { switch (res) { case Resolution12: os << "12"; break; case Resolution24: os << "24"; break; case Resolution48: os << "48"; break; default: assert(false); os << "Unrecognised"; break; } return os; } // Each font resolution has its own set of glyphs class IGlyphSet { public: virtual ~IGlyphSet() {} // 12, 24, 48 virtual Resolution getResolution() const = 0; // each set has 256 glyphs (q3font::GLYPH_COUNT_PER_FONT) virtual IGlyphInfoPtr getGlyph(std::size_t glyphIndex) const = 0; // Gets the scale for calculating the render font size virtual float getGlyphScale() const = 0; // Gets the maximum width of a glyph in this set virtual std::size_t getMaxGlyphWidth() const = 0; // Gets the maximum height of a glyph in this set virtual std::size_t getMaxGlyphHeight() const = 0; // Ensures that each glyph has a valid Shader virtual void realiseShaders() = 0; }; typedef std::shared_ptr IGlyphSetPtr; /** * Holds information about one specific font. * A font consists of three resolutions. */ class IFontInfo { public: virtual ~IFontInfo() {} // The name of the font, e.g. "carleton" virtual const std::string& getName() const = 0; // The language of this font virtual const std::string& getLanguage() const = 0; // Returns the glyphset for the specified resolution virtual IGlyphSetPtr getGlyphSet(Resolution resolution) = 0; }; typedef std::shared_ptr IFontInfoPtr; /** * greebo: Use the FontManager to load a specific font. The returned FontInfo structure * contains all the necessary info about the glyphs, as found in the font's DAT file. */ class IFontManager : public RegisterableModule { public: // Returns the info structure of a specific font (current language), // returns NULL if no font info is available yet virtual IFontInfoPtr findFontInfo(const std::string& name) = 0; }; } const std::string MODULE_FONTMANAGER("FontManager"); inline fonts::IFontManager& GlobalFontManager() { // Cache the reference locally static fonts::IFontManager& _fontManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_FONTMANAGER) ) ); return _fontManager; } DarkRadiant-2.5.0/include/igame.h000066400000000000000000000057311321750546400165750ustar00rootroot00000000000000#pragma once #include "xmlutil/Node.h" #include "imodule.h" #include // String identifier for the game manager module const char* const MODULE_GAMEMANAGER("GameManager"); const char* const RKEY_GAME_TYPE = "user/game/type"; const char* const RKEY_FS_GAME = "user/game/fs_game"; const char* const RKEY_FS_GAME_BASE = "user/game/fs_game_base"; const char* const RKEY_ENGINE_PATH = "user/paths/enginePath"; const char* const RKEY_MOD_PATH = "user/paths/modPath"; const char* const RKEY_MOD_BASE_PATH = "user/paths/modBasePath"; namespace game { /** * \brief * Interface for an object representing a single game type. */ class IGame { public: /** * \brief * Destructor */ virtual ~IGame() {} /** * \brief * Get a string key value from the game file. * * \param key * Name of the key to retrieve. If this key does not exist, a warning is * emitted and the empty string is returned. */ virtual std::string getKeyValue(const std::string& key) const = 0; /** * \brief * Search an XPath relative to the this game node. * * \param xpath * The relative XPath under the game node, including the initial * forward-slash(es). */ virtual xml::NodeList getLocalXPath(const std::string& path) const = 0; }; typedef std::shared_ptr IGamePtr; /** * \brief * Interface for the game management module. */ class IGameManager : public RegisterableModule { public: // Returns the user's local engine path, on POSIX systems this might // point to the folder in the home directory, e.g. ~/.doom3/ if it exists. // If no engine directory is found in the home directory, the regular // engine path is returned, e.g. /usr/local/doom3 or c:\games\doom3 virtual std::string getUserEnginePath() = 0; /** * greebo: Gets the mod path (e.g. ~/.doom3/gathers/). * Returns the mod base path if the mod path itself is empty. */ virtual const std::string& getModPath() const = 0; /** * greebo: Returns the mod base path (e.g. ~/.doom3/darkmod/), * can be an empty string if fs_game_base is not set. */ virtual const std::string& getModBasePath() const = 0; /** greebo: Returns the current Game (shared_ptr). */ virtual IGamePtr currentGame() = 0; // A sorted list of engine paths (queried by the VFS) typedef std::list PathList; // Returns the list of ordered engine paths, which should // be initialised by the Virtual Filesystem (in this exact order) virtual const PathList& getVFSSearchPaths() const = 0; typedef std::vector GameList; virtual const GameList& getSortedGameList() = 0; }; typedef std::shared_ptr IGameManagerPtr; } // namespace game // This is the accessor for the game manager inline game::IGameManager& GlobalGameManager() { // Cache the reference locally static game::IGameManager& _gameManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_GAMEMANAGER) ) ); return _gameManager; } DarkRadiant-2.5.0/include/igl.h000066400000000000000000000034461321750546400162670ustar00rootroot00000000000000#pragma once #include #include #include "imodule.h" const std::string MODULE_OPENGL("OpenGL"); namespace wxutil { class GLWidget; } class wxGLContext; class OpenGLBinding : public RegisterableModule { public: virtual ~OpenGLBinding() {} /// \brief Asserts that there no OpenGL errors have occurred since the last call to glGetError. virtual void assertNoErrors() = 0; /// Returns the shared context widget holding the GL context virtual wxGLContext& getwxGLContext() = 0; /// Registers a GL widget, storing the shared context if necessary virtual void registerGLCanvas(wxutil::GLWidget* widget) = 0; /// Notifies the GL module that a GLWidget has been destroyed virtual void unregisterGLCanvas(wxutil::GLWidget* widget) = 0; /// \brief Is true if the global shared OpenGL context is valid. virtual bool wxContextValid() const = 0; // Returns true if openGL supports ARB or GLSL lighting virtual bool shaderProgramsAvailable() const = 0; // Sets the flag whether shader programs are available. // This is set by the RenderSystem once the extensions are initialised virtual void setShaderProgramsAvailable(bool available) = 0; virtual int getFontHeight() = 0; /// \brief Renders \p string at the current raster-position of the current context. virtual void drawString(const std::string& string) const = 0; /// \brief Renders \p character at the current raster-position of the current context. virtual void drawChar(char character) const = 0; }; inline OpenGLBinding& GlobalOpenGL() { // Cache the reference locally static OpenGLBinding& _openGL( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_OPENGL) ) ); return _openGL; } DarkRadiant-2.5.0/include/iglprogram.h000066400000000000000000000041561321750546400176560ustar00rootroot00000000000000#pragma once #include "math/Vector3.h" #include "math/Matrix4.h" #include "render/Colour4.h" /** * Representation of a GL vertex/fragment program. */ class GLProgram { public: /** * Destructor */ virtual ~GLProgram() {} /** * Create this program using glGenProgramsARB and siblings. This needs the * OpenGL system to be initialised so cannot happen in the constructor. */ virtual void create() = 0; /** * Destroy this program using GL calls. */ virtual void destroy() = 0; /** * Bind this program as the currently-operative shader. */ virtual void enable() = 0; /** * Unbind this program from OpenGL. */ virtual void disable() = 0; /// Data structure containing rendering parameters struct Params { /// Light origin in world space Vector3 lightOrigin; /// Light colour Colour4 lightColour; /// Transformation from world space into light space Matrix4 world2Light; /// Amount of directionality, 0.0 for normal light, 1.0 for ambient float ambientFactor; /// Whether vertex colours should be inverted bool invertVertexColour; Params(const Vector3& lightOrigin_, const Colour4& lightColour_, const Matrix4& world2Light_) : lightOrigin(lightOrigin_), lightColour(lightColour_), world2Light(world2Light_), ambientFactor(0.0), invertVertexColour(false) { } }; /** * \brief * Apply render parameters used by this program to OpenGL. * * This method is invoked shortly before the renderable geometry is * submitted for rendering; the GLProgram must apply to the GL state any * parameters it uses. * * \param viewer * Location of the viewer in object space. * * \param localToWorld * Local to world transformation matrix. * * \param lightParms * Params structure containing lighting information. * */ virtual void applyRenderParams(const Vector3& viewer, const Matrix4& localToWorld, const Params& lightParms) { } }; DarkRadiant-2.5.0/include/iglrender.h000066400000000000000000000134351321750546400174660ustar00rootroot00000000000000#pragma once #include "igl.h" #include "imodule.h" #include "ishaders.h" #include "ShaderLayer.h" #include "math/Vector4.h" #include // Full declaration in iglprogram.h class GLProgram; /** * \brief * Data structure encapsulating various parameters of the OpenGL state machine, * as well as parameters used internally by Radiant. * * The OpenGLState class is used to keep track of OpenGL state parameters used * by the renderer, in order to avoid using slow glGet() calls or repeatedly * changing states to the same value. Each shader pass keeps an OpenGLState * member which stores the state values it wishes to use, and these values are * selectively applied to a single "current" OpenGLState object maintained by * the render system. */ class OpenGLState { public: /// Enum of possible sort positions enum SortPosition { SORT_FIRST = -64, SORT_ZFILL = 0, // used by depth buffer fill passes SORT_INTERACTION = 2, // used by the DBS pass SORT_FULLBRIGHT = 1025, // used by non-translucent editor passes SORT_TRANSLUCENT = 1026, // used by blend-type editor passes SORT_HIGHLIGHT = 1027, // used by the (red) selection system overlay SORT_OVERLAY_FIRST = 1028, // used by decals SORT_OVERLAY_LAST = 2047, SORT_POINT_FIRST = 2048, // used by POINT renderers SORT_POINT_LAST = 3071, SORT_GUI0 = 3072, // used by selection system controls, pivots (visible) SORT_GUI1 = 3073, // used by selection system controls, pivots (obscured) SORT_LAST = 4096, }; private: // The 4 colour components, only for use in OpenGLStates that do not have // any shader stages attached, otherwise pull the colour from there. Colour4 _colour; // Colour inversion flag bool _invertColour; // Set of RENDER_XXX flags unsigned _renderFlags; // GL depth function GLenum _glDepthFunc; // Sort position SortPosition _sortPos; std::string _name; public: const std::string& getName() const { return _name; } void setName(const std::string& name) { _name = name; } /// Return the glColor for this state const Colour4& getColour() const { assert(_colour.isValid()); return _colour; } /// Set the glColor for this state, from a Vector4 void setColour(const Colour4& col) { assert(col.isValid()); _colour = col; } /// Set the glColor for this state, from individual components void setColour(float r, float g, float b, float a) { setColour(Colour4(r, g, b, a)); } /// Set this state to invert colour values void setColourInverted(bool inverted) { _invertColour = inverted; } /// Test whether this state is inverting colour values bool isColourInverted() const { return _invertColour; } /// \name Render flag operations ///@{ /// Set all render flags for this state void setRenderFlags(unsigned newFlags) { _renderFlags = newFlags; } /// Set a single render flag on this state void setRenderFlag(unsigned flag) { setRenderFlags(_renderFlags | flag); } /// Clear a single render flag on this state void clearRenderFlag(unsigned flag) { setRenderFlags(_renderFlags & ~flag); } /// Test the value of a single render flag bool testRenderFlag(unsigned flag) const { return (_renderFlags & flag) > 0; } /// Return the render flags for this state unsigned getRenderFlags() const { return _renderFlags; } ///@} /// Return the depth function GLenum getDepthFunc() const { return _glDepthFunc; } /// Set the depth function void setDepthFunc(GLenum func) { _glDepthFunc = func; } /// Return the sort position SortPosition getSortPosition() const { return _sortPos; } /// Set the sort position void setSortPosition(SortPosition pos) { _sortPos = pos; } /** * \brief * Polygon offset. */ float polygonOffset; /** * \brief * GL texture numbers to be bound to texture units. * * \{ */ GLint texture0; GLint texture1; GLint texture2; GLint texture3; GLint texture4; /** * \} */ /** * Each open GL State refers to one or more shader stages, * which hold the actual values of many parameters, some of them * time-dependent or depending on entity parameters. */ ShaderLayerPtr stage0; ShaderLayerPtr stage1; ShaderLayerPtr stage2; ShaderLayerPtr stage3; ShaderLayerPtr stage4; /** * \brief * Source blend mode. */ GLenum m_blend_src; /** * \brief * Destination blend mode */ GLenum m_blend_dst; // Alpha test function GLenum alphaFunc; // Alpha test threshold GLfloat alphaThreshold; GLfloat m_linewidth; GLfloat m_pointsize; GLint m_linestipple_factor; GLushort m_linestipple_pattern; /** * \brief * GL program or shader object. */ GLProgram* glProgram; /** * \brief * The cube-map texgen mode for rendering. */ ShaderLayer::CubeMapMode cubeMapMode; /// Default constructor OpenGLState() : _colour(Colour4::WHITE()), _invertColour(false), _renderFlags(0), _glDepthFunc(GL_LESS), _sortPos(SORT_FIRST), polygonOffset(0.0f), texture0(0), texture1(0), texture2(0), texture3(0), texture4(0), m_blend_src(GL_SRC_ALPHA), m_blend_dst(GL_ONE_MINUS_SRC_ALPHA), alphaFunc(GL_ALWAYS), alphaThreshold(0), m_linewidth(1), m_pointsize(1), m_linestipple_factor(1), m_linestipple_pattern(0xAAAA), glProgram(NULL), cubeMapMode(ShaderLayer::CUBE_MAP_NONE) { } }; DarkRadiant-2.5.0/include/igrid.h000066400000000000000000000025051321750546400166050ustar00rootroot00000000000000#pragma once /* greebo: The interface of the grid system * * Use these methods to set/get the grid size of the xyviews */ #include "imodule.h" #include enum GridSize { GRID_0125 = -3, GRID_025 = -2, GRID_05 = -1, GRID_1 = 0, GRID_2 = 1, GRID_4 = 2, GRID_8 = 3, GRID_16 = 4, GRID_32 = 5, GRID_64 = 6, GRID_128 = 7, GRID_256 = 8, }; // grid renderings enum GridLook { GRIDLOOK_LINES, GRIDLOOK_DOTLINES, GRIDLOOK_MOREDOTLINES, GRIDLOOK_CROSSES, GRIDLOOK_DOTS, GRIDLOOK_BIGDOTS, GRIDLOOK_SQUARES, }; const char* const MODULE_GRID("Grid"); class IGridManager : public RegisterableModule { public: virtual ~IGridManager() {} virtual void setGridSize(GridSize gridSize) = 0; virtual float getGridSize() const = 0; virtual int getGridPower() const = 0; virtual void gridDown() = 0; virtual void gridUp() = 0; virtual GridLook getMajorLook() const = 0; virtual GridLook getMinorLook() const = 0; /// Signal emitted when the grid is changed virtual sigc::signal signal_gridChanged() const = 0; }; // class IGridManager // This is the accessor for the grid module inline IGridManager& GlobalGrid() { // Cache the reference locally static IGridManager& _grid( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_GRID) ) ); return _grid; } DarkRadiant-2.5.0/include/igroupdialog.h000066400000000000000000000066061321750546400202020ustar00rootroot00000000000000#pragma once #include #include class wxWindow; class wxFrame; /** * greebo: This defines the interface for accessing the GroupDialog * (i.e. the window housing the Entity Inspector, the Media Browser et al). * * Use the addPage() method to add a new tab to the GroupDialog. */ class IGroupDialog { public: /** * Destructor */ virtual ~IGroupDialog() {} struct Page { // The name of this window (unique, can be used to show the page) std::string name; // The label string to be displayed on the tab std::string tabLabel; // The image to be displayed in the tab std::string tabIcon; // the actual widget to be added wxWindow* page; // the title string for the groupdialog window // to be displayed when this tab is active std::string windowLabel; // Define the order of the "native" group dialog pages // Use this enum values to indicate which tab position // you need your page to have sorted at struct Position { enum PredefinedValues { EntityInspector = 100, MediaBrowser = 200, Console = 300, TextureBrowser = 400, End = 5000 }; }; // Defines the position page in the group dialog (defaults to "End") // See the predefined Position enum for already existing positions int position = Position::End; }; typedef std::shared_ptr PagePtr; /** * Adds a page to the group dialog. * @returns: the notebook page widget */ virtual wxWindow* addPage(const PagePtr& page) = 0; /** * Removes the named page from the TextureBrowser. If the page doesn't exist, * nothing happens. */ virtual void removePage(const std::string& name) = 0; /** greebo: Sets the active tab to the given widget. * * @page: The widget that should be displayed, must have been added * using addPage() beforehand. */ virtual void setPage(wxWindow* page) = 0; /** greebo: Activated the named page. The parameter * refers to the name string passed to the addPage() method. * This also shows the GroupDialog, should it be hidden before the call. */ virtual void setPage(const std::string& name) = 0; /** greebo: Toggle the named page. The parameter * refers to the name string passed to the addPage() method. * * The behaviour is as follows: Calling this command opens the * GroupDialog (even if it is hidden) and switches to the given page. * * If the GroupDialog is already visible and focusing the requested page, * the dialog is hidden. */ virtual void togglePage(const std::string& name) = 0; /** greebo: Returns the widget of the currently visible page. */ virtual wxWindow* getPage() = 0; /** * greebo: Returns the name of the current groupdialog page or "" if none is set. */ virtual std::string getPageName() = 0; // Returns the window widget containing the GroupDialog. virtual wxFrame* getDialogWindow() = 0; // Shows the dialog virtual void showDialogWindow() = 0; // Hides the dialog virtual void hideDialogWindow() = 0; /** * Special function for mainframe layouts. This method allows to detach the * contained notebook from the group dialog to pack it somewhere else. * Layout code shouldn't forget to reparent it to the groupdialog again * on deactivation. */ virtual void reparentNotebook(wxWindow* newParent) = 0; /** * Reparents the groupdialog notebook back to the GroupDialog itself. */ virtual void reparentNotebookToSelf() = 0; }; DarkRadiant-2.5.0/include/igroupnode.h000066400000000000000000000015321321750546400176610ustar00rootroot00000000000000#pragma once #include "inode.h" namespace scene { /** greebo: This is used to identify group entities right before * and after map save/load. * * It provides methods to add/substract the origin to/from * their child primitives. */ class GroupNode { public: virtual ~GroupNode() {} /** greebo: This is called right before saving * to move the child brushes of the Doom3Group * according to its origin. */ virtual void addOriginToChildren() = 0; virtual void removeOriginFromChildren() = 0; }; typedef std::shared_ptr GroupNodePtr; } // namespace scene /** greebo: Cast a node onto a GroupNode pointer * * @returns: an empty pointer on failure, the pointer to the class otherwise. */ inline scene::GroupNodePtr Node_getGroupNode(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } DarkRadiant-2.5.0/include/igui.h000066400000000000000000000243161321750546400164500ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "imodule.h" #include "math/Vector4.h" #include "string/convert.h" #include "ishaders.h" namespace gui { class IGui; class IRenderableText { public: virtual ~IRenderableText() {} // Submits the openGL calls virtual void render() = 0; // Re-construct this structure, called when the text in the owning windowDef has been changed virtual void recompile() = 0; }; // An expression as encountered in the GUI code // Evaluates to an instance of a certain type (bool, string, etc.) template class IGuiExpression { public: // Shared ptr typedef typedef std::shared_ptr> Ptr; virtual ~IGuiExpression() {} // Evaluate this expression to retrieve the result virtual ValueType evaluate() = 0; // Value changed signal virtual sigc::signal& signal_valueChanged() = 0; }; // An expression representing a constant value template class ConstantExpression : public IGuiExpression { private: ValueType _value; sigc::signal _sigValueChanged; public: ConstantExpression(const ValueType& value) : _value(value) {} virtual ValueType evaluate() override { return _value; } // Provide a value changed signal, though it's never invoked sigc::signal& signal_valueChanged() override { return _sigValueChanged; } template static std::shared_ptr> Create(const OtherType& value) { return std::make_shared>(value); } }; // Represents a variable or property of a GuiWindowDef // e.g. "text", "notime", "forecolor" or a user-defined variable class IWindowVariable { private: sigc::signal _changedSignal; public: virtual ~IWindowVariable() {} // value-changed signal, to get notified when this value changes sigc::signal& signal_variableChanged() { return _changedSignal; } // To be implemented by subclasses virtual void setValueFromString(const std::string& stringVal) = 0; }; // Represents a GUI property carrying a certain type // e.g. "text" (std::string), "rect" (Vector4), "textscale" (float), ... template class WindowVariable : public IWindowVariable { protected: // Types used by this window variable typedef IGuiExpression ExpressionType; typedef std::shared_ptr ExpressionTypePtr; // The expression which can be evaluated ExpressionTypePtr _expression; sigc::connection _exprChangedSignal; public: typedef std::shared_ptr> Ptr; // smart ptr typedef operator ValueType() const { return getValue(); } virtual ValueType getValue() const { return _expression ? _expression->evaluate() : ValueType(); } // Assigns a new Expression to this variable. The expression needs // to match the ValueType of this variable virtual void setValue(const ExpressionTypePtr& newExpr) { if (_expression == newExpr) return; // Disconnect from any previously subscribed signals _exprChangedSignal.disconnect(); _expression = newExpr; signal_variableChanged().emit(); // Subscribe to this new expression's changed signal if (_expression) { _expression->signal_valueChanged().connect([this]() { signal_variableChanged().emit(); }); } } // Assigns a constant value to this variable virtual void setValue(const ValueType& constantValue) { // Disconnect from any previously subscribed signals _exprChangedSignal.disconnect(); _expression = ConstantExpression::Create(constantValue); signal_variableChanged().emit(); // Since this is a constant expression, we don't need to subscribe to any signal } // Implement the required string->ValueType conversion by means of string::convert<> virtual void setValueFromString(const std::string& stringVal) override { ValueType converted = string::convert(stringVal); setValue(converted); } }; class IGuiWindowDef; typedef std::shared_ptr IGuiWindowDefPtr; class IGuiWindowDef { public: // Public properties // The name of this windowDef std::string name; // Window size (x,y,width,height) WindowVariable rect; // Visible or hidden WindowVariable visible; // The body text of this window WindowVariable text; // Whether this gui is full screen (use on desktop window) WindowVariable menugui; WindowVariable forecolor; WindowVariable hovercolor; WindowVariable backcolor; WindowVariable bordercolor; WindowVariable bordersize; WindowVariable matcolor; WindowVariable rotate; // background shader name WindowVariable background; // background shader (NULL until realised) MaterialPtr backgroundShader; // The name of the font WindowVariable font; // The scale for rendering the font WindowVariable textscale; // The text alignment (left, right, center) WindowVariable textalign; // Text offsets WindowVariable textalignx; WindowVariable textaligny; // Force a specific aspect ratio WindowVariable forceaspectwidth; WindowVariable forceaspectheight; // No mouse events for this window WindowVariable noevents; // Whether this window forces text to wrap at their borders WindowVariable noclip; // Whether time is running for this windowDef WindowVariable notime; // Don't display the cursor WindowVariable nocursor; // Don't wrap words at rectangle borders WindowVariable nowrap; // The window time (0..infinity) std::size_t time; // Each window can define its own set of named variables, // which can be of type float or Vector4 typedef std::shared_ptr IWindowVariablePtr; typedef std::map NamedVariables; NamedVariables variables; // All child windowDefs of this window typedef std::vector ChildWindows; ChildWindows children; public: virtual ~IGuiWindowDef() {} // Returns the owning GUI virtual IGui& getGui() const = 0; virtual void addWindow(const IGuiWindowDefPtr& window) = 0; // Recursively looks for a named child windowDef // Returns NULL if not found virtual IGuiWindowDefPtr findWindowDef(const std::string& name) = 0; // Get the renderable text object containing the OpenGLRenderables virtual IRenderableText& getRenderableText() = 0; /** * greebo: This is some sort of "think" method, giving this windowDef * a chance to handle timed events. * * @updateChildren: recursively updates child windowDef if true */ virtual void update(const std::size_t timeStep, bool updateChildren = true) = 0; // Initialises the time of this windowDef and all children virtual void initTime(const std::size_t time, bool updateChildren = true) = 0; // Prepares renderable objects, to be called by the parent Gui only virtual void pepareRendering(bool prepareChildren = true) = 0; // Returns a reference to the named variable, or throws a std::invalid_argument exception virtual IWindowVariable& findVariableByName(const std::string& name) = 0; }; /** * greebo: This class represents a single D3 GUI. It holds all * the windowDefs and the source code behind. */ class IGui { public: virtual ~IGui() {} virtual const IGuiWindowDefPtr& getDesktop() const = 0; virtual void setDesktop(const IGuiWindowDefPtr& newDesktop) = 0; // Sets the given state variable (gui:: = ) virtual void setStateString(const std::string& key, const std::string& value) = 0; // Retrieve a changed signal for the given key, which is invoked // whenever setStateString() is called virtual sigc::signal& getChangedSignalForState(const std::string& key) = 0; // Returns the state string "gui::" or an empty string if non-existent virtual std::string getStateString(const std::string& key) = 0; // Sets up the time of the entire GUI (all windowDefs) virtual void initTime(const std::size_t time) = 0; // "Think" routine, advances all active windowDefs (where notime == false) virtual void update(const std::size_t timestep) = 0; // Returns a reference to the named windowDef, returns NULL if not found virtual IGuiWindowDefPtr findWindowDef(const std::string& name) = 0; // Called by the GuiRenderer to re-compile text VBOs, etc. virtual void pepareRendering() = 0; }; typedef std::shared_ptr IGuiPtr; enum GuiType { NOT_LOADED_YET, // no attempt to load the GUI has been made UNDETERMINED, // not checked yet for type ONE_SIDED_READABLE, // 1-sided TWO_SIDED_READABLE, // 2-sided NO_READABLE, // not a readable IMPORT_FAILURE, // failed to load FILE_NOT_FOUND, // file doesn't exist }; /** * greebo: Interface managing the idTech4 GUI files, * keeping track of all the loaded GUIs, * parsing the .gui files on demand. */ class IGuiManager : public RegisterableModule { public: typedef std::vector StringList; // A visitor class used to traverse all known GUIs by path class Visitor { public: virtual ~Visitor() {} virtual void visit(const std::string& guiPath, const GuiType& guiType) = 0; }; public: virtual ~IGuiManager() {} // Gets a GUI from the given VFS path, parsing it on demand // Returns NULL if the GUI couldn't be found or loaded. virtual IGuiPtr getGui(const std::string& guiPath) = 0; // Returns the number of known GUIs (or GUI paths) virtual std::size_t getNumGuis() = 0; // Traverse all known GUIs using the given Visitor virtual void foreachGui(Visitor& visitor) = 0; // Returns the GUI appearance type for the given GUI path virtual GuiType getGuiType(const std::string& guiPath) = 0; // Reload the gui virtual void reloadGui(const std::string& guiPath) = 0; // Returns the _errorList for use in a GUI. virtual const StringList& getErrorList() = 0; // Clears out the GUIs and reloads them virtual void reloadGuis() = 0; }; } const char* const MODULE_GUIMANAGER("GuiManager"); // Application-wide Accessor to the global GUI manager inline gui::IGuiManager& GlobalGuiManager() { // Cache the reference locally static gui::IGuiManager& _manager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_GUIMANAGER)) ); return _manager; } DarkRadiant-2.5.0/include/iimage.h000066400000000000000000000064221321750546400167440ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(INCLUDED_IIMAGE_H) #define INCLUDED_IIMAGE_H #include "igl.h" #include "imodule.h" typedef unsigned char byte; class Texture; typedef std::shared_ptr TexturePtr; /** * \brief * Interface for GL bindable texture objects. */ class BindableTexture { public: virtual ~BindableTexture() {} /** * \brief * Bind this texture to OpenGL. * * This method invokes the necessary GL calls to bind and upload the * object's texture. It returns a Texture object representing the texture in * GL. * * \param name * Optional name to give the texture at bind time. This would usually be the * name of the image map, e.g. "textures/blah/bleh". */ virtual TexturePtr bindTexture(const std::string& name = "") const = 0; }; typedef std::shared_ptr BindableTexturePtr; class Image : public BindableTexture { public: /** * greebo: Returns the specified mipmap pixel data. */ virtual byte* getMipMapPixels(std::size_t mipMapIndex) const = 0; /** * greebo: Returns the dimension of the specified mipmap. */ virtual std::size_t getWidth(std::size_t mipMapIndex) const = 0; virtual std::size_t getHeight(std::size_t mipMapIndex) const = 0; // greebo: Returns TRUE whether this image is precompressed (DDS) virtual bool isPrecompressed() const { return false; } }; typedef std::shared_ptr ImagePtr; class ArchiveFile; /// Module responsible for loading images from VFS or disk filesystem class ImageLoader : public RegisterableModule { public: /** * \brief * Load an image from a VFS path. * * Images loaded from the VFS are not specified with an extension. Various * extensions (and optional VFS directory prefixes) will be attempted in an * order specified by the .game file. For example, requesting * "textures/blah/bleh" might result in an attempt to load * "textures/blah/bleh.tga" followed by "dds/textures/blah/bleh.dds" if the * tga version cannot be found. */ virtual ImagePtr imageFromVFS(const std::string& vfsPath) const = 0; /** * \brief * Load an image from a filesystem path. */ virtual ImagePtr imageFromFile(const std::string& filename) const = 0; }; typedef std::shared_ptr ImageLoaderPtr; const std::string MODULE_IMAGELOADER("ImageLoader"); inline const ImageLoader& GlobalImageLoader() { return *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_IMAGELOADER) ); } #endif // _IIMAGE_H DarkRadiant-2.5.0/include/iinteractiveview.h000066400000000000000000000016521321750546400210720ustar00rootroot00000000000000#pragma once #include "iselectiontest.h" #include "ivolumetest.h" #include "math/Vector2.h" /** * Base class of the two interactive views used in DarkRadiant: * OrthoView (XYView) and the Camera (3D View). */ class IInteractiveView { public: virtual ~IInteractiveView() {} /** * Creates a new SelectionTest instance for the given position, measured in * device coordinates in the range [-1..+1] */ virtual SelectionTestPtr createSelectionTestForPoint(const Vector2& point) = 0; // Returns the device dimensions in pixels virtual int getDeviceWidth() const = 0; virtual int getDeviceHeight() const = 0; /** * Returns the VolumeTest instance associated with this view. */ virtual const VolumeTest& getVolumeTest() const = 0; // Schedules a repaint of this view virtual void queueDraw() = 0; // Paints the view immediately virtual void forceRedraw() = 0; }; DarkRadiant-2.5.0/include/ilayer.h000066400000000000000000000143421321750546400167760ustar00rootroot00000000000000#pragma once #include #include #include #include "imodule.h" #include namespace scene { class INode; typedef std::shared_ptr INodePtr; // A list of named layers typedef std::set LayerList; /** * greebo: Interface of a Layered object. */ class Layered { public: // Destructor virtual ~Layered() {} /** * Add this object to the layer with the given ID. */ virtual void addToLayer(int layerId) = 0; /** * Moves this object to the layer. After this operation,the item is member * of this layer only, all other layer memberships are erased. */ virtual void moveToLayer(int layerId) = 0; /** * Remove this object from the layer with the given ID. */ virtual void removeFromLayer(int layerId) = 0; /** * Return the set of layers to which this object is assigned. */ virtual LayerList getLayers() const = 0; /** * greebo: This assigns the given node to the given set of layers. Any previous * assignments of the node will be overwritten by this routine. * Note: The newLayers list must not be empty, otherwise the call will be ignored. */ virtual void assignToLayers(const LayerList& newLayers) = 0; }; class ILayerSystem : public RegisterableModule { public: /** * greebo: Creates a new layer with the given name. * * @returns: the ID of the layer of -1 if the layer could not * be created (e.g. due to a name conflict). */ virtual int createLayer(const std::string& name) = 0; /** * greebo: Overload: Creates a new layer with the given name and the given ID. * * @returns: the ID of the layer of -1 if the layer could not * be created (e.g. due to a name/ID conflict). */ virtual int createLayer(const std::string& name, int layerID) = 0; /** * greebo: Deletes the named layer. All nodes are removed * from this layer before deletion. */ virtual void deleteLayer(const std::string& name) = 0; /** * greebo: Resets the layer system into its ground state. Deletes all * layers except for layer #0 which is renamed to "Default". */ virtual void reset() = 0; typedef std::function LayerVisitFunc; /** * Functor is called using id and name as arguments */ virtual void foreachLayer(const LayerVisitFunc& visitor) = 0; /** * greebo: Returns the ID of the named layer, or -1 if name doesn't exist */ virtual int getLayerID(const std::string& name) const = 0; /** * greebo: Returns the name of the layer with the given ID or "" if it doesn't exist */ virtual std::string getLayerName(int layerID) const = 0; /** * Returns TRUE if the given layer exists, FALSE otherwise. */ virtual bool layerExists(int layerID) const = 0; /** * greebo: Renames the given layer. Returns TRUE on success, FALSE if the name is * already in use. */ virtual bool renameLayer(int layerID, const std::string& newLayerName) = 0; /** * greebo: Returns the ID of the first visible layer or 0 (default layer) if none is visible. */ virtual int getFirstVisibleLayer() const = 0; /** * Returns the ID of the layer that is currently signed as active (will return the default layer * if no other layer is active). Note that the returned layer is not necessarily visible. */ virtual int getActiveLayer() const = 0; /** * Declare the given layer as active. Nothing will happen if the given layer ID is not existent. * This method doesn't change the visibility of the given layer. */ virtual void setActiveLayer(int layerID) = 0; /** * greebo: Queries the visibility of the given layer. */ virtual bool layerIsVisible(const std::string& layerName) = 0; virtual bool layerIsVisible(int layerID) = 0; /** * greebo: Sets the visibility of the given layer. */ virtual void setLayerVisibility(const std::string& layerName, bool visible) = 0; virtual void setLayerVisibility(int layerID, bool visible) = 0; /** * greebo: Traverses the selection and adds each node to the given layer. */ virtual void addSelectionToLayer(const std::string& layerName) = 0; virtual void addSelectionToLayer(int layerID) = 0; /** * greebo: Moves all selected nodes to the given layer. This implicitly * removes the nodes from all other layers. */ virtual void moveSelectionToLayer(const std::string& layerName) = 0; virtual void moveSelectionToLayer(int layerID) = 0; /** * greebo: Removes the selected nodes from the given layers. */ virtual void removeSelectionFromLayer(const std::string& layerName) = 0; virtual void removeSelectionFromLayer(int layerID) = 0; /** * greebo: Updates the visibility of the given node based on the * current layer visibility settings. * * @returns: TRUE if the node was set to "visible", FALSE if the * current layer settings resulted to "invisible" and the * node was therefore hidden. */ virtual bool updateNodeVisibility(const scene::INodePtr& node) = 0; /** * greebo: Sets the selection status of the entire layer. * * @layerID: the ID of the layer to be selected/deselected. * @selected: TRUE if the layer should be selected, FALSE if the members * should be de-selected. */ virtual void setSelected(int layerID, bool selected) = 0; /** * A signal for client code to get notified about layer creation, * renamings and removal. */ virtual sigc::signal signal_layersChanged() = 0; /** * Fired whenever visibility of a layer has been changed. */ virtual sigc::signal signal_layerVisibilityChanged() = 0; /** * Public signal to get notified about layer membership changes, * e.g. when a node has been added to a layer, or moved to a new one. * Since scene::INodes can be added or removed from layers directly, * without the LayerSystem knowing about this, it is sometimes the * responsibility of that algorithm code to emit this signal itself. */ virtual sigc::signal signal_nodeMembershipChanged() = 0; }; } // namespace scene const std::string MODULE_LAYERSYSTEM("LayerSystem"); inline scene::ILayerSystem& GlobalLayerSystem() { // Cache the reference locally static scene::ILayerSystem& _layerSystem( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_LAYERSYSTEM) ) ); return _layerSystem; } DarkRadiant-2.5.0/include/ilightnode.h000066400000000000000000000011371321750546400176350ustar00rootroot00000000000000#pragma once #include #include "inode.h" #include "math/AABB.h" /** * LightNodes derive from this class. * It's mainly used to determine the selectable part * of the light, which is usually the small "diamond" in * the center. */ class ILightNode { public: virtual ~ILightNode() {} /** * greebo: Get the AABB of the Light "Diamond" representation. */ virtual AABB getSelectAABB() = 0; }; typedef std::shared_ptr ILightNodePtr; inline ILightNodePtr Node_getLightNode(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } DarkRadiant-2.5.0/include/imainframe.h000066400000000000000000000067161321750546400176270ustar00rootroot00000000000000#pragma once #include "imodule.h" const std::string MODULE_MAINFRAME("MainFrame"); class wxFrame; class wxToolBar; class wxBoxSizer; /** * Scoped object to block screen updates and display a modal message, * used while reloading shaders, for instance. */ class IScopedScreenUpdateBlocker { public: virtual ~IScopedScreenUpdateBlocker() {} // For operations without calculatable duration, call pulse() regularly to // provide some visual feedback virtual void pulse() = 0; // Update the progress fraction [0..1] virtual void setProgress(float progress) = 0; // Set the status message that might be displayed to the user virtual void setMessage(const std::string& message) = 0; }; typedef std::shared_ptr IScopedScreenUpdateBlockerPtr; /** * The MainFrame represents the top-level application window. */ class IMainFrame : public RegisterableModule { public: // Constructs the toplevel mainframe window and issues the "radiant startup" signal virtual void construct() = 0; // Returns TRUE if screen updates are enabled virtual bool screenUpdatesEnabled() = 0; // Use this to (re-)enable camera and xyview draw updates virtual void enableScreenUpdates() = 0; // Use this to disable camera and xyview draw updates until enableScreenUpdates is called. virtual void disableScreenUpdates() = 0; /** * Returns the main application window widget. Returns NULL if no window * has been constructed yet. */ virtual wxFrame* getWxTopLevelWindow() = 0; /** * Returns TRUE if DarkRadiant is currently "in focus", i.e. the app in the foreground. */ virtual bool isActiveApp() = 0; /** * greebo: Returns the main container widget (a box sizer), where layouts * can start packing widgets into. This resembles the large grey area * in the main window. * May return NULL if mainframe is not constructed yet. */ virtual wxBoxSizer* getWxMainContainer() = 0; enum Toolbar { TOOLBAR_HORIZONTAL, // the "view" toolbar (on the top) TOOLBAR_VERTICAL, // the "edit" toolbar (on the left) }; /** * greebo: Returns a toolbar widget, as specified by the * passed enum value. */ virtual wxToolBar* getToolbar(Toolbar type) = 0; /** * Updates all viewports which are child of the toplevel window. * Set the force flag to true to redraw immediately insteaf of queueing. */ virtual void updateAllWindows(bool force = false) = 0; /** * Applies the named layout to the MainFrame window. See MainFrameLayout class. */ virtual void applyLayout(const std::string& name) = 0; /// Store the layout name, but do not immediately apply it virtual void setActiveLayoutName(const std::string& name) = 0; /** * Returns the name of the currently activated layout or * an empty string if no layout is applied. */ virtual std::string getCurrentLayout() = 0; /** * Acquire a screen update blocker object that displays a modal message. * As soon as the object is destroyed screen updates are allowed again. * * Pass the title and the message to display in the small modal window. */ virtual IScopedScreenUpdateBlockerPtr getScopedScreenUpdateBlocker(const std::string& title, const std::string& message, bool forceDisplay = false) = 0; }; // This is the accessor for the mainframe module inline IMainFrame& GlobalMainFrame() { // Cache the reference locally static IMainFrame& _mainFrame( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MAINFRAME) ) ); return _mainFrame; } DarkRadiant-2.5.0/include/imainframelayout.h000066400000000000000000000045261321750546400210620ustar00rootroot00000000000000#ifndef IMAINFRAME_LAYOUT_H_ #define IMAINFRAME_LAYOUT_H_ #include "imodule.h" #include class IMainFrameLayout { public: /** * Destructor */ virtual ~IMainFrameLayout() {} /** * Each MainFrame layout has a unique name. */ virtual std::string getName() = 0; /** * Use this to let the Layout construct its widgets and * restore its state from the Registry, if appropriate. */ virtual void activate() = 0; /** * Advises this layout to destruct its widgets and remove itself * from the MainFrame. */ virtual void deactivate() = 0; /** * greebo: Each layout should implement this command to maximise * the camera view and restore it again at the next call. The layout * is activated with a un-maximised camera view, so the first call * to this method will maximise it. On deactivation, the layout should * take care of un-maxmising the camview (if necessary) before saving * its state. */ virtual void toggleFullscreenCameraView() = 0; /** * Loads and applies the stored state from the registry. */ virtual void restoreStateFromRegistry() = 0; }; typedef std::shared_ptr IMainFrameLayoutPtr; /** * This represents a function to create a mainframe layout like this: * * IMainFrameLayoutPtr createInstance(); */ typedef std::function CreateMainFrameLayoutFunc; const std::string MODULE_MAINFRAME_LAYOUT_MANAGER("MainFrameLayoutManager"); class IMainFrameLayoutManager : public RegisterableModule { public: /** * Retrieves a layout with the given name. Returns NULL if not found. */ virtual IMainFrameLayoutPtr getLayout(const std::string& name) = 0; /** * Register a layout by passing a name and a function to create such a layout. */ virtual void registerLayout(const std::string& name, const CreateMainFrameLayoutFunc& func) = 0; /** * greebo: Registers all layout commands to the eventmanager. */ virtual void registerCommands() = 0; }; // This is the accessor for the mainframe module inline IMainFrameLayoutManager& GlobalMainFrameLayoutManager() { // Cache the reference locally static IMainFrameLayoutManager& _mainFrameLayoutManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MAINFRAME_LAYOUT_MANAGER) ) ); return _mainFrameLayoutManager; } #endif /* IMAINFRAME_LAYOUT_H_ */ DarkRadiant-2.5.0/include/imap.h000066400000000000000000000057611321750546400164440ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "inode.h" #include // Registry setting for suppressing the map load progress dialog const char* const RKEY_MAP_SUPPRESS_LOAD_STATUS_DIALOG = "user/ui/map/suppressMapLoadDialog"; // Namespace forward declaration class INamespace; typedef std::shared_ptr INamespacePtr; // see mapfile.h class IMapFileChangeTracker; // see ientity.h class ITargetManager; namespace scene { /** * greebo: A root node is the top level element of a map. * It also owns the namespace of the corresponding map. */ class IMapRootNode : public virtual INode { public: virtual ~IMapRootNode() {} /** * greebo: Returns the namespace of this root. */ virtual const INamespacePtr& getNamespace() = 0; /** * Returns the target manager keeping track of all * the named targets in the map. */ virtual ITargetManager& getTargetManager() = 0; /** * The map root node is holding an implementation of the change tracker * interface, to keep track of whether the map resource on disk is * up to date or not. */ virtual IMapFileChangeTracker& getUndoChangeTracker() = 0; }; typedef std::shared_ptr IMapRootNodePtr; } // namespace scene /** * greebo: This is the global interface to the currently * active map file. */ class IMap : public RegisterableModule { public: enum MapEvent { MapLoading, // emitted just before a map is starting to be loaded MapLoaded, // emitted when the current map is done loading MapUnloading, // emitted just before a map is unloaded from memory MapUnloaded, // emitted after a map has been unloaded MapSaving, // emitted before a map is about to be saved (changes are possible) MapSaved, // emitted right after a map has been saved }; typedef sigc::signal MapEventSignal; /// Returns the signal that is emitted on various events virtual MapEventSignal signal_mapEvent() const = 0; /** * Returns the worldspawn node of this map. The worldspawn * node is NOT created if it doesn't exist yet, so this * might return an empty pointer. */ virtual const scene::INodePtr& getWorldspawn() = 0; /** * This retrieves the worldspawn node of this map. * If no worldspawn can be found, this creates one. * Use this instead of getWorldSpawn() if your code needs * a worldspawn to work with. */ virtual const scene::INodePtr& findOrInsertWorldspawn() = 0; /** * Returns the root node of this map or NULL if this is an empty map. */ virtual scene::IMapRootNodePtr getRoot() = 0; /** * Returns the name of the map. */ virtual std::string getMapName() const = 0; }; typedef std::shared_ptr IMapPtr; const std::string MODULE_MAP("Map"); // Application-wide Accessor to the currently active map inline IMap& GlobalMapModule() { // Cache the reference locally static IMap& _mapModule( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MAP) ) ); return _mapModule; } DarkRadiant-2.5.0/include/imapformat.h000066400000000000000000000201331321750546400176430ustar00rootroot00000000000000#pragma once #include "imodule.h" namespace scene { class NodeVisitor; class INode; typedef std::shared_ptr INodePtr; } namespace parser { class DefTokeniser; } class Entity; class IBrush; class IPatch; /** Callback function to control how the Walker traverses the scene graph. This function * will be provided to the map export module by the Radiant map code. */ typedef void (*GraphTraversalFunc) (const scene::INodePtr& root, scene::NodeVisitor& nodeExporter); namespace map { /** * A Primitive parser is able to create a primitive (brush, patch) from a given token stream. * The initial token, e.g. "brushDef3" is already parsed when the stream is passed to the * parse method. * * Such a class should not change its "state" during the parse() calls - the map parser * is calling the same instance of this PrimitiveParser over and over, one call for each * primitive, so when returning from the parse() method the class should be ready * to process the next primitive. */ class PrimitiveParser { public: virtual ~PrimitiveParser() {} /** * Returns the primitive keyword of this parser, e.g. "brushDef3". When the Map parser * encounters this keyword, the stream is passed along to the parse() method to create * a scene node from it. */ virtual const std::string& getKeyword() const = 0; /** * Creates and returns a primitive node according to the encountered token. */ virtual scene::INodePtr parse(parser::DefTokeniser& tok) const = 0; }; typedef std::shared_ptr PrimitiveParserPtr; /** * An abstract map writer class used to write any map elements * as string to the given output stream. * * The IMapWriter interface defines beginWrite/endWrite pairs for * each scene element (Entity, primitives and the Map itself). * These are called by the map saving algorithm when traversing * the scene depth-first. The usual call order will look like this: * * beginWriteMap * beginWriteEntity * beginWriteBrush * endWriteBrush * beginWritePatch * endWritePatch * .... * endWriteEntity * ... * endWriteMap * * Failure Handling: when the IMapWriter implementation encounters * errors during write (e.g. a visited node is not exportable) a * IMapWriter::FailureException will be thrown. The calling code * is designed to catch this exception. */ class IMapWriter { public: // The generic exception type which is thrown by the IMapWriter methods class FailureException : public std::runtime_error { public: FailureException(const std::string& what) : std::runtime_error(what) {} }; // Destructor virtual ~IMapWriter() {} /** * This is called before writing any nodes, to give an opportunity * to write a map header and version info. */ virtual void beginWriteMap(std::ostream& stream) = 0; /** * Called after all nodes have been visited. Note that this method * should NOT attempt to close the given stream. */ virtual void endWriteMap(std::ostream& stream) = 0; // Entity export methods virtual void beginWriteEntity(const Entity& entity, std::ostream& stream) = 0; virtual void endWriteEntity(const Entity& entity, std::ostream& stream) = 0; // Brush export methods virtual void beginWriteBrush(const IBrush& brush, std::ostream& stream) = 0; virtual void endWriteBrush(const IBrush& brush, std::ostream& stream) = 0; // Patch export methods virtual void beginWritePatch(const IPatch& patch, std::ostream& stream) = 0; virtual void endWritePatch(const IPatch& patch, std::ostream& stream) = 0; }; typedef std::shared_ptr IMapWriterPtr; /** * An abstract map reader class used to parse map elements * from the given input (string) stream. The map reader instance * is usually associated with an MapImportFilter class where the * parsed elements are sent to. */ class IMapReader { public: // The generic exception type which is thrown by a map reader class FailureException : public std::runtime_error { public: FailureException(const std::string& what) : std::runtime_error(what) {} }; /** * Read the contents of the given stream and send them through the given MapImportFilter. * Whether the nodes are actually added to the map or not is something the * ImportFilter can decide. * * throws: FailureException on any error. */ virtual void readFromStream(std::istream& stream) = 0; }; class IMapImportFilter { public: /** * Send an entity node to the import filter. In idTech4 maps all entities * are immediate children of the root node in the scene, so this is where * they usually end up after being added (unless they're filtered out). * * @returns: true if the entity got added, false otherwise. */ virtual bool addEntity(const scene::INodePtr& entity) = 0; /** * Add an primitive node to the given entity. * * @returns: true if the primitive got added, false otherwise. */ virtual bool addPrimitiveToEntity(const scene::INodePtr& primitive, const scene::INodePtr& entity) = 0; }; typedef std::shared_ptr IMapReaderPtr; /** * Map Format interface. Each map format is able to traverse the scene graph and write * the contents into a mapfile, or to load a mapfile and populate a scene graph. */ class MapFormat : public RegisterableModule { public: virtual ~MapFormat() {} /** * Get the display name of this map format, e.g. "Doom 3", "Quake 4", etc. */ virtual const std::string& getMapFormatName() const = 0; /** * Each MapFormat can have a certain game type it is designed for, * a value which conincides with the type attribute in the game tag * found in the .game file, e.g. "doom3" or "quake4". */ virtual const std::string& getGameType() const = 0; /** * Instantiate a new map reader, using the given ImportFilter * which will be fed with nodes during the import. */ virtual IMapReaderPtr getMapReader(IMapImportFilter& filter) const = 0; /** * Acquire a map writer instance, for exporting nodes to a stream. */ virtual IMapWriterPtr getMapWriter() const = 0; /** * Returns true if this map format allows the .darkradiant file * to be saved along the actual .map file. Some exporter modules * might want to disable that (i.e. the prefab exporter) */ virtual bool allowInfoFileCreation() const = 0; /** * greebo: Returns true if this map format is able to load * the contents of this file. Usually this includes a version * check of the file header. */ virtual bool canLoad(std::istream& stream) const = 0; }; typedef std::shared_ptr MapFormatPtr; /** * greebo: This is the global map format manager. Use this class to * register any parsers. */ class IMapFormatManager : public RegisterableModule { public: /** * Each MapFormat module should register itself here on startup. */ virtual void registerMapFormat(const std::string& extension, const MapFormatPtr& mapFormat) = 0; /** * Proper MapFormat modules should unregister themselves at shutdown. This includes * removal from all mapped extensions if the format was registered multiple times. */ virtual void unregisterMapFormat(const MapFormatPtr& mapFormat) = 0; /** * Tries to look up the default map format for the given map format name. */ virtual MapFormatPtr getMapFormatByName(const std::string& mapFormatName) = 0; /** * Tries to look up the default map format for the given game type (e.g. "doom3") * associated with the given file extension. */ virtual MapFormatPtr getMapFormatForGameType(const std::string& gameType, const std::string& extension) = 0; /** * Returns the list of registered map formats. */ virtual std::set getMapFormatList(const std::string& extension) = 0; }; typedef std::shared_ptr IMapFormatManagerPtr; } // namespace map const std::string MODULE_MAPFORMATMANAGER("MapFormatManager"); // Application-wide Accessor to the global map format manager inline map::IMapFormatManager& GlobalMapFormatManager() { // Cache the reference locally static map::IMapFormatManager& _mapFormatManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MAPFORMATMANAGER) ) ); return _mapFormatManager; } DarkRadiant-2.5.0/include/imapinfofile.h000066400000000000000000000117351321750546400201560ustar00rootroot00000000000000#pragma once #include #include #include "imodule.h" #include "imap.h" #include namespace parser { class DefTokeniser; } namespace scene { class INode; typedef std::shared_ptr INodePtr; } namespace map { typedef std::pair NodeIndexPair; typedef std::map NodeIndexMap; /** * An info file module is allowed to write text-based information * to the auxiliary .darkradiant file that is written alongside to the * game-compatible .map file. Things like layer or selection set/group * information can be stored persistently between mapping sessions this way. * * The module should write its information in named blocks, like * * MyModuleInfo * { * // arbitrary parseable info here * } * * Later, when the info file is parsed after map load, the module will be asked * to parse the blocks it's responsible for, and apply its information to the map. */ class IMapInfoFileModule { public: virtual ~IMapInfoFileModule() {} // Info File Saving / Exporting // The name of this info file module, mainly for internal book-keeping virtual std::string getName() = 0; /** * Called before any node is written to the .map file. Use tihs * to prepare the internal structures for exporting. */ virtual void onInfoFileSaveStart() = 0; /** * Called during map export traversal when a single * primitive is about to be written to the .map file. * Assemble information about the primitive and save it * internally, until the writeBlocks() method is called. */ virtual void onSavePrimitive(const scene::INodePtr& node, std::size_t entityNum, std::size_t primitiveNum) = 0; /** * Called during map export traversal when a single * entity is about to be written to the .map file. * Assemble information about the entity and save it * internally, until the writeBlocks() method is called. */ virtual void onSaveEntity(const scene::INodePtr& node, std::size_t entityNum) = 0; /** * Final export function, write the assembled data to the * info file stream. This method should include the block file name * and the opening and closing braces in its write operation. */ virtual void writeBlocks(std::ostream& stream) = 0; /** * Called before the info file stream is closed, time for cleanup. */ virtual void onInfoFileSaveFinished() = 0; // Info File Loading / Parsing /** * Called before the info file is loaded, so take tihs opportunity to * clear internal structures that are going to be filled during the parse process. */ virtual void onInfoFileLoadStart() = 0; /** * The info file parser will ask this module when a named block is encountered. * A module which returns true on a block will sign up for parsing the block * and a subsequent call to parseBlock() is imminent. */ virtual bool canParseBlock(const std::string& blockName) = 0; /** * Parse a block as found in the info file. The block name as passed to this method * needs to be registered in the IMapInfoFileManager class before. * * Regarding the state of the tokeniser: the block name will already have been parsed * by the time this method is called, so expect the opening brace { as first token. */ virtual void parseBlock(const std::string& blockName, parser::DefTokeniser& tok) = 0; /** * Invoked by the map parsing code when the info file has been fully loaded, * so modules should now apply the loaded information to the map. * The info file is always loaded after the actual .map file, so this method * can assume that the scene graph is already in place. * For convenience, a NodeMap is passed to this method, mapping * the entity/primitive number combination to scene::INodes. */ virtual void applyInfoToScene(const scene::IMapRootNodePtr& root, const NodeIndexMap& nodeMap) = 0; /** * Post-parsing cleanup routine, called after applyInfoToScene(). */ virtual void onInfoFileLoadFinished() = 0; }; typedef std::shared_ptr IMapInfoFileModulePtr; class IMapInfoFileManager : public RegisterableModule { public: virtual ~IMapInfoFileManager() {} /** * Add an info file module to the global list. The module will be considered * during info file export/import. */ virtual void registerInfoFileModule(const IMapInfoFileModulePtr& module) = 0; /** * Unregister a previouly registered info file module. */ virtual void unregisterInfoFileModule(const IMapInfoFileModulePtr& module) = 0; /** * Call the functor for each registered module. */ virtual void foreachModule(const std::function& functor) = 0; }; } const char* const MODULE_MAPINFOFILEMANAGER("MapInfoFileManager"); // Application-wide Accessor to the global map info file manager inline map::IMapInfoFileManager& GlobalMapInfoFileManager() { // Cache the reference locally static map::IMapInfoFileManager& _manager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MAPINFOFILEMANAGER)) ); return _manager; } DarkRadiant-2.5.0/include/imapresource.h000066400000000000000000000026551321750546400202130ustar00rootroot00000000000000#pragma once #include "inode.h" #include "imodule.h" #include "imap.h" namespace map { class MapFormat; typedef std::shared_ptr MapFormatPtr; } class IMapResource { public: virtual ~IMapResource() {} // Renames this map resource to the new path virtual void rename(const std::string& fullPath) = 0; virtual bool load() = 0; /** * Save this resource * * It's possible to pass a mapformat to be used for saving. If the map * format argument is omitted, the format corresponding to the current * game type is used. * * @returns * true if the resource was saved, false otherwise. */ virtual bool save(const map::MapFormatPtr& mapFormat = map::MapFormatPtr()) = 0; virtual scene::IMapRootNodePtr getNode() = 0; virtual void setNode(const scene::IMapRootNodePtr& node) = 0; }; typedef std::shared_ptr IMapResourcePtr; const char* const MODULE_MAPRESOURCEMANAGER("MapResourceManager"); class IMapResourceManager : public RegisterableModule { public: /** * Load the named map resource from VFS or from a physical path. */ virtual IMapResourcePtr loadFromPath(const std::string& path) = 0; }; inline IMapResourceManager& GlobalMapResourceManager() { // Cache the reference locally static IMapResourceManager& _mapResourceManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MAPRESOURCEMANAGER) ) ); return _mapResourceManager; } DarkRadiant-2.5.0/include/imd5anim.h000066400000000000000000000046151321750546400172160ustar00rootroot00000000000000#pragma once #include "imodule.h" #include #include "math/Vector3.h" #include "math/Quaternion.h" namespace md5 { struct Joint { // The ID of this joint int id; // the name of this bone std::string name; // ID of parent bone (-1 == no parent) int parentId; // The 6 possible components that might be modified by a key enum AnimComponent { X = 1 << 0, Y = 1 << 1, Z = 1 << 2, YAW = 1 << 3, PITCH = 1 << 4, ROLL = 1 << 5, INVALID_COMPONENT = 1 << 6 }; // A bit mask explaining which components we're animating std::size_t animComponents; std::size_t firstKey; // The child joint IDs std::vector children; }; /** * Base class for an MD5 animation as used in Doom 3. */ class IMD5Anim { public: // Information used by the base frame struct Key { Vector3 origin; Quaternion orientation; }; // Each frame has a series of float values, applied to one or more animated components (x, y, z, yaw, pitch, roll) typedef std::vector FrameKeys; /** * Get the number of joints in this animation. */ virtual std::size_t getNumJoints() const = 0; /** * Retrieve a certain joint by index, bounds are [0..getNumJoints()) */ virtual const Joint& getJoint(std::size_t index) const = 0; /** * Returns the baseframe info for the given joint number. */ virtual const Key& getBaseFrameKey(std::size_t jointNum) const = 0; /** * Returns the frame rate this anim should be played. */ virtual int getFrameRate() const = 0; /** * Returns the number of frames in this animation. */ virtual std::size_t getNumFrames() const = 0; /** * Returns the float values of the given frame index. */ virtual const FrameKeys& getFrameKeys(std::size_t index) const = 0; }; typedef std::shared_ptr IMD5AnimPtr; class IAnimationCache : public RegisterableModule { public: /** * Returns the MD5 animation for the given VFS path, or NULL if * the file does not exist or the anim was found to be invalid. */ virtual IMD5AnimPtr getAnim(const std::string& vfsPath) = 0; }; const char* const MODULE_ANIMATIONCACHE("MD5AnimationCache"); } // namespace inline md5::IAnimationCache& GlobalAnimationCache() { // Cache the reference locally static md5::IAnimationCache& _animationCache( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(md5::MODULE_ANIMATIONCACHE) ) ); return _animationCache; } DarkRadiant-2.5.0/include/imd5model.h000066400000000000000000000012151321750546400173630ustar00rootroot00000000000000#pragma once #include #include "imd5anim.h" namespace md5 { /** * Interface for a MD5 model object. This can be used * to control the animation applied to the MD5. */ class IMD5Model { public: /** * Set the animation to play on this model. * Pass a NULL animation to clear any anim. */ virtual void setAnim(const IMD5AnimPtr& anim) = 0; /** * Returns the currently applied animation. */ virtual const IMD5AnimPtr& getAnim() const = 0; /** * Update the mesh using the given animation playback time. */ virtual void updateAnim(std::size_t time) = 0; }; typedef std::shared_ptr IMD5ModelPtr; } // namespace DarkRadiant-2.5.0/include/imediabrowser.h000066400000000000000000000023401321750546400203400ustar00rootroot00000000000000#pragma once #include "imodule.h" #include namespace ui { /** * Interface to the MediaBrowser, which is displaying the * available materials, accessible as a page in the * GroupDialog's notebook. */ class IMediaBrowser : public RegisterableModule { public: virtual ~IMediaBrowser() {} /** * Returns the texture name of the currently selected item. * Will return an empty string if a folder is selected or * nothing is selected at all. */ virtual std::string getSelection() = 0; /** Set the given path as the current selection, highlighting it * in the tree view. * * @param selection * The fullname of the item to select, or the empty string if there * should be no selection. */ virtual void setSelection(const std::string& selection) = 0; // The tab name as registred in the GroupDialog const char* const getGroupDialogTabName() const { return "mediabrowser"; } }; } const char* const MODULE_MEDIABROWSER = "MediaBrowser"; inline ui::IMediaBrowser& GlobalMediaBrowser() { // Cache the reference locally static ui::IMediaBrowser& _mediaBrowser( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MEDIABROWSER) ) ); return _mediaBrowser; } DarkRadiant-2.5.0/include/imenu.h000066400000000000000000000053651321750546400166330ustar00rootroot00000000000000#pragma once #include #include #include class wxMenuItem; namespace ui { /** * An abstract MenuItem needs to provide a getWidget() method * which will be packed into a parent GTK container, plus * various callbacks for determining the visibility/sensitivity of * this item (these are invoked before the menu is showing itself). * * An utility implementation of this class can be found in * gtkutil::MenuItem which can be constructed from strings * and std::function objects. */ class IMenuItem { public: /** * Each menu item must return a distinct widget which is packed * into the parent wxMenu */ virtual wxMenuItem* getMenuItem() = 0; // Callback to run when this item is selected in the menus virtual void execute() = 0; /** * Returns TRUE if this item is visible and should be rendered. * Default implementation returns true for convenience. */ virtual bool isVisible() { return true; } /** * Returns TRUE if this item is sensitive and therefore clickable. * Default implementation returns true for convenience. */ virtual bool isSensitive() { return true; } /** * Called to let the item prepare its visual appearance. Empty default impl. */ virtual void preShow() {} }; typedef std::shared_ptr IMenuItemPtr; /** * An abstract menu object, which can have one or more IMenuItems as children. * The order in which the items are added is visually preserved. */ class IMenu { public: /* PUBLIC TYPES */ /** * Function callback. Each menu item is associated with one of these, which * is invoked when the menu item is activated. */ typedef std::function Callback; /** * Sensitivity callback. This function object returns a true or false value, * indicating whether the associated menu item should be clickable or not. */ typedef std::function SensitivityTest; /** * Visibility callback. This function object returns a true or false value, * indicating whether the associated menu item should be visible or not. */ typedef std::function VisibilityTest; protected: /* * Default test. Returns true in all cases. If a menu item does * not specify its own test, this will be used to ensure the * item is always visible or sensitive. */ static bool _alwaysTrue() { return true; } public: // Convenience method, directly taking text and icon strings plus // callback function objects as argument. virtual void addItem(wxMenuItem* widget, const Callback& callback, const SensitivityTest& sensTest = SensitivityTest(_alwaysTrue), const VisibilityTest& visTest = VisibilityTest(_alwaysTrue)) = 0; // Adds a certain item to this menu virtual void addItem(const IMenuItemPtr& item) = 0; }; } // namespace DarkRadiant-2.5.0/include/imodel.h000066400000000000000000000167131321750546400167660ustar00rootroot00000000000000#pragma once #include "Bounded.h" #include "irender.h" #include "inode.h" #include "imodule.h" #include "imodelsurface.h" #include /* Forward decls */ class AABB; class ModelSkin; class Matrix4; namespace model { typedef std::vector StringList; /** * \brief * Interface for static models. * * This interface provides functions for obtaining information about a LWO or * ASE model, such as its bounding box or poly count. The interface also * inherits from OpenGLRenderable to allow model instances to be used for * rendering. */ class IModel : public OpenGLRenderable, public Bounded { public: /** * greebo: The filename (without path) of this model. */ virtual std::string getFilename() const = 0; /** * greebo: Returns the VFS path which can be used to load * this model from the modelcache. */ virtual std::string getModelPath() const = 0; /** Apply the given skin to this model. * * @param skin * The ModelSkin instance to apply to this model. */ virtual void applySkin(const ModelSkin& skin) = 0; /** Return the number of material surfaces on this model. Each material * surface consists of a set of polygons sharing the same material. */ virtual int getSurfaceCount() const = 0; /** Return the number of vertices in this model, equal to the sum of the * vertex count from each surface. */ virtual int getVertexCount() const = 0; /** Return the number of triangles in this model, equal to the sum of the * triangle count from each surface. */ virtual int getPolyCount() const = 0; /** * \brief * Return a vector of strings listing the active materials used in this * model, after any skin remaps. * * The list is owned by the model instance. */ virtual const StringList& getActiveMaterials() const = 0; /** * \brief * Return the surface with the given index * * Retrieves the interface of a specific surface, to get access to the * surface's polygons and vertices. * * \param surfaceNum * The surface index, must be in [0..getSurfaceCount()) */ virtual const IModelSurface& getSurface(unsigned surfaceNum) const = 0; }; // Smart pointer typedefs typedef std::shared_ptr IModelPtr; typedef std::weak_ptr IModelWeakPtr; /** * greebo: Each node in the scene that represents "just" a model, * derives from this class. Use a cast on this class to * identify model nodes in the scene. */ class ModelNode { public: virtual ~ModelNode() {} // Returns the contained IModel virtual const IModel& getIModel() const = 0; // Returns the contained IModel virtual IModel& getIModel() = 0; // Returns true if this model's scale has been modified // and needs to be written to file virtual bool hasModifiedScale() = 0; }; typedef std::shared_ptr ModelNodePtr; class IModelExporter; typedef std::shared_ptr IModelExporterPtr; /** * Exporter Interface for models (meshes). */ class IModelExporter { public: virtual ~IModelExporter() {} // Virtual constructor idiom. Use this method to generate a new // instance of the implementing subclass. This way the model format manager // can create a fresh instance of this exporter on demand. virtual IModelExporterPtr clone() = 0; enum class Format { Text, // Exporter writes text-based format Binary, // Exporter exports to a binary stream }; virtual Format getFileFormat() const = 0; // The display name for referencing this exporter in the GUI virtual const std::string& getDisplayName() const = 0; // Returns the uppercase file extension this exporter is suitable for virtual const std::string& getExtension() const = 0; // Adds the given Surface to the exporter's queue // The given transform is applied to the surface before the vertices are added to the queue. // Note: Scaling components of the matrix are not treated separately here. virtual void addSurface(const IModelSurface& surface, const Matrix4& localToWorld) = 0; // Adds the given set of polygons to the named surface // The given transform is applied to the surface before the vertices are added to the queue. // Note: Scaling components of the matrix are not treated separately here. virtual void addPolygons(const std::string& materialName, const std::vector& polys, const Matrix4& localToWorld) = 0; // Export the model file to the given stream virtual void exportToStream(std::ostream& stream) = 0; }; /** * Importer interface for models. An importer must be able * to load a model (node) from the VFS. * The importer instance shouldn't maintain an internal state, * such that the same instance can be used to load several models, * from different client code. */ class IModelImporter { public: // Returns the uppercase file extension this exporter is suitable for virtual const std::string& getExtension() const = 0; /** * greebo: Returns a newly created model node for the given model name. * * @modelName: This is usually the value of the "model" spawnarg of entities. * * @returns: the newly created modelnode (can be NULL if the model was not found). */ virtual scene::INodePtr loadModel(const std::string& modelName) = 0; /** * Load a model from the VFS, and return the IModel subclass for it. * * @returns: the IModelPtr containing the renderable model or * NULL if the model loader could not load the file. */ virtual model::IModelPtr loadModelFromPath(const std::string& path) = 0; }; typedef std::shared_ptr IModelImporterPtr; class IModelFormatManager : public RegisterableModule { public: virtual ~IModelFormatManager() {} // Register/unregister an importer class virtual void registerImporter(const IModelImporterPtr& importer) = 0; virtual void unregisterImporter(const IModelImporterPtr& importer) = 0; // Find an importer for the given extension, returns the NullModelLoader if nothing found // Passing in an empty extension will return the NullModelLoader as well virtual IModelImporterPtr getImporter(const std::string& extension) = 0; // Register/unregister an exporter class virtual void registerExporter(const IModelExporterPtr& exporter) = 0; virtual void unregisterExporter(const IModelExporterPtr& exporter) = 0; // Find an exporter for the given extension, returns empty if nothing found virtual IModelExporterPtr getExporter(const std::string& extension) = 0; // Calls the functor with each registered model importer as argument virtual void foreachImporter(const std::function& functor) = 0; // Calls the functor with each registered model exporter as argument virtual void foreachExporter(const std::function& functor) = 0; }; } // namespace model // Utility methods inline bool Node_isModel(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node) != nullptr; } inline model::ModelNodePtr Node_getModel(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } // Contains the default format used for exporting scaled models const char* const RKEY_DEFAULT_MODEL_EXPORT_FORMAT = "user/ui/map/defaultScaledModelExportFormat"; const char* const MODULE_MODELFORMATMANAGER("ModelFormatManager"); inline model::IModelFormatManager& GlobalModelFormatManager() { std::shared_ptr _modelFormatManager( std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MODELFORMATMANAGER) ) ); return *_modelFormatManager; } DarkRadiant-2.5.0/include/imodelcache.h000066400000000000000000000030551321750546400177450ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "imodel.h" #include "inode.h" #include namespace model { /** Modelcache interface. */ class IModelCache : public RegisterableModule { public: /** * greebo: This method returns the readily fabricated scene::Node * containing the suitable model node. The modelPath is analysed * and the correct ModelLoader is invoked to create the node. * * @returns: a valid scene::INodePtr, which is never NULL. If the model * could not be loaded, the fallback "NullModel" is returned. */ virtual scene::INodePtr getModelNode(const std::string& modelPath) = 0; /** * greebo: Get the IModel object for the given VFS path. The request is cached, * so calling this with the same path twice will return the same * IModelPtr to save memory. * * This method is primarily used by the ModelLoaders to acquire their model data. */ virtual IModelPtr getModel(const std::string& modelPath) = 0; // Clears a specific model from the cache virtual void removeModel(const std::string& modelPath) = 0; // Clears the modelcache virtual void clear() = 0; /// Signal emitted after models are reloaded virtual sigc::signal signal_modelsReloaded() = 0; }; } // namespace model const char* const MODULE_MODELCACHE("ModelCache"); inline model::IModelCache& GlobalModelCache() { static model::IModelCache& _modelCache( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MODELCACHE) ) ); return _modelCache; } DarkRadiant-2.5.0/include/imodelsurface.h000066400000000000000000000021501321750546400203250ustar00rootroot00000000000000#pragma once // Math/Vertex classes #include "render/ArbitraryMeshVertex.h" namespace model { // A Polygon (Triangle) which is part of a model surface struct ModelPolygon { ArbitraryMeshVertex a; ArbitraryMeshVertex b; ArbitraryMeshVertex c; }; // Abstract definition of a model surface class IModelSurface { public: // Returns the number of vertices of this surface virtual int getNumVertices() const = 0; // Returns the number of tris of this surface virtual int getNumTriangles() const = 0; // Get a specific vertex of this surface virtual const ArbitraryMeshVertex& getVertex(int vertexIndex) const = 0; /** * greebo: Returns a specific polygon from this model surface. * Don't expect this to be fast as things are returned by value. * This is merely to provide read access to the model polygons * for scripts and plugins. */ virtual ModelPolygon getPolygon(int polygonIndex) const = 0; /** * Get the name of the default material for this surface, i.e. * the name of the material without any skin applied. */ virtual const std::string& getDefaultMaterial() const = 0; }; } // namespace DarkRadiant-2.5.0/include/imodule.h000066400000000000000000000336541321750546400171560ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include "itextstream.h" /** * \defgroup module Module system */ /** greebo: These registry keys can be used application-wide during runtime * to retrieve the various paths. */ const char* const RKEY_APP_PATH = "user/paths/appPath"; const char* const RKEY_HOME_PATH = "user/paths/homePath"; const char* const RKEY_SETTINGS_PATH = "user/paths/settingsPath"; const char* const RKEY_BITMAPS_PATH = "user/paths/bitmapsPath"; const char* const RKEY_MAP_PATH = "user/paths/mapPath"; const char* const RKEY_PREFAB_PATH = "user/paths/prefabPath"; /** * greebo: Compatibility level: a number inlined into all the modules and returned * by their RegisterableModule::getCompatibilityLevel() method. * * This number should be changed each time the set of module/plugin files (.so/.dll/.dylib) * is modified, especially when files are going to be removed from a DarkRadiant release. * The number will be checked by the ModuleRegistry against the internally stored one * to detect outdated binaries and reject their registration. * * As long as no external module/plugin files are removed this number is safe to stay * as it is. Keep this number compatible to std::size_t, i.e. unsigned. */ #define MODULE_COMPATIBILITY_LEVEL 20170327 // A function taking an error title and an error message string, invoked in debug builds // for things like ASSERT_MESSAGE and ERROR_MESSAGE typedef std::function ErrorHandlingFunction; // This method holds a function pointer which can do some error display (like popups) // Each module binary has its own copy of this, it's initialised in performDefaultInitialisation() inline ErrorHandlingFunction& GlobalErrorHandler() { static ErrorHandlingFunction _func; return _func; } /** * Provider for various information that may be required by modules during * initialisation. * * \ingroup module */ class ApplicationContext { public: /** * \brief * Argument list type. */ typedef std::vector ArgumentList; /** * Destructor */ virtual ~ApplicationContext() {} /** * Return the application path of the current Radiant instance. */ virtual std::string getApplicationPath() const = 0; /** * Return the toplevel path contaning runtime data files, such as the GL * programs or UI descriptor files. On Windows this is the same as the * application path, on Linux it is determined at compile-time but probably * something like /usr/share/darkradiant. */ virtual std::string getRuntimeDataPath() const = 0; /** * Return the settings path of the current Radiant instance. */ virtual std::string getSettingsPath() const = 0; /** * Return the settings path of the current Radiant instance. */ virtual std::string getBitmapsPath() const = 0; /** * \brief * Return the list of command line arguments. */ virtual const ArgumentList& getCmdLineArgs() const = 0; /** * Return the reference to the application's output/error streams. */ virtual std::ostream& getOutputStream() const = 0; virtual std::ostream& getErrorStream() const = 0; virtual std::ostream& getWarningStream() const = 0; // Provides a single mutex object which should be locked by client code // before writing to the any of the above streams. virtual std::mutex& getStreamLock() const = 0; /** * Sets up the paths and stores them into the registry. */ virtual void savePathsToRegistry() const = 0; /** * Retrieve a function pointer which can handle assertions and runtime errors */ virtual const ErrorHandlingFunction& getErrorHandlingFunction() const = 0; }; /** * Set of strings for module dependencies. */ typedef std::set StringSet; /** * Interface class for modules. Each RegisterableModule can return its name and * its set of dependencies. * * Note that this interface does NOT concern itself with the type (interface) * of each individual module; it is up to the GlobalBlah() accessor function * associated with each module to perform the required downcast to the known * type. * * All RegisterableModules implement sigc::trackable, since they will often want * to connect themselves to another module's signal(s). * * \ingroup module */ class RegisterableModule: public sigc::trackable { private: const std::size_t _compatibilityLevel; public: RegisterableModule() : _compatibilityLevel(MODULE_COMPATIBILITY_LEVEL) {} /** * Destructor */ virtual ~RegisterableModule() {} /** * Return the name of this module. This must be globally unique across all * modules; the modulesystem will throw a logic_error if two modules attempt * to register themselves with the same name. */ virtual const std::string& getName() const = 0; /** * Return the set of dependencies for this module. The return value is a * set of strings, each containing the unique name (as returned by * getName()) of a module which must be initialised before this one. */ virtual const StringSet& getDependencies() const = 0; /** * Instruct this module to initialise itself. A RegisterableModule must NOT * invoke any calls to other modules in its constructor, since at the point * of construction the other modules will not have been initialised. Once * all of the dependencies are processed by the ModuleRegistry, each module * will have its initialiseModule() method called at the appropriate point. * * The ModuleRegistry guarantees that any modules named in the set of * dependencies returned by getDependencies() will be initialised and ready * for use at the time this method is invoked. Attempting to access a module * which was NOT listed as a dependency is undefined. * * @param ctx * The ApplicationContext of the running Radiant application. */ virtual void initialiseModule(const ApplicationContext& ctx) = 0; /** * Optional shutdown routine. Allows the module to de-register itself, * shutdown windows, save stuff into the Registry and so on. * * All the modules are getting called one by one, all other modules * are available until the last shutdownModule() call was invoked. */ virtual void shutdownModule() { // Empty default implementation } // Internally queried by the ModuleRegistry. To protect against leftover // binaries containing outdated moudles from being loaded and registered // the compatibility level is compared with the one in the ModuleRegistry. // Old modules with mismatching numbers will be rejected. // Function is intentionally non-virtual and inlined. std::size_t getCompatibilityLevel() const { return _compatibilityLevel; } }; /** * Shared pointer typdef. */ typedef std::shared_ptr RegisterableModulePtr; /** * A ModuleObserver can be attached/detached to certain modules (like the * MaterialManager or the OpenGLRendersystem) to get notified about * realise() or unrealise() events. * (Might be replaced by the sigc signal framework eventually) */ class ModuleObserver { public: virtual ~ModuleObserver() {} virtual void unrealise() = 0; virtual void realise() = 0; }; /** * Interface for the module registry. This is the owner and manager of all * RegisterableModules defined in DLLs and the main binary. * * For obvious reasons, the ModuleRegistry itself is not a module, but a static * object owned by the main binary and returned through a globally-accessible * method. * * \ingroup module */ class IModuleRegistry { public: /** * Destructor */ virtual ~IModuleRegistry() {} /** * Register a RegisterableModule. The name and dependencies are retrieved * through the appropriate RegisterableModule interface methods. * * This method does not cause the RegisterableModule to be initialised. */ virtual void registerModule(const RegisterableModulePtr& module) = 0; /** * Initialise all of the modules previously registered with * registerModule() in the order required by their dependencies. This method * is invoked once, at application startup, with any subsequent attempts * to invoke this method throwing a logic_error. */ virtual void loadAndInitialiseModules() = 0; /** * All the RegisterableModule::shutdownModule() routines are getting * called one by one. No modules are actually destroyed during the * iteration is ongoing, so each module is guaranteed to exist. * * After the last shutdownModule() call has been invoked, the modules * can be safely unloaded/destroyed. */ virtual void shutdownModules() = 0; /** * Retrieve the module associated with the provided unique name. If the * named module is not found, an empty RegisterableModulePtr is returned * (this allows modules to be optional). * * Note that the return value of this function is RegisterableModulePtr, * which in itself is useless to application code. It is up to the accessor * functions defined in each module interface (e.g. GlobalEntityCreator()) * to downcast the pointer to the appropriate type. */ virtual RegisterableModulePtr getModule(const std::string& name) const = 0; /** * Returns TRUE if the named module exists in the records. */ virtual bool moduleExists(const std::string& name) const = 0; /** * This retrieves a reference to the information structure ApplicationContext, * which holds the AppPath, SettingsPath and references to the application * streams. The latter can be used by modules to initialise their * rMessage/globalErrorStreams(). */ virtual const ApplicationContext& getApplicationContext() const = 0; /** * Invoked when all modules have been initialised. */ virtual sigc::signal signal_allModulesInitialised() const = 0; /** * Progress function called during module loading and intialisation. * The string value will carry a message about what is currently in progress. * The float value passed to the signal indicates the overall progress and * will be in the range [0.0f..1.0f]. */ typedef sigc::signal ProgressSignal; virtual ProgressSignal signal_moduleInitialisationProgress() const = 0; /** * Invoked when all modules have been shut down (i.e. after shutdownModule()). */ virtual sigc::signal signal_allModulesUninitialised() const = 0; // The compatibility level this Registry instance was compiled against. // Old module registrations will be rejected by the registry anyway, // on top of that they can actively query this number from the registry // to check whether they are being loaded into an incompatible binary. virtual std::size_t getCompatibilityLevel() const = 0; }; namespace module { /** * \namespace module * Types and functions implementing the module registry system. * * \ingroup module */ /** greebo: This is a container holding a reference to the registry. * The getRegistry() symbol above is not exported to the * modules in Win32 compiles. That's why this structure * has to be initialised in the RegisterModule() routine. * * As soon as it's initialised, the module can access the ModuleRegistry * with the routine GlobalModuleRegistry() below. */ class RegistryReference { IModuleRegistry* _registry; public: RegistryReference() : _registry(NULL) {} inline void setRegistry(IModuleRegistry& registry) { _registry = ®istry; } inline IModuleRegistry& getRegistry() { assert(_registry); // must not be NULL return *_registry; } static RegistryReference& Instance() { static RegistryReference _registryRef; return _registryRef; } }; /** * Global accessor method for the ModuleRegistry. */ inline IModuleRegistry& GlobalModuleRegistry() { return RegistryReference::Instance().getRegistry(); } // Exception thrown if the module being loaded is incompatible with the main binary class ModuleCompatibilityException : public std::runtime_error { public: ModuleCompatibilityException(const std::string& msg) : std::runtime_error(msg) {} }; // greebo: This should be called once by each module at load time to initialise // the OutputStreamHolders inline void initialiseStreams(const ApplicationContext& ctx) { GlobalOutputStream().setStream(ctx.getOutputStream()); GlobalWarningStream().setStream(ctx.getWarningStream()); GlobalErrorStream().setStream(ctx.getErrorStream()); #ifndef NDEBUG GlobalDebugStream().setStream(ctx.getOutputStream()); #endif // Set up the mutex for thread-safe logging GlobalOutputStream().setLock(ctx.getStreamLock()); GlobalWarningStream().setLock(ctx.getStreamLock()); GlobalErrorStream().setLock(ctx.getStreamLock()); GlobalDebugStream().setLock(ctx.getStreamLock()); } // Helper method initialising a few references and checking a module's // compatibility level with the one reported by the ModuleRegistry inline void performDefaultInitialisation(IModuleRegistry& registry) { if (registry.getCompatibilityLevel() != MODULE_COMPATIBILITY_LEVEL) { throw ModuleCompatibilityException("Compatibility level mismatch"); } // Initialise the streams using the given application context initialiseStreams(registry.getApplicationContext()); // Remember the reference to the ModuleRegistry RegistryReference::Instance().setRegistry(registry); // Set up the assertion handler GlobalErrorHandler() = registry.getApplicationContext().getErrorHandlingFunction(); } } // Platform-specific definition which needs to be defined both // in the plugins and the main binary. #if defined(WIN32) #if defined(_MSC_VER) // In VC++ we use this to export symbols instead of using .def files // Note: don't use __stdcall since this is adding stack bytes to the function name #define DARKRADIANT_DLLEXPORT __declspec(dllexport) #else #define DARKRADIANT_DLLEXPORT #endif #elif defined(__APPLE__) #define DARKRADIANT_DLLEXPORT __attribute__((visibility("default"))) #else #define DARKRADIANT_DLLEXPORT #endif DarkRadiant-2.5.0/include/imousetool.h000066400000000000000000000117601321750546400177110ustar00rootroot00000000000000#pragma once #include #include #include #include "imousetoolevent.h" class RenderSystem; class RenderableCollector; class VolumeTest; namespace ui { /** * A Tool class represents an operator which can be "used" * in DarkRadiant's Ortho- and Camera views by using the mouse. */ class MouseTool { public: typedef MouseToolEvent Event; enum class Result { Ignored, // event does not apply for this tool Activated, // event handled, tool is now active Continued, // event handled, tool continues to be active Finished, // event handled, tool no longer active }; // Returns the name of this operation. This name is only used // internally and should be unique. virtual const std::string& getName() = 0; // The display name, which is also used in the status bar virtual const std::string& getDisplayName() = 0; virtual Result onMouseDown(Event& ev) = 0; virtual Result onMouseMove(Event& ev) = 0; virtual Result onMouseUp(Event& ev) = 0; // During an active operation the user may hit ESC, // in which case the onCancel event will be fired. // This should not be ignored by the tool, which should // seek to shut down any ongoing operation safely, unless it actually // wants to pass the key through and stay active (e.g. FreeMoveTool). // Classes returning Finished to this call will have the tool cleared. virtual Result onCancel(IInteractiveView&) { // Default behaviour is to remove this tool once ESC is encountered return Result::Finished; } // A tool using pointer mode Capture might want to get notified // when the mouse capture of the window has been lost due to // the user alt-tabbing out of the app or something else. // Any tools using PointerMode::Capture must watch out for this event. virtual void onMouseCaptureLost(IInteractiveView& view) {} // Some tools might want to receive mouseMove events even when they // are not active, to send feedback to the user before the buttons // are pressed. The Clipper tool uses this to change the mouse cursor // to a crosshair when moved over a manipulatable clip point. virtual bool alwaysReceivesMoveEvents() { return false; } // By default, when the user is dragging the mouse to the borders of // the view, the viewport will be moved along. For some tools this might // not be desirable, in which case they need to override this method to // return false. virtual bool allowChaseMouse() { return true; } // A tool can request special mouse capture modes during its active phase // All flags can be combined, use Normal to indicate that no capturing is needed. struct PointerMode { enum Flags { Normal = 0, // no capturing, absolute coordinates are sent to onMouseMove, pointer will be shown Capture = 1, // capture mouse (tools should implement onMouseCaptureLost), see also MotionDeltas Freeze = 2, // pointer will be frozen and kept at the same position Hidden = 4, // pointer will be hidden MotionDeltas = 8, // onMouseMove will receive delta values relative to the capture position }; }; // Some tools might want to capture the pointer after onMouseDown // Override this method to return "Capture" instead of "Normal". virtual unsigned int getPointerMode() { return PointerMode::Normal; } // Bitmask determining which view are refreshed in which way // after each click and mouse pointer movement event struct RefreshMode { enum Flags { NoRefresh = 0, // don't refresh anything Queue = 1 << 0, // queue a redraw (will be painted as soon as the app is idle) Force = 1 << 1, // force a redraw ActiveView = 1 << 2, // refresh the active view only (the one the mouse tool has been activated on) AllViews = 1 << 3, // refresh all available views }; }; virtual unsigned int getRefreshMode() { // By default, force a refresh of the view the tool is active on return RefreshMode::Force | RefreshMode::ActiveView; } // Optional render routine that is invoked after the scene // has been drawn on the interactive window. The projection // and modelview matrix have already been set up for // overlay rendering (glOrtho). virtual void renderOverlay() {} // For in-scene rendering of active mousetools they need implement this method. // Any needed shaders should be acquired on-demand from the attached rendersystem. // Renderable objects need to be submitted to the given RenderableCollector. virtual void render(RenderSystem& renderSystem, RenderableCollector& collector, const VolumeTest& volume) {} }; typedef std::shared_ptr MouseToolPtr; } // namespace DarkRadiant-2.5.0/include/imousetoolevent.h000066400000000000000000000024021321750546400207440ustar00rootroot00000000000000#pragma once #include "icameraview.h" #include "math/Vector2.h" namespace ui { // Base class for a generic event to be handled by the MouseTool implementation class MouseToolEvent { private: // The device position, normalised within [-1..+1] Vector2 _devicePosition; // The device delta for tools using a frozen mouse pointer Vector2 _deviceDelta; IInteractiveView& _view; public: MouseToolEvent(IInteractiveView& view, const Vector2& devicePosition) : _devicePosition(devicePosition), _deviceDelta(0, 0), _view(view) {} MouseToolEvent(IInteractiveView& view, const Vector2& devicePosition, const Vector2& delta) : _devicePosition(devicePosition), _deviceDelta(delta), _view(view) {} virtual ~MouseToolEvent() {} // Returns the mouse position in normalised device coordinates (x,y in [-1..+1]) const Vector2& getDevicePosition() const { return _devicePosition; } // Used by MouseMove events, this contains the delta // in device coordinates since the last mousemove event const Vector2& getDeviceDelta() const { return _deviceDelta; } IInteractiveView& getInteractiveView() { return _view; } }; } // namespace DarkRadiant-2.5.0/include/imousetoolmanager.h000066400000000000000000000103521321750546400212400ustar00rootroot00000000000000#pragma once #include "imousetool.h" #include "imodule.h" #include #include #include namespace ui { // A list of mousetools class MouseToolStack : public std::list { public: // Tries to handle the given event, returning the first tool that responded positively MouseToolPtr handleMouseDownEvent(MouseTool::Event& mouseEvent) { for (const_iterator i = begin(); i != end(); ++i) { // Ask each tool to handle the event MouseTool::Result result = (*i)->onMouseDown(mouseEvent); if (result != MouseTool::Result::Ignored && result != MouseTool::Result::Finished) { // This tool is now activated return *i; } } return MouseToolPtr(); } }; // A set of MouseTools, use the GlobalMouseToolManager() to get access class IMouseToolGroup { public: virtual ~IMouseToolGroup() {} // MouseTools categories enum class Type { OrthoView = 0, CameraView = 1, }; // Returns the type of this group virtual Type getType() = 0; // Returns the display name of this group virtual std::string getDisplayName() = 0; // Add a MouseTool to the given group virtual void registerMouseTool(const MouseToolPtr& tool) = 0; // Removes the given mousetool from all categories. virtual void unregisterMouseTool(const MouseToolPtr& tool) = 0; // Returns the named mouse tool (case sensitive) or NULL if not found. virtual MouseToolPtr getMouseToolByName(const std::string& name) = 0; // Visit each mouse tool with the given function object virtual void foreachMouseTool(const std::function& func) = 0; // Returns the set of mapped tools for this mouse event. The unsigned int is a bitmap // using the same flags as used in wxutil::MouseButton virtual MouseToolStack getMappedTools(unsigned int state) = 0; // Returns the mapping for the given mousetool virtual unsigned int getMappingForTool(const MouseToolPtr& tool) = 0; // Map a tool to a defined mouse state (as used in wxutil::MouseButton) // It's possible to map the same tool to multiple mouse states virtual void addToolMapping(unsigned int state, const MouseToolPtr& tool) = 0; // Iterate over each tool mapping virtual void foreachMapping(const std::function& func) = 0; // Removes all mappings for all tools virtual void clearToolMappings() = 0; // Removes all mappings for the given tool virtual void clearToolMapping(MouseToolPtr& tool) = 0; }; typedef std::shared_ptr IMouseToolGroupPtr; /** * An interface to an object which is able to hold one or more mousetool * groups. * * Clients of IMouseToolManager are GlobalXYWnd and GlobalCamera, for instance. */ class IMouseToolManager : public RegisterableModule { public: virtual ~IMouseToolManager() {} // Get the group defined by the given enum. This always succeeds, if the group // is not existing yet, a new one will be created internally. virtual IMouseToolGroup& getGroup(IMouseToolGroup::Type group) = 0; // Iterate over each group using the given visitor function virtual void foreachGroup(const std::function& functor) = 0; // Returns matching MouseTools for the given event and group type. To convert a wxMouseEvent to // the unsigned int mouseState, use wxutil::MouseButton virtual MouseToolStack getMouseToolsForEvent(IMouseToolGroup::Type group, unsigned int mouseState) = 0; // Prints the list of possible mouse actions to the statusbar virtual void updateStatusbar(unsigned int mouseState) = 0; // Reverts all mappings to the defaults as defined in the stock input.xml virtual void resetBindingsToDefault() = 0; }; } // namespace const char* const MODULE_MOUSETOOLMANAGER = "MouseToolManager"; inline ui::IMouseToolManager& GlobalMouseToolManager() { // Cache the reference locally static ui::IMouseToolManager& _mtManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MOUSETOOLMANAGER) ) ); return _mtManager; } DarkRadiant-2.5.0/include/inameobserver.h000066400000000000000000000012001321750546400203370ustar00rootroot00000000000000#ifndef _NAME_OBSERVER_H_ #define _NAME_OBSERVER_H_ #include #include /** * greebo: A NameObserver is observing one name in a map Namespace. * * It provides "event methods" which get called by the Namespace on * any name changes. */ class NameObserver { public: virtual ~NameObserver() {} /** * greebo: This is the "change" event, which gets issued by the Namespace. * The old name as well as the new name is passed along. */ virtual void onNameChange(const std::string& oldName, const std::string& newName) = 0; }; typedef std::shared_ptr NameObserverPtr; #endif /* _NAME_OBSERVER_H_ */ DarkRadiant-2.5.0/include/inamespace.h000066400000000000000000000125031321750546400176130ustar00rootroot00000000000000#ifndef _INAMESPACE_H__ #define _INAMESPACE_H__ #include "inode.h" #include "imodule.h" // Forward declaration (is declared in inameobserver.h) class NameObserver; /** * greebo: The Namespace is the managing class which keeps track * of all the names in the map. It gets notified on name changes * and broadcasts this event to all registered NameObservers. * * Additionally, it provides algorithms to merge elements from * another namespace into this one, preserving all links between * Namespaced objects. */ class INamespace { public: virtual ~INamespace() {} /** * greebo: Call this to put the given scene::Node and all its * children into this Namespace. This will search for Namespaced * items and tell them to add their relevant "names" to this space. */ virtual void connect(const scene::INodePtr& root) = 0; /** * greebo: The counter-part of the connect() method above. This will * remove the given node and all its children from this namespace. */ virtual void disconnect(const scene::INodePtr& root) = 0; /** * greebo: Returns true if the given name already exists in this namespace. */ virtual bool nameExists(const std::string& name) = 0; /** * greebo: Inserts the given name into this namespace. The name * must not exist yet, it won't get inserted otherwise. * * @returns: TRUE if the insertion was successful, FALSE if the name already exists. */ virtual bool insert(const std::string& name) = 0; /** * greebo: Removes the given name from this namespace. The name * is available again afterwards. * * @returns: TRUE if the removal was successful, FALSE if the name did not exist. */ virtual bool erase(const std::string& name) = 0; /** * \brief * Add a new name to the namespace, ensuring that it is unique by adding or * changing a numeric prefix if necessary. * * \return * The actual name that was inserted, unique in this namespace. */ virtual std::string addUniqueName(const std::string& originalName) = 0; /** * greebo: Registers/de-registers a Observer. The Observer will get notified * as soon as the name changes. */ virtual void addNameObserver(const std::string& name, NameObserver& observer) = 0; virtual void removeNameObserver(const std::string& name, NameObserver& observer) = 0; /** * greebo: Tells the namespace to notify the nameobservers about a name change. * This usually gets called by the entity itself, whose name has been altered. */ virtual void nameChanged(const std::string& oldName, const std::string& newName) = 0; /** * \brief * Prepares the given scene graph for import into this namespace by ensuring * that none of its names conflict with those in this namespace. * * The nodes below should have been added to a different namespace * prior to this call, so that links are preserved during name changes. * * After this call, the imported nodes are renamed to fit into this * namespace and can be safely connected to this Namespace. This method does * \em not actually import the given scene graph's names into this * namespace. */ virtual void ensureNoConflicts(const scene::INodePtr& root) = 0; }; typedef std::shared_ptr INamespacePtr; /** * greebo: A Namespaced object is the primary communication * partner for the Namespace. This object provides methods * to connect/disconnect items from a map Namespace. * * Note: the standard implementation in D3 maps is * the NamespaceManager class in the entity module. * * Note: I chose to use raw INamespace* pointers in the interface * to allow the Namespace calling these functions without maintaining * weak_ptrs to itself. This can of course be changed in the * future, but I didn't want to bother with "self" weak_ptrs right now. */ class Namespaced { public: /** * Destructor */ virtual ~Namespaced() {} /** * Returns the name of this object. */ virtual std::string getName() const = 0; // Gets/sets the namespace of this named object virtual void setNamespace(INamespace* space) = 0; virtual INamespace* getNamespace() const = 0; /** * Tells the Namespaced object to register all relevant * names with the associated Namespace. */ virtual void attachNames() = 0; // Counter-part of the registerNames() method above. virtual void detachNames() = 0; /** * Tells the object to change its name. Invoked by the * INamespace class itself during import. */ virtual void changeName(const std::string& newName) = 0; virtual void connectNameObservers() = 0; virtual void disconnectNameObservers() = 0; }; typedef std::shared_ptr NamespacedPtr; inline NamespacedPtr Node_getNamespaced(scene::INodePtr node) { return std::dynamic_pointer_cast(node); } /** * greebo: Use the namespace factory to create new Namespace objects. */ class INamespaceFactory : public RegisterableModule { public: /** * Creates and returns a new Namespace. */ virtual INamespacePtr createNamespace() = 0; }; const std::string MODULE_NAMESPACE_FACTORY("NamespaceFactory"); // Factory accessor inline INamespaceFactory& GlobalNamespaceFactory() { // Cache the reference locally static INamespaceFactory& _namespaceFactory( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_NAMESPACE_FACTORY) ) ); return _namespaceFactory; } #endif /* _INAMESPACE_H__ */ DarkRadiant-2.5.0/include/inode.h000066400000000000000000000160311321750546400166040ustar00rootroot00000000000000#pragma once #include "Bounded.h" #include "ilayer.h" #include "irenderable.h" #include #include #include class AABB; class Matrix4; class IRenderEntity; typedef std::shared_ptr IRenderEntityPtr; namespace scene { /** * Interface for objects which can be filtered by the FilterSystem. */ class Filterable { public: /** * Destructor */ virtual ~Filterable() {} /** * Return the filtered state of this object. Returns true if the object is * hidden due to filtering, and false if it is visible. * * It is up to the object's implementation to determine what criteria are * used for filtering (texture, entityclass etc), however these criteria * MUST be controlled by the filter system, and cannot include other * arbitrary criteria such as the time of day or available memory. * * Note that this is not the primary function to discard objects during * rendering or selection test. Use INode::visible() instead, which includes * the current "filtered" setting. */ virtual bool isFiltered() const = 0; // Set the filtered status of this object virtual void setFiltered(bool filtered) = 0; }; class INode; typedef std::shared_ptr INodePtr; typedef std::weak_ptr INodeWeakPtr; class IMapRootNode; typedef std::shared_ptr IMapRootNodePtr; class Graph; typedef std::shared_ptr GraphPtr; class NodeVisitor { public: /** * Destructor */ virtual ~NodeVisitor() {} /** * greebo: Gets called before the children are traversed. * Return TRUE to traverse the children, FALSE to prevent this. */ virtual bool pre(const INodePtr& node) = 0; /** * greebo: Optional post-traverse call, gets invoked after the children * of this node have been traversed. */ virtual void post(const INodePtr& node) {} }; /** * greebo: Abstract definition of a Node, a basic element * of the scenegraph. All nodes share a certain set of * functionality, like Layer functionality or being a Renderable. */ class INode : public Layered, public Filterable, public Bounded, public Renderable { public: enum class Type { Unknown = 0, MapRoot, Entity, Brush, Patch, Model, Particle, EntityConnection, }; public: virtual ~INode() {} /// Get the user-friendly string name of this node. virtual std::string name() const = 0; // Returns the type of this node virtual Type getNodeType() const = 0; /** * Set the scenegraph this node is belonging to. This is usually * set by the scenegraph itself during insertion. */ virtual void setSceneGraph(const GraphPtr& sceneGraph) = 0; /** greebo: Returns true, if the node is the root element * of the scenegraph. */ virtual bool isRoot() const = 0; /** greebo: Sets the "isRoot" flag of this node. */ virtual void setIsRoot(bool isRoot) = 0; /** greebo: State bit accessor methods. This enables/disables * the bit of the state flag (e.g. hidden, excluded) */ virtual void enable(unsigned int state) = 0; virtual void disable(unsigned int state) = 0; // Returns true if the given state bit mask is set, false otherwise virtual bool checkStateFlag(unsigned int state) const = 0; /** greebo: Returns true, if the node is not hidden by * exclusion, filtering or anything else. */ virtual bool visible() const = 0; /** greebo: Returns true, if the node is excluded (eExcluded flag set) */ virtual bool excluded() const = 0; // Set the "forced visible" flag, which overrides the ordinary filtered/excluded state // This is used to force the rendering of nodes that are selected but would otherwise // be hidden due to the filtered/layered state. virtual void setForcedVisibility(bool forceVisible, bool includeChildren) = 0; // Child node handling // Adds a new child node (is appended at the end of the list of existing children) virtual void addChildNode(const INodePtr& node) = 0; // Adds a new child node (is inserted at the front of any existing children) // Useful in special scenarios like when adding a world spawn node. virtual void addChildNodeToFront(const INodePtr& node) = 0; virtual void removeChildNode(const INodePtr& node) = 0; virtual bool hasChildNodes() const = 0; /** * greebo: Traverses this node and all child nodes (recursively) * using the given visitor. * * Note: replaces the legacy Node_traverseSubgraph() method. */ virtual void traverse(NodeVisitor& visitor) = 0; /** * greebo: Traverses all child nodes (recursively) using the given visitor. * Note: this will NOT visit the current node. */ virtual void traverseChildren(NodeVisitor& visitor) const = 0; /** * Traversal function which can be used to hit all nodes in a * graph or collection. If the functor returns false traversal stops. */ typedef std::function VisitorFunc; /** * Call the given functor for each child node, depth first * This is a simpler alternative to the usual traverse() method * which provides pre() and post() methods and more control about * which nodes to traverse and. This forEachNode() routine simply * hits every child node including their children. * * @returns: true if the functor returned false on any of the * visited nodes. The return type is used to pass the stop signal * up the stack during traversal. */ virtual bool foreachNode(const VisitorFunc& functor) const = 0; /** * Returns a shared_ptr to itself. */ virtual scene::INodePtr getSelf() = 0; // Set the parent of this node, is called on insertion in a traversable virtual void setParent(const INodePtr& parent) = 0; virtual scene::INodePtr getParent() const = 0; /** * greebo: Gets called after the node has been inserted into the scene. */ virtual void onInsertIntoScene(IMapRootNode& root) = 0; /** * greebo: This gets called by the SceneGraph before the Node is actually * removed from the scene. This gives the node the opportunity to * change its "selected" status or anything else. */ virtual void onRemoveFromScene(IMapRootNode& root) = 0; /** * Returns true if this node is in the scene */ virtual bool inScene() const = 0; // Get/Set the render entity this node is attached to virtual IRenderEntity* getRenderEntity() const = 0; virtual void setRenderEntity(IRenderEntity* entity) = 0; // Call this if the node gets changed in any way or gets inserted somewhere. virtual void boundsChanged() = 0; // Call this on transform change virtual void transformChanged() = 0; // Returns the bounds in world coordinates virtual const AABB& worldAABB() const = 0; // Returns the transformation from local to world coordinates virtual const Matrix4& localToWorld() const = 0; // Undo/Redo events - some nodes need to do extra legwork after undo or redo // This is called by the TraversableNodeSet after a undo/redo operation // not by the UndoSystem itself, at least not yet. virtual void onPostUndo() {} virtual void onPostRedo() {} // Called during recursive transform changed, but only by INodes themselves virtual void transformChangedLocal() = 0; }; } // namespace scene DarkRadiant-2.5.0/include/iorthocontextmenu.h000066400000000000000000000024261321750546400213070ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "imenu.h" namespace ui { class IOrthoContextMenu : public RegisterableModule { public: // The section enum specifies where menu item should be listed under // It's possible to add new sections > SECTION_USER enum Section { SECTION_CREATE, // Create Entity, Create Speaker, etc. SECTION_ACTION, // Make Visportals SECTION_SELECTION_GROUPS, // Selection Groups SECTION_LAYER, // Layer operations SECTION_USER = 100, }; /** * Adds a new ortho context menu item in the given section. * * @section: an integer value referring to the Section enum above. * It's possible to specify new sections by passing values > SECTION_USER. * Sections are visually separated by horizontal lines in the menu. */ virtual void addItem(const IMenuItemPtr& item, int section) = 0; /** * Removes a certain item from the menus. */ virtual void removeItem(const IMenuItemPtr& item) = 0; }; } // namespace const char* const MODULE_ORTHOCONTEXTMENU = "OrthoContextMenu"; inline ui::IOrthoContextMenu& GlobalOrthoContextMenu() { // Cache the reference locally static ui::IOrthoContextMenu& _menu( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_ORTHOCONTEXTMENU) ) ); return _menu; } DarkRadiant-2.5.0/include/iorthoview.h000066400000000000000000000046541321750546400177150ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "iinteractiveview.h" template class BasicVector3; typedef BasicVector3 Vector3; // Possible types of the orthogonal view window enum EViewType { YZ = 0, XZ = 1, XY = 2 }; namespace ui { class IOrthoView : public IInteractiveView { public: virtual ~IOrthoView() {} // The cursor types available on orthoviews enum class CursorType { Pointer, Crosshair, Default = Pointer, }; // Returns the scale factor of this view virtual float getScale() const = 0; // Snaps the given Vector to the XY view's grid // Note that one component of the given vector stays untouched. virtual void snapToGrid(Vector3& point) = 0; // Returns the projection type (XY, XZ, YZ) of this view virtual EViewType getViewType() const = 0; // Sets the mouse cursor type of this view virtual void setCursorType(CursorType type) = 0; // Increase / decrease zoom factor virtual void zoomIn() = 0; virtual void zoomOut() = 0; // Scrolls the view by the specified amount of screen pixels virtual void scroll(int x, int y) = 0; }; class IXWndManager : public RegisterableModule { public: // Passes a draw call to each allocated view, set force to true // to redraw immediately instead of queueing the draw. virtual void updateAllViews(bool force = false) = 0; // Free all allocated views virtual void destroyViews() = 0; // Sets the origin of all available views virtual void setOrigin(const Vector3& origin) = 0; // Sets the scale of all available views virtual void setScale(float scale) = 0; // Positions all available views virtual void positionAllViews(const Vector3& origin) = 0; // Positions the active views virtual void positionActiveView(const Vector3& origin) = 0; // Returns the view type of the currently active view virtual EViewType getActiveViewType() const = 0; // Sets the viewtype of the active view virtual void setActiveViewType(EViewType viewType) = 0; }; } // namespace const char* const MODULE_ORTHOVIEWMANAGER = "OrthoviewManager"; // This is the accessor for the xy window manager module inline ui::IXWndManager& GlobalXYWndManager() { // Cache the reference locally static ui::IXWndManager& _xyWndManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_ORTHOVIEWMANAGER) ) ); return _xyWndManager; } DarkRadiant-2.5.0/include/iparticlenode.h000066400000000000000000000012171321750546400203300ustar00rootroot00000000000000#pragma once #include "inode.h" #include namespace particles { class IRenderableParticle; typedef std::shared_ptr IRenderableParticlePtr; /// Interface for a Node containing a particle system class IParticleNode: public virtual scene::INode { public: // Get the reference to the render particle this node is containing virtual IRenderableParticlePtr getParticle() const = 0; }; typedef std::shared_ptr IParticleNodePtr; /// Test if a node is a particle node inline bool isParticleNode(const scene::INodePtr& node) { return (dynamic_cast(node.get()) != NULL); } } // namespace DarkRadiant-2.5.0/include/iparticles.h000066400000000000000000000147671321750546400176630ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "irenderable.h" #include #include class RenderSystem; class Matrix4; template class BasicVector3; typedef BasicVector3 Vector3; class AABB; namespace scene { class INode; typedef std::shared_ptr INodePtr; } /// Classes related to storage and rendering of particle systems namespace particles { // see iparticlestage.h for definition class IStageDef; // see iparticlenode.h for definition class IParticleNode; typedef std::shared_ptr IParticleNodePtr; /** * \brief * Definition of a particle system. * * Each particle system is made up of one or more particle stages, information * about which is provided via the IStageDef interface. */ class IParticleDef { public: /** * Destructor */ virtual ~IParticleDef() {} /// Get the name of the particle system. virtual const std::string& getName() const = 0; /** * Get the name of the .prt file this particle is defined in. * Might return an empty string if this particle def has not been saved yet. */ virtual const std::string& getFilename() const = 0; /** * Set/get the depth hack flag */ virtual float getDepthHack() const = 0; virtual void setDepthHack(float value) = 0; /// Returns the number of stages for this particle system. virtual std::size_t getNumStages() const = 0; /// Get a const stage definition from the particle definition virtual const IStageDef& getStage(std::size_t stageNum) const = 0; /// Get a stage definition from the particle definition virtual IStageDef& getStage(std::size_t stageNum) = 0; /** * Add a new stage to this particle, returns the index of the new stage. */ virtual std::size_t addParticleStage() = 0; /** * Removes the stage with the given index. */ virtual void removeParticleStage(std::size_t index) = 0; /** * Swaps the location of the two given particle stages. After this step the * particle stage at will be at and vice versa. * If one of the indices is out of bounds (or both indices are equal) nothing will happen. */ virtual void swapParticleStages(std::size_t index, std::size_t index2) = 0; /// Signal emitted when some aspect of the particle def has changed virtual sigc::signal signal_changed() const = 0; // Comparison operators - particle defs are considered equal if all properties (except the name!), // number of stages and stage contents are the equal virtual bool operator==(const IParticleDef& other) const = 0; virtual bool operator!=(const IParticleDef& other) const = 0; // Copies all properties from the other particle, overwriting this one // Note: Name, filename and observers are not copied virtual void copyFrom(const IParticleDef& other) = 0; }; typedef std::shared_ptr IParticleDefPtr; /** * A renderable particle, which is capable of compiling the * particle system into actual geometry usable for the backend rendersystem. * * As it derives from Renderable, this object can be added to a RenderableCollector * during the front-end render phase. */ class IRenderableParticle : public Renderable { public: /** * Update the particle geometry using the given rendersystem. * The rendersystem is needed for acquiring the shaders and * the current render time. * * @viewRotation: the matrix to orient themselves to the viewer. */ virtual void update(const Matrix4& viewRotation) = 0; /** * Get the particle definition used by this renderable. */ virtual const IParticleDefPtr& getParticleDef() const = 0; /** * Set the particle definition. You'll need to call update() after * setting a new particle def. */ virtual void setParticleDef(const IParticleDefPtr& def) = 0; /** * greebo: Particles have a main direction, usually defined by the * emitter's rotation. For a stand-alone particle (without emitter) * this direction defaults to <0,0,1>, but can be overridden here. */ virtual void setMainDirection(const Vector3& direction) = 0; /** * Set the colour needed by the particle system when the setting * "use entity colour" is activated. */ virtual void setEntityColour(const Vector3& colour) = 0; /** * Returns the bounding box taken by the entirety of quads in this particle. * Make sure to call this after the update() method, as getAABB() will * calculate and return the bounds at the time passed to update(). */ virtual const AABB& getBounds() = 0; }; typedef std::shared_ptr IRenderableParticlePtr; /** * Callback for evaluation particle defs. */ typedef std::function< void (const IParticleDef&) > ParticleDefVisitor; /// Inteface for the particles manager class IParticlesManager : public RegisterableModule { public: /// Signal emitted when particle definitions are reloaded virtual sigc::signal signal_particlesReloaded() const = 0; /// Enumerate each particle def. virtual void forEachParticleDef(const ParticleDefVisitor&) = 0; /// Return the definition object for the given named particle system virtual IParticleDefPtr getDefByName(const std::string& name) = 0; /** * Create a renderable particle, which is capable of compiling the * particle system into actual geometry usable for the backend rendersystem. * * @returns: the renderable particle instance or NULL if the named particle was not found. */ virtual IRenderableParticlePtr getRenderableParticle(const std::string& name) = 0; /// Create and return a particle node for the named particle system virtual IParticleNodePtr createParticleNode(const std::string& name) = 0; /** * \brief * Force the particles manager to reload all particle definitions from the * .prt files. * * Any existing references to IParticleDefs will remain valid, but their * contents might change. Anything sensitive to these changes (like the * renderable particles) should connect to the particles reloaded signal. * * If particle defs are removed from the .prt files, the corresponding * IParticleDef instance will remain in memory, but will be empty after * reload. */ virtual void reloadParticleDefs() = 0; }; } // namespace const char* const MODULE_PARTICLESMANAGER = "ParticlesManager"; // Accessor inline particles::IParticlesManager& GlobalParticlesManager() { // Cache the reference locally static particles::IParticlesManager& _particlesManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_PARTICLESMANAGER) ) ); return _particlesManager; } DarkRadiant-2.5.0/include/iparticlestage.h000066400000000000000000000227321321750546400205130ustar00rootroot00000000000000#pragma once #include template class BasicVector3; typedef BasicVector3 Vector3; template class BasicVector4; typedef BasicVector4 Vector4; namespace particles { /** * greebo: A particle parameter represents a bounded member value * of a particle stage (e.g. speed or size). * * Use the evaluate() method to retrieve a particular value. * * It is modeled after the idParticleParam class in the D3 SDK. */ class IParticleParameter { public: // Get the lower bound of this parameter virtual float getFrom() const = 0; // Get the upper bound of this parameter virtual float getTo() const = 0; // Set the lower bound of this parameter virtual void setFrom(float value) = 0; // Set the upper bound of this parameter virtual void setTo(float value) = 0; // Return a specific value. Fraction == 0 returns the lower bound value. virtual float evaluate(float fraction) const = 0; // Returns the integrated value at the point . virtual float integrate(float fraction) const = 0; // Comparison operators - particle parameters are considered equal // if both from and to are equal virtual bool operator==(const IParticleParameter& other) const = 0; virtual bool operator!=(const IParticleParameter& other) const = 0; }; /** * \brief * Definition of a single particle stage. * * Each stage consists of a set of particles with the same properties (texture, * acceleration etc). * * Most of the member descriptions are directly taken from the D3 SDK. */ class IStageDef { public: // Particle orientation enum OrientationType { ORIENTATION_VIEW, ORIENTATION_AIMED, // angle and aspect are disregarded ORIENTATION_X, ORIENTATION_Y, ORIENTATION_Z }; // Particle distribution enum DistributionType { DISTRIBUTION_RECT, // ( sizeX sizeY sizeZ ) DISTRIBUTION_CYLINDER, // ( sizeX sizeY sizeZ ) DISTRIBUTION_SPHERE // ( sizeX sizeY sizeZ ringFraction ) // a ringFraction of zero allows the entire sphere, 0.9 would only // allow the outer 10% of the sphere }; // Particle direction enum DirectionType { DIRECTION_CONE, // parm0 is the solid cone angle DIRECTION_OUTWARD // direction is relative to offset from origin, parm0 is an upward bias }; enum CustomPathType { PATH_STANDARD, PATH_HELIX, // ( sizeX sizeY sizeZ radialSpeed climbSpeed ) PATH_FLIES, PATH_ORBIT, PATH_DRIP }; public: /// Get the shader name. virtual const std::string& getMaterialName() const = 0; /// Set the shader name. virtual void setMaterialName(const std::string& material) = 0; /// Get the particle count. virtual int getCount() const = 0; /// Set the particle count. virtual void setCount(int count) = 0; /** * Get the duration in seconds. */ virtual float getDuration() const = 0; /** * Set the duration in seconds, updates cyclemsec. */ virtual void setDuration(float duration) = 0; /** * Returns ( duration + deadTime ) in msec. */ virtual int getCycleMsec() const = 0; /** * Get the cycles value. */ virtual float getCycles() const = 0; /** * Set the cycles value. */ virtual void setCycles(float cycles) = 0; /** * Get the bunching value [0..1] */ virtual float getBunching() const = 0; /** * Set the bunching value [0..1] */ virtual void setBunching(float value) = 0; /** * Get the time offset in seconds */ virtual float getTimeOffset() const = 0; /** * Set the time offset in seconds */ virtual void setTimeOffset(float value) = 0; /** * Get the dead time in seconds */ virtual float getDeadTime() const = 0; /** * Set the dead time in seconds, updates cyclemsec. */ virtual void setDeadTime(float value) = 0; /** * Get the particle render colour. */ virtual const Vector4& getColour() const = 0; /** * Set the particle render colour. */ virtual void setColour(const Vector4& colour) = 0; /** * Get the particle render colour. */ virtual const Vector4& getFadeColour() const = 0; /** * Set the particle render colour. */ virtual void setFadeColour(const Vector4& colour) = 0; /** * Get the fade in fraction [0..1] */ virtual float getFadeInFraction() const = 0; /** * Set the fade in fraction [0..1] */ virtual void setFadeInFraction(float fraction) = 0; /** * Get the fade out fraction [0..1] */ virtual float getFadeOutFraction() const = 0; /** * Set the fade out fraction [0..1] */ virtual void setFadeOutFraction(float fraction) = 0; /** * Get the fade index fraction [0..1] */ virtual float getFadeIndexFraction() const = 0; /** * Set the fade index fraction [0..1] */ virtual void setFadeIndexFraction(float fraction) = 0; /** * Get the animation frames. */ virtual int getAnimationFrames() const = 0; /** * Set the animation frames. */ virtual void setAnimationFrames(int animationFrames) = 0; /** * Get the animation rate. */ virtual float getAnimationRate() const = 0; /** * Set the animation frames. */ virtual void setAnimationRate(float animationRate) = 0; /** * Get the initial angle. */ virtual float getInitialAngle() const = 0; /** * Set the initial angle. */ virtual void setInitialAngle(float angle) = 0; /** * Get the bounds expansion value. */ virtual float getBoundsExpansion() const = 0; /** * Set the bounds expansion value. */ virtual void setBoundsExpansion(float value) = 0; /** * Get the random distribution flag. */ virtual bool getRandomDistribution() const = 0; /** * Set the random distribution flag. */ virtual void setRandomDistribution(bool value) = 0; /** * Get the "use entity colour" flag. */ virtual bool getUseEntityColour() const = 0; /** * Set the "use entity colour" flag. */ virtual void setUseEntityColour(bool value) = 0; /** * Get the gravity factor. */ virtual float getGravity() const = 0; /** * Set the gravity factor. */ virtual void setGravity(float value) = 0; /** * Get the "apply gravity in world space" flag. */ virtual bool getWorldGravityFlag() const = 0; /** * Get the "apply gravity in world space" flag. */ virtual void setWorldGravityFlag(bool value) = 0; /** * Get the offset vector. */ virtual const Vector3& getOffset() const = 0; /** * Set the offset vector. */ virtual void setOffset(const Vector3& value) = 0; /** * Get the orientation type. */ virtual OrientationType getOrientationType() const = 0; /** * Set the orientation type. */ virtual void setOrientationType(OrientationType value) = 0; /** * Get the orientation parameter with the given index [0..3] */ virtual float getOrientationParm(int parmNum) const = 0; /* * Set the orientation parameter with the given index [0..3]. */ virtual void setOrientationParm(int parmNum, float value) = 0; /** * Get the distribution type. */ virtual DistributionType getDistributionType() const = 0; /** * Set the distribution type. */ virtual void setDistributionType(DistributionType value) = 0; /** * Get the distribution parameter with the given index [0..3] */ virtual float getDistributionParm(int parmNum) const = 0; /* * Set the distribution parameter with the given index [0..3]. */ virtual void setDistributionParm(int parmNum, float value) = 0; /** * Get the direction type. */ virtual DirectionType getDirectionType() const = 0; /** * Set the direction type. */ virtual void setDirectionType(DirectionType value) = 0; /** * Get the direction parameter with the given index [0..3] */ virtual float getDirectionParm(int parmNum) const = 0; /* * Set the direction parameter with the given index [0..3]. */ virtual void setDirectionParm(int parmNum, float value) = 0; /** * Get the custom path type. */ virtual CustomPathType getCustomPathType() const = 0; /** * Set the custom path type. */ virtual void setCustomPathType(CustomPathType value) = 0; /** * Get the custom path parameter with the given index [0..7] */ virtual float getCustomPathParm(int parmNum) const = 0; /* * Set the custom path parameter with the given index [0..7]. */ virtual void setCustomPathParm(int parmNum, float value) = 0; /** * Get the particle size */ virtual const IParticleParameter& getSize() const = 0; virtual IParticleParameter& getSize() = 0; /** * Get the aspect ratio. */ virtual const IParticleParameter& getAspect() const = 0; virtual IParticleParameter& getAspect() = 0; /** * Get the particle speed. */ virtual const IParticleParameter& getSpeed() const = 0; virtual IParticleParameter& getSpeed() = 0; /** * Get the particle rotation speed. */ virtual const IParticleParameter& getRotationSpeed() const = 0; virtual IParticleParameter& getRotationSpeed() = 0; // Comparison operators - particle stages are considered equal // if all properties are equal virtual bool operator==(const IStageDef& other) const = 0; virtual bool operator!=(const IStageDef& other) const = 0; /** * Copy operator, copies all properties from the other stage into this one. */ virtual void copyFrom(const IStageDef& other) = 0; /** * Returns the stage visibility. This flag is used in the Particle Editor context only, * it is not saved to the .prt file therefore it has no effect within the Doom 3 engine. */ virtual bool isVisible() const = 0; /** * Sets the stage visibility. This flag is used in the Particle Editor context only, * it is not saved to the .prt file therefore it has no effect within the Doom 3 engine. */ virtual void setVisible(bool visible) = 0; }; } // namespace DarkRadiant-2.5.0/include/ipatch.h000066400000000000000000000156561321750546400167720ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(INCLUDED_IPATCH_H) #define INCLUDED_IPATCH_H #include "imodule.h" #include #include "inode.h" #include "math/Vector2.h" #include "math/Vector3.h" #include "render/VertexNT.h" // This is thrown by the internal patch routines class GenericPatchException : public std::runtime_error { public: // Constructor GenericPatchException(const std::string& what): std::runtime_error(what) {} }; /* greebo: A PatchControl consists of a vertex and a set of texture coordinates. * Multiple PatchControls form a PatchControlArray or (together with width and height) a PatchControlMatrix. */ struct PatchControl { Vector3 vertex; // The coordinates of the control point Vector2 texcoord; // The texture coordinates of this point }; /** * A structure representing the fully tesselated patch * Can be acquired through the IPatch interface for * exporting the geometry to an external app. */ struct PatchMesh { std::size_t width; // width of this mesh std::size_t height; // height of this mesh /// Geometry with normals and texture coordinates std::vector vertices; }; typedef BasicVector2 Subdivisions; // The abstract base class for a Doom3-compatible patch class IPatch { public: // An observer can attach itself to a specific patch instance. // to get notified about changes. class Observer { public: /** * Is called when the dimensions and/or the * values of one or more control points get altered. */ virtual void onPatchControlPointsChanged() = 0; /** * Is called when the patch shader is changed. */ virtual void onPatchTextureChanged() = 0; /** * Is called by the Patch destructor. After this call * the observer is automatically detached. */ virtual void onPatchDestruction() = 0; }; virtual void attachObserver(Observer* observer) = 0; virtual void detachObserver(Observer* observer) = 0; virtual ~IPatch() {} // Resizes the patch to the given dimensions virtual void setDims(std::size_t width, std::size_t height) = 0; // Get the patch dimensions virtual std::size_t getWidth() const = 0; virtual std::size_t getHeight() const = 0; // Return a defined patch control vertex at , virtual PatchControl& ctrlAt(std::size_t row, std::size_t col) = 0; virtual const PatchControl& ctrlAt(std::size_t row, std::size_t col) const = 0; // Returns a copy of the fully tesselated patch geometry (slow!) virtual PatchMesh getTesselatedPatchMesh() const = 0; /** * greebo: Inserts two columns before and after the column with index . * Throws an GenericPatchException if an error occurs. */ virtual void insertColumns(std::size_t colIndex) = 0; /** * greebo: Inserts two rows before and after the row with index . * Throws an GenericPatchException if an error occurs. */ virtual void insertRows(std::size_t rowIndex) = 0; /** * greebo: Removes columns or rows right before and after the col/row * with the given index, reducing the according dimension by 2. */ virtual void removePoints(bool columns, std::size_t index) = 0; /** * greebo: Appends two rows or columns at the beginning or the end. */ virtual void appendPoints(bool columns, bool beginning) = 0; // Updates the patch tesselation matrix, call this everytime you're done with your PatchControl changes virtual void controlPointsChanged() = 0; // Check if the patch has invalid control points or width/height are zero virtual bool isValid() const = 0; // Check whether all control vertices are in the same 3D spot (with minimal tolerance) virtual bool isDegenerate() const = 0; // Shader handling virtual const std::string& getShader() const = 0; virtual void setShader(const std::string& name) = 0; // greebo: returns true if the patch's shader is visible, false otherwise virtual bool hasVisibleMaterial() const = 0; /** * greebo: Sets/gets whether this patch is a patchDef3 (fixed tesselation) */ virtual bool subdivisionsFixed() const = 0; /** greebo: Returns the x,y subdivision values (for tesselation) */ virtual const Subdivisions& getSubdivisions() const = 0; /** greebo: Sets the subdivision of this patch * * @isFixed: TRUE, if this patch should be a patchDef3 (fixed tesselation) * @divisions: a two-component vector containing the desired subdivisions */ virtual void setFixedSubdivisions(bool isFixed, const Subdivisions& divisions) = 0; }; /* greebo: the abstract base class for a patch-creating class. * At the moment, the CommonPatchCreator, Doom3PatchCreator and Doom3PatchDef2Creator derive from this base class. */ class PatchCreator : public RegisterableModule { public: // Create a patch and return the sceneNode virtual scene::INodePtr createPatch() = 0; }; class Patch; class IPatchNode { public: virtual ~IPatchNode() {} /** * greebo: Retrieves the actual patch from a PatchNode, only works from within the main module. */ virtual Patch& getPatchInternal() = 0; // Get access to the patch interface virtual IPatch& getPatch() = 0; }; typedef std::shared_ptr IPatchNodePtr; inline bool Node_isPatch(const scene::INodePtr& node) { return node->getNodeType() == scene::INode::Type::Patch; //return std::dynamic_pointer_cast(node) != NULL; } inline IPatch* Node_getIPatch(const scene::INodePtr& node) { IPatchNodePtr patchNode = std::dynamic_pointer_cast(node); if (patchNode != NULL) { return &patchNode->getPatch(); } return NULL; } // Casts a node onto a patch inline Patch* Node_getPatch(const scene::INodePtr& node) { IPatchNodePtr patchNode = std::dynamic_pointer_cast(node); if (patchNode != NULL) { return &patchNode->getPatchInternal(); } return NULL; } const char* const MODULE_PATCHDEF2 = "PatchModuleDef2"; const char* const MODULE_PATCHDEF3 = "PatchModuleDef3"; enum class PatchDefType { Def2, Def3, }; // Acquires the PatchCreator of the given type ("Def2", "Def3") inline PatchCreator& GlobalPatchCreator(PatchDefType type) { std::shared_ptr _patchCreator( std::static_pointer_cast( module::GlobalModuleRegistry().getModule( type == PatchDefType::Def2 ? MODULE_PATCHDEF2 : MODULE_PATCHDEF3 ) ) ); return *_patchCreator; } #endif DarkRadiant-2.5.0/include/ipath.h000066400000000000000000000036521321750546400166200ustar00rootroot00000000000000#ifndef IPATH_H_ #define IPATH_H_ #include "inode.h" #include namespace scene { /** greebo: This is the base structure used as unique to scenegraph elements. * * It extends the functionality of std::vector to mimick * the interface of a std::stack (push(), top(), pop()) and * provides an additional parent() method that allows * to retrieve the element right below the top() element. * * The parent() method is a convenience method helping to insert * scenegraph elements at the right place without complicated * popping() and pushing(). * * Note: Normally, deriving from STL containers is not a good idea, due to * the non-virtual destructors (all data members in the subclasses * wouldn't get destroyed). In this case it is ok to derive from * std::vector, because no additional data members are introduced. */ class Path : public std::vector { public: // Default constructor Path() {} // Constructor taking an initial element. // The stack is starting with one top element. Path(const INodePtr& initialElement) { push(initialElement); } // Destructor virtual ~Path() {} // Accessor method to retrieve the last element inserted. INodePtr& top() { return back(); } // Accessor method to retrieve the last element inserted. const INodePtr& top() const { return back(); } // Accessor method to retrieve the element below the last inserted. INodePtr& parent() { return *(std::vector::end()-2); } // Accessor method to retrieve the element below the last inserted. const INodePtr& parent() const { return *(std::vector::end()-2); } // Add an element to the stack, this becomes the top() element void push(const INodePtr& newElement) { push_back(newElement); } // Remove the topmost element, the size of the stack is reduced by 1. void pop() { pop_back(); } }; // class Path } // namespace scene #endif /*IPATH_H_*/ DarkRadiant-2.5.0/include/ipreferencesystem.h000066400000000000000000000072631321750546400212510ustar00rootroot00000000000000#pragma once #include #include "imodule.h" // A list containing possible values for a combo box widgets typedef std::list ComboBoxValueList; /* greebo: This is the interface the preference page has to provide for adding * elements to the dialog page. */ class IPreferencePage { public: // destructor virtual ~IPreferencePage() {} /** * greebo: Allows to set a custom title of this page. The default title * upon construction is "guessed" by adding a " Settings" to * the page name, "Settings/Patch" gets assigned * a "Patch Settings" as default title. * Use this method to change this to fit your needs. */ virtual void setTitle(const std::string& title) = 0; // greebo: Use this to add a checkbox to the preference dialog that is connected to a registry value virtual void appendCheckBox(const std::string& label, const std::string& registryKey) = 0; // greebo: This adds a horizontal slider and connects it to the given registryKey. virtual void appendSlider(const std::string& name, const std::string& registryKey, double lower, double upper, double step_increment, double page_increment) = 0; /** * \brief * Add a drop-down combo box to the preference page. * * \param name * The name to be displayed next to the combo box. * * \param registryKey * The registry key which stores the value of the combo box. * * \param valueList * List of strings containing the values that should be displayed in the * combo box. * * \param storeValueNotIndex * If true, store the selected text in the registry key. If false, store the * numeric index of the selected item in the registry key. The default is * false. */ virtual void appendCombo(const std::string& name, const std::string& registryKey, const ComboBoxValueList& valueList, bool storeValueNotIndex = false) = 0; /* greebo: Appends an entry field with as caption which is connected to the given registryKey */ virtual void appendEntry(const std::string& name, const std::string& registryKey) = 0; /* greebo: Appends an entry field with spinner buttons which retrieves its value from the given * RegistryKey. The lower and upper values have to be passed as well. */ virtual void appendSpinner(const std::string& name, const std::string& registryKey, double lower, double upper, int fraction) = 0; // greebo: Adds a PathEntry to choose files or directories (depending on the given boolean) virtual void appendPathEntry(const std::string& name, const std::string& registryKey, bool browseDirectories) = 0; // Appends a static label (to add some text to the preference page) virtual void appendLabel(const std::string& caption) = 0; }; const char* const MODULE_PREFERENCESYSTEM("PreferenceSystem"); class IPreferenceSystem : public RegisterableModule { public: /** * greebo: Retrieves the page for the given path. If the page * doesn't exist yet, it will be created at the given path, for example: * * "Settings/Patch Settings" * (spaces are ok, slashes are treated as delimiters, don't use them in the page name) * * Use the page interface to add widgets and connect them to registry keys. * @path: The path to lookup/create * @returns: the IPreferencePage reference. */ virtual IPreferencePage& getPage(const std::string& path) = 0; }; inline IPreferenceSystem& GlobalPreferenceSystem() { // Cache the reference locally static IPreferenceSystem& _prefSystem( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_PREFERENCESYSTEM) ) ); return _prefSystem; } DarkRadiant-2.5.0/include/iradiant.h000066400000000000000000000032111321750546400172750ustar00rootroot00000000000000#pragma once #include "imodule.h" #include #include const std::string MODULE_RADIANT("Radiant"); class ThreadManager; // Interface to provide feedback during running operations // see IRadiant::performLongRunningOperation() class ILongRunningOperation { public: virtual ~ILongRunningOperation() {} // Update the operation progress fraction - range [0..1] virtual void setProgress(float progress) = 0; // Set the message that is displayed to the user virtual void setMessage(const std::string& message) = 0; }; /** * \brief * Interface to the core application. */ class IRadiant : public RegisterableModule { public: /// Signal emitted when main Radiant application is constructed virtual sigc::signal signal_radiantStarted() const = 0; /// Signal emitted just before Radiant shuts down virtual sigc::signal signal_radiantShutdown() const = 0; /// Get the threading manager virtual ThreadManager& getThreadManager() = 0; // Runs a long running operation that should block input on all windows // until it completes. The operation functor needs to take a reference to // an operation object which can be used to give feedback like progress or // text messages that might be displayed to the user. virtual void performLongRunningOperation( const std::function& operationFunc, const std::string& title = std::string()) = 0; }; inline IRadiant& GlobalRadiant() { // Cache the reference locally static IRadiant& _radiant( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_RADIANT) ) ); return _radiant; } DarkRadiant-2.5.0/include/iregistry.h000066400000000000000000000067761321750546400175460ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "xmlutil/Document.h" #include "xmlutil/Node.h" #include #include namespace { const std::string RKEY_SKIP_REGISTRY_SAVE = "user/skipRegistrySaveOnShutdown"; } /** * \addtogroup registry XML Registry */ // String identifier for the registry module const std::string MODULE_XMLREGISTRY("XMLRegistry"); /** * Abstract base class for the registry module. * * \ingroup registry */ class Registry : public RegisterableModule { public: enum Tree { treeStandard, treeUser }; // Sets a variable in the XMLRegistry or retrieves one virtual void set(const std::string& key, const std::string& value) = 0; virtual std::string get(const std::string& key) = 0; // Checks whether a key exists in the registry virtual bool keyExists(const std::string& key) = 0; /** * Import an XML file into the registry, without a version check. If the * file cannot be imported for any reason, a std::runtime_error exception * will be thrown. * * @param importFilePath * Full pathname of the file to import. * * @param parentKey * The path to the node within the current registry under which the * imported nodes should be added. * * @param tree: the tree the file should be imported to (e.g. eDefault) */ virtual void import(const std::string& importFilePath, const std::string& parentKey, Tree tree) = 0; // Dumps the whole XML content to std::out for debugging purposes virtual void dump() const = 0; // Exports the data which has been modified during this session // to XML files in the user's settings path virtual void saveToDisk() = 0; // Saves the specified node and all its children into the file virtual void exportToFile(const std::string& key, const std::string& filename = "-") = 0; // Retrieves the nodelist corresponding for the specified XPath (wraps to xml::Document) virtual xml::NodeList findXPath(const std::string& path) = 0; // Creates an empty key virtual xml::Node createKey(const std::string& key) = 0; // Creates a new node named as children of with the name attribute set to // The newly created node is returned after creation virtual xml::Node createKeyWithName(const std::string& path, const std::string& key, const std::string& name) = 0; /** * greebo: Sets the named attribute of the given node specified by . * * @path: The XPath to the node. * @attrName: The name of the attribute. * @attrValue: The string value of the attribute. */ virtual void setAttribute(const std::string& path, const std::string& attrName, const std::string& attrValue) = 0; /** * greebo: Loads the value of the given attribute at the given path. * * @path: The XPath to the node. * @attrName: The name of the attribute. * * @returns: the string value of the attribute. */ virtual std::string getAttribute(const std::string& path, const std::string& attrName) = 0; // Deletes an entire subtree from the registry virtual void deleteXPath(const std::string& path) = 0; /// Return a signal which will be emitted when a given key changes virtual sigc::signal signalForKey(const std::string& key) const = 0; }; typedef std::shared_ptr RegistryPtr; // This is the accessor for the registry inline Registry& GlobalRegistry() { // Cache the reference locally static Registry& _registry( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_XMLREGISTRY) ) ); return _registry; } DarkRadiant-2.5.0/include/irender.h000066400000000000000000000532521321750546400171440ustar00rootroot00000000000000#pragma once #include "imodule.h" #include #include "math/Vector3.h" #include "ShaderLayer.h" #include /** * \file * Interfaces for the back-end renderer. */ /** * \name Global render flags * * These flags control which capabilities of the renderer are used throughout * the render process. They have a four stage lifecycle: * * 1. The flags are initially SET in the Shader implementation, describing the * features that the particular Shader would like to use for rendering its * renderables. For example, a shader pass performing a blend will set * RENDER_BLEND as one of its flags. * * 2. The flags are MASKED by another set of flags provided to a * RenderableCollector before it traverses the scene graph, in order to control * which shader-specified flags can actually be used for that render pass. For * example, the XYRenderer renders in wireframe mode only, so it does not enable * RENDER_FILL in its mask, while the CamRenderer does. * * 3. The flags may be used to set or change OpenGL state in the shader pass * implementation. For example, if RENDER_BLEND is set, then glEnable(GL_BLEND) * will be called before the associated shader's renderables are rendered. Some * flags map directly to glEnable parameters, while others (such as * RENDER_PROGRAM) specify more complex changes. Some flags do not enable any GL * features at all. * * 4. The flags are passed as a parameter to the OpenGLRenderable::render() * method, allowing individual objects to modify their behaviour accordingly. * For example, objects may decide whether or not to submit colour information * to OpenGL based on the value of the RENDER_VERTEX_COLOUR flag. */ ///@{ const unsigned RENDER_DEFAULT = 0; const unsigned RENDER_LINESTIPPLE = 1 << 0; // glEnable(GL_LINE_STIPPLE) const unsigned RENDER_POLYGONSTIPPLE = 1 << 2; // glEnable(GL_POLYGON_STIPPLE) const unsigned RENDER_ALPHATEST = 1 << 4; // glEnable(GL_ALPHA_TEST) const unsigned RENDER_DEPTHTEST = 1 << 5; // glEnable(GL_DEPTH_TEST) const unsigned RENDER_DEPTHWRITE = 1 << 6; // glDepthMask(GL_TRUE) /// Colour buffer writing disabled with glColorMask const unsigned RENDER_MASKCOLOUR = 1 << 7; const unsigned RENDER_CULLFACE = 1 << 8; // glglEnable(GL_CULL_FACE) const unsigned RENDER_SCALED = 1 << 9; // glEnable(GL_NORMALIZE) const unsigned RENDER_SMOOTH = 1 << 10; // glShadeModel const unsigned RENDER_LIGHTING = 1 << 11; // glEnable(GL_LIGHTING) const unsigned RENDER_BLEND = 1 << 12; // glEnable(GL_BLEND) const unsigned RENDER_OFFSETLINE = 1 << 13; // glEnable(GL_POLYGON_OFFSET_LINE) /// Objects will be rendered as filled polygons (not wireframe). const unsigned RENDER_FILL = 1 << 14; /** * If enabled, mesh objects (geometry that does not consist only of GL_POINTS) * should submit vertex colour information. If disabled, mesh objects must not * change glColor during rendering. * * Does not affect GL state. */ const unsigned RENDER_VERTEX_COLOUR = 1 << 15; /** * If enabled, point geometry may submit colours for each point. If disabled, * point geometry must not change colour during rendering. * * Does not affect GL state. */ const unsigned RENDER_POINT_COLOUR = 1 << 16; /// GL_TEXTURE_2D will be enabled during rendering. const unsigned RENDER_TEXTURE_2D = 1 << 17; /** * Cube map rendering (in camera space) is enabled. The renderer will enable * GL_TEXTURE_CUBE_MAP, and set up the texture matrix such that the viewer * location is the origin. Objects should submit their vertex coordinates as * texture coordinates, which will result in the correct cube map alignment. */ const unsigned RENDER_TEXTURE_CUBEMAP = 1 << 18; /** * Normal map information will be used during rendering. If enabled, objects * should submit normal/tangent/bitangent vertex attributes to enable normal * mapping. */ const unsigned RENDER_BUMP = 1 << 19; /// A vertex and fragment shader program will be used during rendering. const unsigned RENDER_PROGRAM = 1 << 20; const unsigned RENDER_OVERRIDE = 1 << 21; typedef unsigned RenderStateFlags; ///@} class AABB; class Matrix4; template class BasicVector3; typedef BasicVector3 Vector3; class Shader; typedef std::shared_ptr ShaderPtr; /** * A RenderEntity represents a map entity as seen by the renderer. * It provides up to 12 numbered parameters to the renderer: * parm0, parm1 ... parm11. * * A few of the entity parms are hardwired to things like render colour * as defined through the entity's _color keyvalue, some are set through * scripting, spawmargs or gameplay code. */ class IRenderEntity { public: /** * Get the value of this entity's shader parm with the given index. */ virtual float getShaderParm(int parmNum) const = 0; /** * Entities can specify directions, which are used for particle emission for instance. */ virtual const Vector3& getDirection() const = 0; /** * Returns the wireframe shader for this entity - child primitives need this for rendering. */ virtual const ShaderPtr& getWireShader() const = 0; }; typedef std::shared_ptr IRenderEntityPtr; typedef std::weak_ptr IRenderEntityWeakPtr; /** * \brief * Interface for a light source in the renderer. */ class RendererLight : public virtual IRenderEntity { public: virtual ~RendererLight() {} virtual const ShaderPtr& getShader() const = 0; /** * \brief * Return the origin of the light volume in world space. * * This corresponds to the "origin" key of the light object, i.e. the center * of the bounding box for an omni light and the tip of the pyramid for a * projected light. */ virtual const Vector3& worldOrigin() const = 0; /** * \brief * Return the world-space to light-texture-space transformation matrix. * * The light texture space is a box, with coordinates [0..1] on each * dimension, representing the texture (UV) coordinates of the light falloff * textures that will be applied to rendered fragments within the light * volume. * * The matrix returned by this method transforms coordinates in world space * into coordinates in light-texture space. */ virtual Matrix4 getLightTextureTransformation() const = 0; /// Return true if this light intersects the given AABB virtual bool intersectsAABB(const AABB& aabb) const = 0; /** * \brief * Return the light origin in world space. * * The light origin is the point from which the light rays are considered to * be projected, i.e. the direction from which bump maps will be illuminated * and shadows (if they existed) would be cast. * * For an omindirectional light, this origin is determined from the * "light_center" keyvalue in combination with the bounding box itself, * whereas for a projected light it is always equal to the tip of the * pyramid (the same as worldOrigin()). */ virtual Vector3 getLightOrigin() const = 0; }; typedef std::shared_ptr RendererLightPtr; inline std::ostream& operator<< (std::ostream& os, const RendererLight& l) { return os << "RendererLight { worldOrigin = " << l.worldOrigin() << " }"; } /** * \brief * Interface for an object which can test its intersection with a RendererLight. * * Objects which implement this interface define a intersectsLight() function * which determines whether the given light intersects the object. They also * provide methods to allow the renderer to provide the list of lights which * will be illuminating the object, subsequent to the intersection test. * * \todo * This interface seems to exist because of the design decision that lit objects * should maintain a list of lights which illuminate them. This is a poor * design because this should be the responsibility of the renderer. When the * renderer is refactored to process the scene light-by-light this class will * not be necessary. */ class LitObject { public: virtual ~LitObject() {} /// Test if the given light intersects the LitObject virtual bool intersectsLight(const RendererLight& light) const = 0; /// Add a light to the set of lights which do intersect this object virtual void insertLight(const RendererLight& light) {} /// Clear out all lights in the set of lights intersecting this object virtual void clearLights() {} }; typedef std::shared_ptr LitObjectPtr; class Renderable; typedef std::function RenderableCallback; typedef std::function RendererLightCallback; /** * \brief * A list of lights which may intersect an object * * A LightList is responsible for calculating which lights intersect a * particular object. Although there is nothing exposed in the interface, the * LightList holds a reference to a single lit object, and it is the * intersection with this object which is calculated. * * \internal * This interface doesn't really make any sense, and its purpose is not clear. * It seems to be basically a set of callback functions which need to be invoked * at the right time during the render process, but these shouldn't really be * the responsibility of anything outside the renderer itself. * * As of 2011-01-09/r6927 the calling sequence seems to be as follows: * * 1. Illuminated object (e.g. patch, brush) adds itself to the RenderSystem * with attachLitObject() at construction. * 2. attachLitObject() returns a reference to a (newly-created) LightList which * manages the lights intersecting this lit object. The lit object stores this * reference internally, while the LightList implementation also stores a * reference to the LitObject. * 3. When the lit object's renderSolid() method is invoked to set up a render, * it invokes LightList::calculateIntersectingLights() on the stored LightList * reference. * 4. calculateIntersectingLights() first checks to see if the lights need * updating, which is true if EITHER this LightList's setDirty() method OR the * RenderSystem's lightChanged() has been called since the last calculation. If * no update is needed, it returns. * 5. If an update IS needed, the LightList iterates over all lights in the * scene, and tests if each one intersects its associated lit object (which is * the one that just invoked calculateIntersectingLights(), although nothing * enforces this). This intersection test is performed by passing the light to * the LitObject::intersectsLight() method. * 6. For each light which passes the intersection test, the LightList both adds * it to its internal list of "active" (i.e. intersecting) lights for its * object, and passes it to the object's insertLight() method. Some object * classes then use insertLight() to populate another internal LightList subject * to additional (internal) intersection tests, but this is not required. * 7. At this point, calculateIntersectingLights() has finished, and returns * control to its calling renderSolid() method. * 8. The renderSolid() method (or another method it calls) passes a LightList * to the RenderableCollector with setLights(). The light list it passes may be * the original list returned from attachLitObject(), or the additional internal * list populated in step 6. * 9. The RenderableCollector state machine stores the LightList as the * "current" light list. * 10. Any subsequent renderables submitted with * RenderableCollector::addRenderable() are associated with the current * LightList passed in the previous step, and passed to the current Shader. * 11. The OpenGLShader accepts the renderable and LightList, and adds them to * its internal OpenGLShaderPasses: once only if RENDER_BUMP is not active, not * at all if RENDER_BUMP is active but the LightList is NULL, or once for each * light in the LightList otherwise. * 12. The OpenGLShaderPass now contains a list of TransformedRenderable * structures, each associating a single renderable with a single light. * Multiple TransformedRenderable will exist for the same renderable if there * were multiple lights illuminating it. */ class LightList { public: virtual ~LightList() {} /// Trigger the LightList to recalculate which lights intersect its object virtual void calculateIntersectingLights() const = 0; /// Set the dirty flag, informing the LightList that an update is required virtual void setDirty() = 0; /// Invoke a callback on all contained lights. virtual void forEachLight(const RendererLightCallback& callback) const = 0; }; const int c_attr_TexCoord0 = 1; const int c_attr_Tangent = 3; const int c_attr_Binormal = 4; /** * \brief * Data object passed to the backend OpenGLRenderable::render() method * containing information about the render pass which may be of use to * renderable objects, including the render flags and various * matrices/coordinates. */ class RenderInfo { // Render flags RenderStateFlags _flags; // Viewer location in 3D space Vector3 _viewerLocation; // Cube map mode ShaderLayer::CubeMapMode _cubeMapMode; public: /// Default constructor RenderInfo(RenderStateFlags flags = RENDER_DEFAULT, const Vector3& viewer = Vector3(0, 0, 0), ShaderLayer::CubeMapMode cubeMode = ShaderLayer::CUBE_MAP_NONE) : _flags(flags), _viewerLocation(viewer), _cubeMapMode(cubeMode) { } /// Check if a flag is set bool checkFlag(unsigned flag) const { return (_flags & flag) != 0; } /// Get the entire flag bitfield. RenderStateFlags getFlags() const { return _flags; } /// Get the viewer location. const Vector3& getViewerLocation() const { return _viewerLocation; } /// Get the cube map mode. ShaderLayer::CubeMapMode getCubeMapMode() const { return _cubeMapMode; } }; /** * \brief * Interface for objects which can render themselves in OpenGL. * * This interface is used by the render backend, after renderable objects have * first been submitted using the Renderable interface. The backend render() * function should contain the OpenGL calls necessary to submit vertex, normal * and texture-coordinate data. * * No GL state changes should occur in render(), other than those specifically * allowed by the render flags. */ class OpenGLRenderable { public: virtual ~OpenGLRenderable() {} /** * \brief * Submit OpenGL render calls. */ virtual void render(const RenderInfo& info) const = 0; }; class Matrix4; class Texture; class ModuleObserver; #include "math/Vector3.h" class Material; typedef std::shared_ptr MaterialPtr; /** * A Shader represents a single material which can be rendered in OpenGL, which * may correspond to an actual material (Material), a raw colour or a special * GL shader. * * Importantly, a Shader also maintains its own list of OpenGLRenderable objects * which use it -- the actual rendering is performed by traversing a list of * Shaders and rendering the geometry attached to each one. */ class Shader { public: virtual ~Shader() {} /** * Attach a renderable object to this Shader, which will be rendered using * this Shader when the render backend is activated. * * @param renderable * The OpenGLRenderable object to add. * * @param modelview * The modelview transform for this object. * * @param lights * A LightList containing all of the lights which should illuminate this * object. */ virtual void addRenderable(const OpenGLRenderable& renderable, const Matrix4& modelview, const LightList* lights = 0) = 0; /** * Like above, but taking an additional IRenderEntity argument. */ virtual void addRenderable(const OpenGLRenderable& renderable, const Matrix4& modelview, const IRenderEntity& entity, const LightList* lights = 0) = 0; /** * \brief * Control the visibility of this shader. * * A shader that is not visible will perform no rendering and ignore any * renderables submitted to it with addRenderable(). */ virtual void setVisible(bool visible) = 0; /// Query if this shader is visible virtual bool isVisible() const = 0; virtual void incrementUsed() = 0; virtual void decrementUsed() = 0; virtual void attach(ModuleObserver& observer) = 0; virtual void detach(ModuleObserver& observer) = 0; /** * \brief Retrieve the Material that was used to construct this shader (if * any). * * \return * An Material subclass with information about the shader definition */ virtual const MaterialPtr& getMaterial() const = 0; virtual unsigned int getFlags() const = 0; }; /** * Shared pointer typedef for Shader. */ typedef std::shared_ptr ShaderPtr; const std::string MODULE_RENDERSYSTEM("ShaderCache"); /** * \brief * The main interface for the backend renderer. */ class RenderSystem : public RegisterableModule { public: /** * \brief * Capture the given shader, increasing its reference count and * returning a pointer to the Shader object. * * The object must be freed after use by calling release(). * * @param name * The name of the shader to capture. * * @returns * Shader* object corresponding to the given material shader name. */ virtual ShaderPtr capture(const std::string& name) = 0; /** * \brief * Main render method. * * This method traverses all of the OpenGLRenderable objects that have been * submitted to Shader instances, and invokes their render() method to draw * their geometry. * * \param globalFlagsMask * The mask of render flags which are permitted during this render pass. Any * render flag which is 0 in this mask will not be enabled during rendering, * even if the particular shader requests it. * * \param modelview * The modelview transformation matrix to apply before rendering. * * \param projection * The view projection matrix to apply before rendering. * * \param viewer * Location of the viewer in world space. */ virtual void render(RenderStateFlags globalFlagsMask, const Matrix4& modelview, const Matrix4& projection, const Vector3& viewer = Vector3(0, 0, 0)) = 0; virtual void realise() = 0; virtual void unrealise() = 0; /** * Get the current render time in milliseconds. */ virtual std::size_t getTime() const = 0; /** * Set the render time in milliseconds. */ virtual void setTime(std::size_t milliSeconds) = 0; /* SHADER PROGRAMS */ /// Available GL programs used for backend rendering. enum ShaderProgram { /// No shader program (normal GL fixed-function pipeline) SHADER_PROGRAM_NONE, /// Lighting interaction shader SHADER_PROGRAM_INTERACTION }; /// Get the current shader program in use. virtual ShaderProgram getCurrentShaderProgram() const = 0; /// Set the shader program to use. virtual void setShaderProgram(ShaderProgram prog) = 0; /* LIGHT MANAGEMENT */ /** * \brief * Add a lit object to the renderer. * * The renderer will create and return a reference to a LightList associated * with this particular LitObject. The lit object can use the public * LightList interface to trigger a recalculation of light intersections, or * to set the dirty flag indicating to the LightList that a recalculation is * necessary. * * \internal * When the LightList implementation performs the intersection calculation, * it will use the LitObject's intersectsLight method to do so, and if the * intersection is detected, the insertLight method will be invoked on the * LitObject. This means that (1) the renderer stores a LightList for each * object, (2) the object itself has a reference to the LightList owned by * the renderer, and (3) the LitObject interface allows the object to * maintain ANOTHER list of intersecting lights, added with insertLight(). * This seems like a lot of indirection, but it might have something to do * with allowing objects with multiple sub-components to submit only a * subset of lights for each component. * * \param object * The lit object to add. * * \return * A reference to a LightList which manages the lights that intersect the * submitted object. */ virtual LightList& attachLitObject(LitObject& object) = 0; virtual void detachLitObject(LitObject& cullable) = 0; virtual void litObjectChanged(LitObject& cullable) = 0; /** * \brief * Attach a light source to the renderer. */ virtual void attachLight(RendererLight& light) = 0; /** * \brief * Detach a light source from the renderer. */ virtual void detachLight(RendererLight& light) = 0; /** * \brief * Indicate that the given light source has been modified. */ virtual void lightChanged(RendererLight& light) = 0; virtual void attachRenderable(const Renderable& renderable) = 0; virtual void detachRenderable(const Renderable& renderable) = 0; virtual void forEachRenderable(const RenderableCallback& callback) const = 0; // Initialises the OpenGL extensions virtual void extensionsInitialised() = 0; // Subscription to get notified as soon as the openGL extensions have been initialised virtual sigc::signal signal_extensionsInitialised() = 0; }; typedef std::shared_ptr RenderSystemPtr; typedef std::weak_ptr RenderSystemWeakPtr; /** * \brief * Global accessor method for the RenderSystem instance. */ inline RenderSystem& GlobalRenderSystem() { // Cache the reference locally static RenderSystem& _instance( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_RENDERSYSTEM) ) ); return _instance; } DarkRadiant-2.5.0/include/irenderable.h000066400000000000000000000123251321750546400177640ustar00rootroot00000000000000#pragma once #include class Shader; typedef std::shared_ptr ShaderPtr; class RenderSystem; typedef std::shared_ptr RenderSystemPtr; class OpenGLRenderable; class LightList; class Matrix4; class IRenderEntity; /** * \brief * Class which accepts OpenGLRenderable objects during the first pass of * rendering. * * Each Renderable in the scenegraph is passed a reference to a * RenderableCollector, on which the Renderable sets the necessary state * variables and then submits its OpenGLRenderable(s) for later rendering. A * single Renderable may submit more than one OpenGLRenderable, with a different * state each time -- for instance a Renderable model class may submit each of * its material surfaces separately with the respective shaders set beforehand. * * \todo * This class probably doesn't need to be a state machine, convert it to a * single submit method with necessary parameters. */ class RenderableCollector { public: virtual ~RenderableCollector() {} /** * Enumeration containing render styles. */ enum EStyle { eWireframeOnly, eFullMaterials }; /** * Push a Shader onto the internal shader stack. This is an OpenGL-style * push, which does not accept an argument but duplicates the topmost * stack value. The new value should be set with SetState(). */ virtual void PushState() = 0; /** * Pop the topmost Shader off the internal stack. This discards the value * without returning it. */ virtual void PopState() = 0; /** * Set the Shader to be used when rendering any subsequently-submitted * OpenGLRenderable object. This shader remains in effect until it is * changed with a subsequent call to SetState(). * * @param state * The Shader to be used from now on. * * @param mode * The type of rendering (wireframe or shaded) that this shader should be * used for. Individual RenderableCollector subclasses may ignore this method * call if it does not use the render mode they are interested in. */ virtual void SetState(const ShaderPtr& state, EStyle mode) = 0; /** * Submit an OpenGLRenderable object for rendering when the backend render * pass is conducted. The object will be rendered using the Shader previous- * ly set with SetState(). * * @param renderable * The renderable object to submit. * * @param world * The local to world transform that should be applied to this object when * it is rendered. */ virtual void addRenderable(const OpenGLRenderable& renderable, const Matrix4& world) = 0; /** * Like addRenderable() above but providing an additional IRenderEntity argument * needed to evaluate the shader expressions right before rendering. */ virtual void addRenderable(const OpenGLRenderable& renderable, const Matrix4& world, const IRenderEntity& entity) = 0; /** * \brief * Determine if this RenderableCollector can accept renderables for full * materials rendering, or just wireframe rendering. * * \return * true if full materials are supported, false if only wireframe rendering * is supported. */ virtual bool supportsFullMaterials() const = 0; struct Highlight { enum Flags { NoHighlight = 0, Faces = 1 << 0, /// Highlight faces of subsequently-submitted objects, if supported Primitives = 1 << 1, /// Highlight primitives of subsequently-submitted objects, if supported GroupMember = 1 << 2, /// Highlight as member of group, if supported }; }; virtual void setHighlightFlag(Highlight::Flags flags, bool enabled) = 0; /** * Set the list of lights to be used for lighting-mode rendering. This * method only makes sense for RenderableCollectors that support this * rendering mode. * * TODO: Use std::shared_ptr<> here. */ virtual void setLights(const LightList& lights) { } }; class VolumeTest; /** Interface class for Renderable objects. All objects which wish to be * rendered need to implement this interface. During the scenegraph traversal * for rendering, each Renderable object is passed a RenderableCollector object * which it can use to submit its geometry and state parameters. */ class Renderable { public: /** * Destructor */ virtual ~Renderable() {} /** * Sets the rendersystem this renderable is attached to. This is necessary * for this object to request Materials/Shaders for rendering. */ virtual void setRenderSystem(const RenderSystemPtr& renderSystem) = 0; /** Submit renderable geometry when rendering takes place in Solid mode. */ virtual void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const = 0; /** Submit renderable geometry when rendering takes place in Wireframe * mode. */ virtual void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const = 0; virtual void renderComponents(RenderableCollector&, const VolumeTest&) const { } virtual void viewChanged() const { } struct Highlight { enum Flags { NoHighlight = 0, Selected = 1 << 0, GroupMember = 1 << 1, }; }; /** * Returns information about whether the renderer should highlight this node and how. */ virtual std::size_t getHighlightFlags() = 0; }; typedef std::shared_ptr RenderablePtr; DarkRadiant-2.5.0/include/irendersystemfactory.h000066400000000000000000000021221321750546400217670ustar00rootroot00000000000000#ifndef _IRENDERSYSTEM_FACTORY_H_ #define _IRENDERSYSTEM_FACTORY_H_ #include "imodule.h" #include "irender.h" class RenderSystem; typedef std::shared_ptr RenderSystemPtr; namespace render { /** * greebo: The rendersystem factory can be used to generate * new instances of DarkRadiant's backend renderer. * * The backend renderer provides access to named Shader objects * which can be filled with OpenGLRenderable objects. */ class IRenderSystemFactory : public RegisterableModule { public: /** * Instantiates a new rendersystem. */ virtual RenderSystemPtr createRenderSystem() = 0; }; } // namespace const char* const MODULE_RENDERSYSTEMFACTORY = "RenderSystemFactory"; // Global accessor to the rendersystem factory module inline render::IRenderSystemFactory& GlobalRenderSystemFactory() { // Cache the reference locally static render::IRenderSystemFactory& _instance( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_RENDERSYSTEMFACTORY) ) ); return _instance; } #endif /* _IRENDERSYSTEM_FACTORY_H_ */ DarkRadiant-2.5.0/include/iresourcechooser.h000066400000000000000000000016041321750546400210710ustar00rootroot00000000000000#pragma once #include #include class wxWindow; namespace ui { /** * A Resource Chooser is a dialog for picking mod resources like * shaders, animations, sound shaders, particles, etc. * * The actual implementation varies a lot, but all resource pickers * share the ability to return the VFS path or name of the picked object * and to pre-select an item immediately after its shown. */ class IResourceChooser { public: virtual ~IResourceChooser() {} // Run the dialog and return the selected shader. The dialog will enter a // new event loop and block the UI until it's closed again. // The returned string will be empty if the user clicks cancel. virtual std::string chooseResource(const std::string& preselected = std::string()) = 0; // Destroys the window. Don't rely on the destructor, it won't call Destroy() for you. virtual void destroyDialog() = 0; }; }DarkRadiant-2.5.0/include/iscenegraph.h000066400000000000000000000106521321750546400200010ustar00rootroot00000000000000#pragma once #include #include "imodule.h" #include "inode.h" #include "ipath.h" #include "imap.h" #include /** * \defgroup scenegraph Scenegraph * * \namespace scene * \ingroup scenegraph * Interfaces and types relating to the scene-graph. */ // String identifier for the registry module const std::string MODULE_SCENEGRAPH("SceneGraph"); class VolumeTest; namespace scene { class ISpacePartitionSystem; typedef std::shared_ptr ISpacePartitionSystemPtr; /** * A scene-graph - a Directed Acyclic Graph (DAG). * * Each node may refer to zero or more 'child' nodes (directed). * A node may never have itself as one of its ancestors (acyclic). */ class Graph { public: /* greebo: Derive from this class to get notified on scene changes */ class Observer { public: // destructor virtual ~Observer() {} // Gets called when anything in the scenegraph changes virtual void onSceneGraphChange() {} // Gets called when a new is inserted into the scenegraph virtual void onSceneNodeInsert(const INodePtr& node) {} // Gets called when is removed from the scenegraph virtual void onSceneNodeErase(const INodePtr& node) {} }; // Returns the root-node of the graph. virtual const IMapRootNodePtr& root() const = 0; // Sets the root-node of the graph to be 'newRoot'. virtual void setRoot(const IMapRootNodePtr& newRoot) = 0; // greebo: Adds a node to the scenegraph virtual void insert(const scene::INodePtr& node) = 0; // Removes a node from the scenegraph virtual void erase(const scene::INodePtr& node) = 0; /// \brief Invokes all scene-changed callbacks. Called when any part of the scene changes the way it will appear when the scene is rendered. /// \todo Move to a separate class. virtual void sceneChanged() = 0; /// \brief Add a \p callback to be invoked when the scene changes. /// \todo Move to a separate class. virtual void addSceneObserver(Observer* observer) = 0; // greebo: Remove the scene observer from the list virtual void removeSceneObserver(Observer* observer) = 0; /// Accessor for the signal emitted when bounds are changed virtual sigc::signal signal_boundsChanged() const = 0; /// \brief Invokes all bounds-changed callbacks. Called when the bounds of any instance in the scene change. /// \todo Move to a separate class. virtual void boundsChanged() = 0; // A specific node has changed its bounds virtual void nodeBoundsChanged(const scene::INodePtr& node) = 0; // A walker class to be used in "foreachNodeInVolume" class Walker { public: virtual ~Walker() {} // Called for each visited node, returns TRUE if traversal should continue virtual bool visit(const INodePtr& node) = 0; }; // Visit each scene node in the given volume using the given walker class, even hidden ones virtual void foreachNodeInVolume(const VolumeTest& volume, Walker& walker) = 0; // Same as above, but culls any hidden nodes virtual void foreachVisibleNodeInVolume(const VolumeTest& volume, Walker& walker) = 0; // Call the functor on each scene node in the entire scenegraph, including hidden ones virtual void foreachNode(const INode::VisitorFunc& functor) = 0; // Call the functor on each scene node in the entire scenegraph, excluding hidden ones virtual void foreachVisibleNode(const INode::VisitorFunc& functor) = 0; // Call the functor on each scene node in the given volume, even hidden ones virtual void foreachNodeInVolume(const VolumeTest& volume, const INode::VisitorFunc& functor) = 0; // Same as above, but culls any hidden nodes virtual void foreachVisibleNodeInVolume(const VolumeTest& volume, const INode::VisitorFunc& functor) = 0; // Returns the associated spacepartition virtual ISpacePartitionSystemPtr getSpacePartition() = 0; }; typedef std::shared_ptr GraphPtr; typedef std::weak_ptr GraphWeakPtr; class Cloneable { public: /// \brief destructor virtual ~Cloneable() {} /// \brief Returns a copy of itself. virtual scene::INodePtr clone() const = 0; }; typedef std::shared_ptr CloneablePtr; } // namespace // Accessor to the singleton scenegraph, used for the main map inline scene::Graph& GlobalSceneGraph() { // Cache the reference locally static scene::Graph& _sceneGraph( *std::dynamic_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_SCENEGRAPH) ) ); return _sceneGraph; } inline void SceneChangeNotify() { GlobalSceneGraph().sceneChanged(); } DarkRadiant-2.5.0/include/iscenegraphfactory.h000066400000000000000000000017741321750546400213760ustar00rootroot00000000000000#pragma once #include "iscenegraph.h" namespace scene { /** * greebo: The scenegraph factory can be used to generate * new instances of DarkRadiant's main scene manager. * * A Scenegraph consists of [0..N] scene::INodes, forming * an acyclic graph. There is one main scenegraph in DarkRadiant * accessible through GlobalSceneGraph(), but it's possible to have * more than this one, used for preview scenes for example. */ class ISceneGraphFactory : public RegisterableModule { public: /** * Instantiates a new scenegraph. */ virtual GraphPtr createSceneGraph() = 0; }; } // namespace const char* const MODULE_SCENEGRAPHFACTORY = "SceneGraphFactory"; // Global accessor to the rendersystem factory module inline scene::ISceneGraphFactory& GlobalSceneGraphFactory() { // Cache the reference locally static scene::ISceneGraphFactory& _instance( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_SCENEGRAPHFACTORY) ) ); return _instance; } DarkRadiant-2.5.0/include/iscript.h000066400000000000000000000041121321750546400171600ustar00rootroot00000000000000#pragma once #include "imodule.h" // Forward-declare the stuff in namespace pybind11 { class module; class dict; } namespace py = pybind11; namespace script { struct ExecutionResult { // The output of the script std::string output; // whether an error occurred bool errorOccurred; }; typedef std::shared_ptr ExecutionResultPtr; } // namespace script class IScriptInterface { public: virtual ~IScriptInterface() {} /** * This method is called by the Scripting System to let this class * add its objects to the Python context. */ virtual void registerInterface(py::module& scope, py::dict& globals) = 0; }; typedef std::shared_ptr IScriptInterfacePtr; /** * DarkRadiant's Scripting System, based on pybind11. It's possible * to expose additional interfaces by using the addInterface() method. */ class IScriptingSystem : public RegisterableModule { public: /** * greebo: Add a named interface to the scripting system. The interface object * must provide a "registerInterface" method which will declare the names * and objects to the given namespace. */ virtual void addInterface(const std::string& name, const IScriptInterfacePtr& iface) = 0; /** * greebo: Executes the given python script file. The filename is specified relatively * to the scripts/ folder. */ virtual void executeScriptFile(const std::string& filename) = 0; /** * greebo: Interprets the given string as python script. * * @returns: the result object. */ virtual script::ExecutionResultPtr executeString(const std::string& scriptString) = 0; }; typedef std::shared_ptr IScriptingSystemPtr; // String identifier for the script module const char* const MODULE_SCRIPTING_SYSTEM("ScriptingSystem"); // This is the accessor for the scripting system inline IScriptingSystem& GlobalScriptingSystem() { // Cache the reference locally static IScriptingSystem& _scriptingSystem( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_SCRIPTING_SYSTEM) ) ); return _scriptingSystem; } DarkRadiant-2.5.0/include/iselectable.h000066400000000000000000000021311321750546400177560ustar00rootroot00000000000000#pragma once #include /** * greebo: A Selectable is everything that can be highlighted * by the user in the scene (e.g. by interaction with the mouse). */ class ISelectable { public: // destructor virtual ~ISelectable() {} // Set the selection status of this object virtual void setSelected(bool select) = 0; // Check the selection status of this object (TRUE == selected) virtual bool isSelected() const = 0; }; typedef std::shared_ptr ISelectablePtr; namespace scene { class INode; typedef std::shared_ptr INodePtr; } inline ISelectablePtr Node_getSelectable(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } inline void Node_setSelected(const scene::INodePtr& node, bool selected) { ISelectablePtr selectable = Node_getSelectable(node); if (selectable) { selectable->setSelected(selected); } } inline bool Node_isSelected(const scene::INodePtr& node) { ISelectablePtr selectable = Node_getSelectable(node); if (selectable) { return selectable->isSelected(); } return false; } DarkRadiant-2.5.0/include/iselection.h000066400000000000000000000233511321750546400176470ustar00rootroot00000000000000#pragma once #include #include "imodule.h" #include "ivolumetest.h" #include #include class RenderableCollector; namespace render { class View; } class ISelectable; namespace scene { class INode; typedef std::shared_ptr INodePtr; } template class BasicVector2; typedef BasicVector2 Vector2; template class BasicVector3; typedef BasicVector3 Vector3; template class BasicVector4; typedef BasicVector4 Vector4; class Matrix4; class Quaternion; typedef sigc::signal SelectionChangedSignal; typedef sigc::slot SelectionChangedSlot; class SelectionInfo; class Face; class Brush; class Patch; namespace selection { struct WorkZone; /** * A Manipulator is a renderable object which contains one or more * ManipulatorComponents, each of which can be manipulated by the user. For * example, the rotation Manipulator draws several circles which cause rotations * around specific axes. */ class Manipulator { public: // Manipulator type enum, user-defined manipulators should return "Custom" enum Type { Drag, Translate, Rotate, Scale, Clip, ModelScale, Custom }; /** * Part of a Manipulator which can be operated upon by the user. * * \see Manipulator */ class Component { public: virtual ~Component() {} /** * Called when the user successfully activates this component. The calling code provides * information about the view we're operating in, the starting device coords and the * location of the current selection pivot. */ virtual void beginTransformation(const Matrix4& pivot2world, const VolumeTest& view, const Vector2& devicePoint) = 0; struct Constraint { enum Flags { Unconstrained = 0, // no keyboard modifier held Type1 = 1 << 0, // usually: shift held down Grid = 1 << 1, // usually: ctrl NOT held down Type3 = 1 << 2, // usually: alt held down }; }; /** * Called during mouse movement, the component is asked to calculate the deltas and distances * it needs to perform the translation/rotation/scale/whatever the operator does on the selected objects. * The pivot2world transform relates to the original pivot location at the time the transformation started. * If the constrained flags are not 0, they indicate the user is holding down a key during movement, * usually the SHIFT or CTRL key. It's up to the component to decide how to handle the constraint. */ virtual void transform(const Matrix4& pivot2world, const VolumeTest& view, const Vector2& devicePoint, unsigned int flags) = 0; }; virtual ~Manipulator() {} // ID and Type management virtual std::size_t getId() const = 0; virtual void setId(std::size_t id) = 0; virtual Type getType() const = 0; /** * Get the currently-active ManipulatorComponent. This is determined by the * most recent selection test. */ virtual Component* getActiveComponent() = 0; virtual void testSelect(const render::View& view, const Matrix4& pivot2world) {} // Renders the manipulator's visual representation to the scene virtual void render(RenderableCollector& collector, const VolumeTest& volume) = 0; virtual void setSelected(bool select) = 0; virtual bool isSelected() const = 0; // Manipulators should indicate whether component editing is supported or not virtual bool supportsComponentManipulation() const = 0; }; typedef std::shared_ptr ManipulatorPtr; } class SelectionSystem : public RegisterableModule { public: enum EModifier { eManipulator, // greebo: This is the standard case (drag, click without modifiers) eToggle, // This is for Shift-Clicks to toggle the selection of an instance eReplace, // This is active if the mouse is moved to a NEW location and Alt-Shift is held eCycle, // This is active if the mouse STAYS at the same position and Alt-Shift is held }; enum EMode { eEntity, ePrimitive, eGroupPart, eComponent, }; // The possible modes when in "component manipulation mode" enum EComponentMode { eDefault, eVertex, eEdge, eFace, }; /** greebo: An SelectionSystem::Observer gets notified * as soon as the selection is changed. */ class Observer { public: virtual ~Observer() {} /** greebo: This gets called upon selection change. * * @instance: The instance that got affected (this may also be the parent brush of a FaceInstance). * @isComponent: is TRUE if the changed selectable is a component (like a FaceInstance, VertexInstance). */ virtual void selectionChanged(const scene::INodePtr& node, bool isComponent) = 0; }; virtual void addObserver(Observer* observer) = 0; virtual void removeObserver(Observer* observer) = 0; // Returns the ID of the registered manipulator virtual std::size_t registerManipulator(const selection::ManipulatorPtr& manipulator) = 0; virtual void unregisterManipulator(const selection::ManipulatorPtr& manipulator) = 0; virtual selection::Manipulator::Type getActiveManipulatorType() = 0; // Returns the currently active Manipulator, which is always non-null virtual const selection::ManipulatorPtr& getActiveManipulator() = 0; virtual void setActiveManipulator(std::size_t manipulatorId) = 0; virtual void setActiveManipulator(selection::Manipulator::Type manipulatorType) = 0; virtual const SelectionInfo& getSelectionInfo() = 0; virtual void SetMode(EMode mode) = 0; virtual EMode Mode() const = 0; virtual void SetComponentMode(EComponentMode mode) = 0; virtual EComponentMode ComponentMode() const = 0; virtual std::size_t countSelected() const = 0; virtual std::size_t countSelectedComponents() const = 0; virtual void onSelectedChanged(const scene::INodePtr& node, const ISelectable& selectable) = 0; virtual void onComponentSelection(const scene::INodePtr& node, const ISelectable& selectable) = 0; virtual scene::INodePtr ultimateSelected() = 0; virtual scene::INodePtr penultimateSelected() = 0; /** * \brief * Set the selection status of all objects in the scene. * * \param selected * true to select all objects, false to deselect all objects. */ virtual void setSelectedAll(bool selected) = 0; virtual void setSelectedAllComponents(bool selected) = 0; /** * @brief * Visitor interface the for the selection system. * * This defines the Visitor interface which is used in the foreachSelected() * and foreachSelectedComponent() visit methods. */ class Visitor { public: virtual ~Visitor() {} /** * @brief * Called by the selection system for each visited node. */ virtual void visit(const scene::INodePtr& node) const = 0; }; /** * @brief * Use the provided Visitor object to enumerate each selected node. */ virtual void foreachSelected(const Visitor& visitor) = 0; /** * @brief * Use the provided Visitor object to enumerate each selected component. */ virtual void foreachSelectedComponent(const Visitor& visitor) = 0; /** * Call the given functor to enumerate each selected node. */ virtual void foreachSelected(const std::function& functor) = 0; /** * @brief * Use the provided functor to enumerate each selected component. */ virtual void foreachSelectedComponent(const std::function& functor) = 0; /** * Call the given functor for each selected brush. Selected group nodes like func_statics * are traversed recursively, invoking the functor for each visible brush in question. */ virtual void foreachBrush(const std::function& functor) = 0; /** * Call the given functor for each selected face. Selected group nodes like func_statics * are traversed recursively, invoking the functor for each visible face in question. * Singly selected faces (those which have been selected in component mode) are * considered as well by this method. */ virtual void foreachFace(const std::function& functor) = 0; /** * Call the given functor for each selected patch. Selected group nodes like func_statics * are traversed recursively, invoking the functor for each visible patch in question. */ virtual void foreachPatch(const std::function& functor) = 0; /// Signal emitted when the selection is changed virtual SelectionChangedSignal signal_selectionChanged() const = 0; virtual const Matrix4& getPivot2World() = 0; virtual void pivotChanged() = 0; // Feedback events invoked by the ManipulationMouseTool virtual void onManipulationStart() = 0; virtual void onManipulationChanged() = 0; virtual void onManipulationEnd() = 0; virtual void SelectPoint(const render::View& view, const Vector2& devicePoint, const Vector2& deviceEpsilon, EModifier modifier, bool face) = 0; virtual void SelectArea(const render::View& view, const Vector2& devicePoint, const Vector2& deviceDelta, EModifier modifier, bool face) = 0; /** * Returns the current "work zone", which is defined by the * currently selected elements. Each time a scene node is selected, * the workzone is adjusted to surround the current selection. * Deselecting nodes doesn't change the workzone. * * The result is used to determine the "third" component of operations * performed in the 2D views, like placing an entity. * * Note: the struct is defined in selectionlib.h. */ virtual const selection::WorkZone& getWorkZone() = 0; }; const char* const MODULE_SELECTIONSYSTEM("SelectionSystem"); inline SelectionSystem& GlobalSelectionSystem() { // Cache the reference locally static SelectionSystem& _selectionSystem( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_SELECTIONSYSTEM) ) ); return _selectionSystem; } DarkRadiant-2.5.0/include/iselectiongroup.h000066400000000000000000000100611321750546400207160ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "iselectable.h" #include // GroupSelectables are regular selectables which can be part of // one or more SelectionGroups. class IGroupSelectable : public ISelectable { public: typedef std::vector GroupIds; virtual ~IGroupSelectable() {} // Adds this item to the group specified by its ID virtual void addToGroup(std::size_t groupId) = 0; // Removes this item from the group specified by its ID virtual void removeFromGroup(std::size_t groupId) = 0; // Returns true if this node is member of any group virtual bool isGroupMember() = 0; // Returns the group this node has been added to last // This represents the currently "active" group ID // Will throw an exception if this node is not a member of any group virtual std::size_t getMostRecentGroupId() = 0; // Returns all group assignments of this node // The most recently added group is at the back of the list virtual const GroupIds& getGroupIds() = 0; // Special overload to control whether this selectable should propagate // the status change to the group it belongs to. virtual void setSelected(bool select, bool changeGroupStatus) = 0; }; namespace selection { // Represents a SelectionGroup which can contain 0 or more IGroupSelectable nodes. class ISelectionGroup { public: virtual ~ISelectionGroup() {} // Returns the ID of this group virtual std::size_t getId() const = 0; // Gets the name of this group virtual const std::string& getName() const = 0; // Sets the name of this group virtual void setName(const std::string& name) = 0; // Adds the given node to this group. The node should be a IGroupSelectable // which will be checked internally. If the node is not matching, nothing happens. virtual void addNode(const scene::INodePtr& node) = 0; // Remvoes the given node from this group. The node should be a IGroupSelectable // which will be checked internally. If the node is not matching, nothing happens. // The group will not be removed if this was the last member node virtual void removeNode(const scene::INodePtr& node) = 0; // Returns the number of nodes in this group virtual std::size_t size() const = 0; // Sets the selection status of all the nodes in this group virtual void setSelected(bool selected) = 0; // Calls the given functor for each node in this group. // The functor should not change the membership of this group, this will likely lead // to internal iterator corruption. virtual void foreachNode(const std::function& functor) = 0; }; typedef std::shared_ptr ISelectionGroupPtr; class ISelectionGroupManager : public RegisterableModule { public: virtual ~ISelectionGroupManager() {} // Creates a new selection group. The group is stored within the SelectionGroupManager // so the returned shared_ptr can safely be let go by the client code. // In the pathological case of being run out of IDs this will throw a std::runtime_error virtual ISelectionGroupPtr createSelectionGroup() = 0; // Tries to get a selection group by ID. Returns an empty ptr if the ID doesn't exist virtual ISelectionGroupPtr getSelectionGroup(std::size_t id) = 0; // Unline getSelectionGroup() this will create the group if it doesn't exist virtual ISelectionGroupPtr findOrCreateSelectionGroup(std::size_t id) = 0; // Sets the selection status of all members of the given group virtual void setGroupSelected(std::size_t id, bool selected) = 0; // Deletes all selection groups virtual void deleteAllSelectionGroups() = 0; // Deletes the group with the given ID. All nodes will be removed from this group as well. virtual void deleteSelectionGroup(std::size_t id) = 0; }; } // namespace const char* const MODULE_SELECTIONGROUP = "SelectionGroupManager"; inline selection::ISelectionGroupManager& GlobalSelectionGroupManager() { // Cache the reference locally static selection::ISelectionGroupManager& _manager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_SELECTIONGROUP) ) ); return _manager; } DarkRadiant-2.5.0/include/iselectionset.h000066400000000000000000000052231321750546400203610ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "inode.h" #include #include namespace selection { class ISelectionSet { public: // The name of this set virtual const std::string& getName() = 0; // Checks whether this set is empty virtual bool empty() = 0; // Selects all member nodes of this set virtual void select() = 0; // De-selects all member nodes of this set virtual void deselect() = 0; // Removes all members, leaving this set as empty virtual void clear() = 0; // Clears this set and loads the currently selected nodes in the // scene as new members into this set. virtual void assignFromCurrentScene() = 0; // Adds the given node to this set virtual void addNode(const scene::INodePtr& node) = 0; // Returns the nodes contained in this selection set. virtual std::set getNodes() = 0; }; typedef std::shared_ptr ISelectionSetPtr; class ISelectionSetManager : public RegisterableModule { public: /** * Signal emitted when the list of selection sets has been changed, by * deletion or addition. */ virtual sigc::signal signal_selectionSetsChanged() const = 0; class Visitor { public: virtual void visit(const ISelectionSetPtr& set) = 0; }; /** * greebo: Traverses the list of selection sets using * the given visitor class. */ virtual void foreachSelectionSet(Visitor& visitor) = 0; typedef std::function VisitorFunc; /** * greebo: Traverses the list of selection sets using the given functor. */ virtual void foreachSelectionSet(const VisitorFunc& functor) = 0; /** * greebo: Creates a new selection set with the given name. * If a selection with that name is already registered, the existing * one is returned. */ virtual ISelectionSetPtr createSelectionSet(const std::string& name) = 0; /** * Removes the named selection set. If the named set is * not existing, nothing happens. */ virtual void deleteSelectionSet(const std::string& name) = 0; /** * Deletes all sets. */ virtual void deleteAllSelectionSets() = 0; /** * Finds the named selection set. * * @returns the found selection set or NULL if the set is not existent. */ virtual ISelectionSetPtr findSelectionSet(const std::string& name) = 0; }; } // namespace const char* const MODULE_SELECTIONSET = "SelectionSetManager"; inline selection::ISelectionSetManager& GlobalSelectionSetManager() { // Cache the reference locally static selection::ISelectionSetManager& _manager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_SELECTIONSET) ) ); return _manager; } DarkRadiant-2.5.0/include/iselectiontest.h000066400000000000000000000175241321750546400205540ustar00rootroot00000000000000#pragma once #include #include "math/FloatTools.h" #include "inode.h" #include "iselection.h" #include #include class SelectionIntersection { private: float _depth; float _distance; public: SelectionIntersection() : _depth(1), _distance(2) {} SelectionIntersection(float depth, float distance) : _depth(depth), _distance(distance) {} bool operator<(const SelectionIntersection& other) const { if (_distance != other._distance) { return _distance < other._distance; } if (_depth != other._depth) { return _depth < other._depth; } return false; } bool equalEpsilon(const SelectionIntersection& other, float distanceEpsilon, float depthEpsilon) const { return float_equal_epsilon(_distance, other._distance, distanceEpsilon) && float_equal_epsilon(_depth, other._depth, depthEpsilon); } float depth() const { return _depth; } bool isValid() const { return depth() < 1; } // returns true if self is closer than other bool isCloserThan(const SelectionIntersection& other) const { return *this < other; } // assigns other to *this if other is closer than *this void assignIfCloser(const SelectionIntersection& other) { if (other.isCloserThan(*this)) { *this = other; } } }; /** * greebo: A helper class allowing to traverse a sequence of Vector3 * objects in memory. The Vector3 objects can have a certain distance * (stride) which is passed to the constructor. Incrementing the contained * iterator object moves from one Vector3 to the next in memory. */ class VertexPointer { typedef const unsigned char* byte_pointer; public: typedef const Vector3* vector_pointer; typedef const Vector3& vector_reference; class iterator { public: iterator() {} iterator(byte_pointer vertices, std::size_t stride) : m_iter(vertices), m_stride(stride) {} bool operator==(const iterator& other) const { return m_iter == other.m_iter; } bool operator!=(const iterator& other) const { return !operator==(other); } iterator operator+(std::size_t i) { return iterator(m_iter + i * m_stride, m_stride); } iterator operator+=(std::size_t i) { m_iter += i * m_stride; return *this; } iterator& operator++() { m_iter += m_stride; return *this; } iterator operator++(int) { iterator tmp = *this; m_iter += m_stride; return tmp; } vector_reference operator*() const { return *reinterpret_cast(m_iter); } private: byte_pointer m_iter; std::size_t m_stride; }; VertexPointer(vector_pointer vertices, std::size_t stride) : m_vertices(reinterpret_cast(vertices)), m_stride(stride) {} iterator begin() const { return iterator(m_vertices, m_stride); } vector_reference operator[](std::size_t i) const { return *reinterpret_cast(m_vertices + m_stride*i); } private: // The address of the first Vector3 object byte_pointer m_vertices; // The distance (in bytes) to the next object in memory std::size_t m_stride; }; class IndexPointer { public: typedef unsigned int index_type; typedef const index_type* pointer; class iterator { public: iterator(pointer iter) : m_iter(iter) {} bool operator==(const iterator& other) const { return m_iter == other.m_iter; } bool operator!=(const iterator& other) const { return !operator==(other); } iterator operator+(std::size_t i) { return m_iter + i; } iterator operator+=(std::size_t i) { return m_iter += i; } iterator operator++() { return ++m_iter; } iterator operator++(int) { return m_iter++; } const index_type& operator*() const { return *m_iter; } private: void increment() { ++m_iter; } pointer m_iter; }; IndexPointer(pointer indices, std::size_t count) : m_indices(indices), m_finish(indices + count) {} iterator begin() const { return m_indices; } iterator end() const { return m_finish; } private: pointer m_indices; pointer m_finish; }; template class BasicVector3; typedef BasicVector3 Vector3; class Matrix4; class VolumeTest; class SelectionTest { public: virtual ~SelectionTest() {} virtual void BeginMesh(const Matrix4& localToWorld, bool twoSided = false) = 0; virtual const VolumeTest& getVolume() const = 0; virtual const Vector3& getNear() const = 0; virtual const Vector3& getFar() const = 0; virtual void TestPoint(const Vector3& point, SelectionIntersection& best) = 0; virtual void TestPolygon(const VertexPointer& vertices, std::size_t count, SelectionIntersection& best) = 0; virtual void TestLineLoop(const VertexPointer& vertices, std::size_t count, SelectionIntersection& best) = 0; virtual void TestLineStrip(const VertexPointer& vertices, std::size_t count, SelectionIntersection& best) = 0; virtual void TestLines(const VertexPointer& vertices, std::size_t count, SelectionIntersection& best) = 0; virtual void TestTriangles(const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best) = 0; virtual void TestQuads(const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best) = 0; virtual void TestQuadStrip(const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best) = 0; }; typedef std::shared_ptr SelectionTestPtr; class Selector { public: virtual ~Selector() {} virtual void pushSelectable(ISelectable& selectable) = 0; virtual void popSelectable() = 0; virtual void addIntersection(const SelectionIntersection& intersection) = 0; }; inline void Selector_add(Selector& selector, ISelectable& selectable) { selector.pushSelectable(selectable); selector.addIntersection(SelectionIntersection(0, 0)); selector.popSelectable(); } inline void Selector_add(Selector& selector, ISelectable& selectable, const SelectionIntersection& intersection) { selector.pushSelectable(selectable); selector.addIntersection(intersection); selector.popSelectable(); } class VolumeTest; class SelectionTestable { public: virtual ~SelectionTestable() {} virtual void testSelect(Selector& selector, SelectionTest& test) = 0; }; typedef std::shared_ptr SelectionTestablePtr; inline SelectionTestablePtr Node_getSelectionTestable(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } class ComponentSelectionTestable { public: virtual ~ComponentSelectionTestable() {} virtual bool isSelectedComponents() const = 0; virtual void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode) = 0; virtual void invertSelectedComponents(SelectionSystem::EComponentMode mode) = 0; virtual void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) = 0; }; typedef std::shared_ptr ComponentSelectionTestablePtr; inline ComponentSelectionTestablePtr Node_getComponentSelectionTestable(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } class Plane3; typedef std::function PlaneCallback; class SelectedPlanes { public: virtual ~SelectedPlanes() {} virtual bool contains(const Plane3& plane) const = 0; }; class PlaneSelectable { public: virtual ~PlaneSelectable() {} virtual void selectPlanes(Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback) = 0; virtual void selectReversedPlanes(Selector& selector, const SelectedPlanes& selectedPlanes) = 0; }; typedef std::shared_ptr PlaneSelectablePtr; inline PlaneSelectablePtr Node_getPlaneSelectable(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } DarkRadiant-2.5.0/include/ishaderexpression.h000066400000000000000000000041701321750546400212460ustar00rootroot00000000000000#pragma once #include class IRenderEntity; namespace shaders { // Material registers, used to store shader expression results // The first two slots are always reserved for the constants 0 and 1, see enum ReservedRegisters typedef std::vector Registers; // The indices to the constants in the registers array enum ReservedRegisters { REG_ZERO = 0, REG_ONE = 1, NUM_RESERVED_REGISTERS, }; /** * A shader expression is something found in a Doom 3 material declaration, * where things like shader parameters, time and constants can be combined * by mathematical operators and table-lookups. A shader expression can be * a constant number in its simplest form, or a complex formula like this: * * vertexParm 0 0.1 * sintable[time * 0.3], 0.15 * sintable[time * 0.15] * * The above makes vertex parameter 0 a time-dependent value which is evaluated * each frame during rendering. * * A shader expression can be evaluated which results in a single floating point * value. In actual materials the shader expression is linked to a material register * where the value is written to after evaluation. */ class IShaderExpression { public: /** * Retrieve the floating point value of this expression. DEPRECATED */ virtual float getValue(std::size_t time) = 0; /** * Retrieve the floating point value of this expression. */ virtual float getValue(std::size_t time, const IRenderEntity& entity) = 0; /** * Evaluates the value of this expression, writing any results * into the linked material register. DEPRECATED */ virtual float evaluate(std::size_t time) = 0; /** * Evaluates the value of this expression, writing any results * into the linked material register. */ virtual float evaluate(std::size_t time, const IRenderEntity& entity) = 0; /** * Link the expression to the given Registers vector. * Calling evaluate() will cause the result to be saved into the register. * * @returns: the register position the result will be written to. */ virtual std::size_t linkToRegister(Registers& registers) = 0; }; typedef std::shared_ptr IShaderExpressionPtr; } // namespace DarkRadiant-2.5.0/include/ishaders.h000066400000000000000000000302271321750546400173130ustar00rootroot00000000000000#pragma once #include "iimage.h" #include "imodule.h" #include #include "math/Vector3.h" #include "math/Vector4.h" #include #include #include "Texture.h" #include "ShaderLayer.h" #include "ishaderexpression.h" class Image; // Forward declaration namespace shaders { class MapExpression; typedef std::shared_ptr MapExpressionPtr; } // namespace shaders /** * \brief * Interface for a material shader. * * A material shader consists of global parameters, an editor image, and zero or * more shader layers (including diffusemap, bumpmap and specularmap textures * which are handled specially). */ class Material { public: enum CullType { CULL_BACK, // default backside culling, for materials without special flags CULL_FRONT, // "backSided" CULL_NONE, // "twoSided" }; // Global material flags enum Flags { FLAG_NOSHADOWS = 1 << 0, // noShadows FLAG_NOSELFSHADOW = 1 << 1, // noSelfShadow FLAG_FORCESHADOWS = 1 << 2, // forceShadows FLAG_NOOVERLAYS = 1 << 3, // noOverlays FLAG_FORCEOVERLAYS = 1 << 4, // forceOverlays FLAG_TRANSLUCENT = 1 << 5, // translucent FLAG_FORCEOPAQUE = 1 << 6, // forceOpaque FLAG_NOFOG = 1 << 7, // noFog FLAG_NOPORTALFOG = 1 << 8, // noPortalFog FLAG_UNSMOOTHEDTANGENTS = 1 << 9, // unsmoothedTangents FLAG_MIRROR = 1 << 10, // mirror }; // Surface Flags enum SurfaceFlags { SURF_SOLID = 1 << 0, SURF_OPAQUE = 1 << 1, SURF_WATER = 1 << 2, SURF_PLAYERCLIP = 1 << 3, SURF_MONSTERCLIP = 1 << 4, SURF_MOVEABLECLIP = 1 << 5, SURF_IKCLIP = 1 << 6, SURF_BLOOD = 1 << 7, SURF_TRIGGER = 1 << 8, SURF_AASSOLID = 1 << 9, SURF_AASOBSTACLE = 1 << 10, SURF_FLASHLIGHT_TRIGGER = 1 << 11, SURF_NONSOLID = 1 << 12, SURF_NULLNORMAL = 1 << 13, SURF_AREAPORTAL = 1 << 14, SURF_NOCARVE = 1 << 15, SURF_DISCRETE = 1 << 16, SURF_NOFRAGMENT = 1 << 17, SURF_SLICK = 1 << 18, SURF_COLLISION = 1 << 19, SURF_NOIMPACT = 1 << 20, SURF_NODAMAGE = 1 << 21, SURF_LADDER = 1 << 22, SURF_NOSTEPS = 1 << 23, SURF_ENTITYGUI = 1 << 24, }; // Surface Type (plastic, stone, etc.) enum SurfaceType { SURFTYPE_DEFAULT, SURFTYPE_METAL, SURFTYPE_STONE, SURFTYPE_FLESH, SURFTYPE_WOOD, SURFTYPE_CARDBOARD, SURFTYPE_LIQUID, SURFTYPE_GLASS, SURFTYPE_PLASTIC, SURFTYPE_RICOCHET, SURFTYPE_AASOBSTACLE, SURFTYPE_10, SURFTYPE_11, SURFTYPE_12, SURFTYPE_13, SURFTYPE_14, SURFTYPE_15 }; /** * \brief * Requested sort position from material declaration (e.g. "sort decal"). * The actual sort order of a material is stored as a floating point number, * these enumerations represent some regularly used shortcuts in material decls. * The values of this enum have been modeled after the ones found in the D3 SDK. */ enum SortRequest { SORT_SUBVIEW = -3, // mirrors, viewscreens, etc SORT_GUI = -2, // guis SORT_BAD = -1, SORT_OPAQUE, // opaque SORT_PORTAL_SKY, SORT_DECAL, // scorch marks, etc. SORT_FAR, SORT_MEDIUM, // normal translucent SORT_CLOSE, SORT_ALMOST_NEAREST, // gun smoke puffs SORT_NEAREST, // screen blood blobs SORT_POST_PROCESS = 100 // after a screen copy to texture }; // Deform Type enum DeformType { DEFORM_NONE, DEFORM_SPRITE, DEFORM_TUBE, DEFORM_FLARE, DEFORM_EXPAND, DEFORM_MOVE, DEFORM_TURBULENT, DEFORM_EYEBALL, DEFORM_PARTICLE, DEFORM_PARTICLE2, }; struct DecalInfo { int stayMilliSeconds; int fadeMilliSeconds; Vector4 startColour; Vector4 endColour; }; enum Coverage { MC_UNDETERMINED, MC_OPAQUE, // completely fills the triangle, will have black drawn on fillDepthBuffer MC_PERFORATED, // may have alpha tested holes MC_TRANSLUCENT // blended with background }; virtual ~Material() {} /** * \brief * Return the editor image texture for this shader. */ virtual TexturePtr getEditorImage() = 0; /** * \brief * Return true if the editor image is no tex for this shader. */ virtual bool isEditorImageNoTex() = 0; /** * \brief * Get the string name of this shader. */ virtual std::string getName() const = 0; virtual bool IsInUse() const = 0; virtual void SetInUse(bool bInUse) = 0; // test if it's a true shader, or a default shader created to wrap around a texture virtual bool IsDefault() const = 0; // get shader file name (ie the file where this one is defined) virtual const char* getShaderFileName() const = 0; /** * \brief * Return the requested sort position of this material. * greebo: D3 is using floating points for the sort value but * as far as I can see only rounded numbers have been used. */ virtual int getSortRequest() const = 0; /** * \brief * Return a polygon offset if one is defined. The default is 0. */ virtual float getPolygonOffset() const = 0; /** * Get the desired texture repeat behaviour. */ virtual ClampType getClampType() const = 0; // Get the cull type (none, back, front) virtual CullType getCullType() const = 0; /** * Get the global material flags (translucent, noshadows, etc.) */ virtual int getMaterialFlags() const = 0; /** * Surface flags (areaportal, nonsolid, etc.) */ virtual int getSurfaceFlags() const = 0; /** * Surface Type (wood, stone, surfType15, ...) */ virtual SurfaceType getSurfaceType() const = 0; /** * Get the deform type of this material */ virtual DeformType getDeformType() const = 0; /** * Returns the spectrum of this shader, -1 means "no defined spectrum" */ virtual int getSpectrum() const = 0; /** * Retrieves the decal info structure of this material. */ virtual const DecalInfo& getDecalInfo() const = 0; /** * Returns the coverage type of this material, also needed * by the map compiler. */ virtual Coverage getCoverage() const = 0; /** * Returns the raw shader definition block, as parsed by the material manager. * The definition is lacking the outermost curly braces. */ virtual std::string getDefinition() = 0; /** Determine whether this is an ambient light shader, i.e. the * material def contains the global "ambientLight" keyword. */ virtual bool isAmbientLight() const = 0; /** Determine whether this is an blend light shader, i.e. the * material def contains the global "blendLight" keyword. */ virtual bool isBlendLight() const = 0; /** Determine whether this is an fog light shader, i.e. the * material def contains the global "fogLight" keyword. */ virtual bool isFogLight() const = 0; /** * For light shaders: implicitly no-shadows lights (ambients, fogs, etc) * will never cast shadows but individual light entities can also override this value. */ virtual bool lightCastsShadows() const = 0; // returns true if the material will generate shadows, not making a // distinction between global and no-self shadows virtual bool surfaceCastsShadow() const = 0; /** * returns true if the material will draw anything at all. Triggers, portals, * etc, will not have anything to draw. A not drawn surface can still castShadow, * which can be used to make a simplified shadow hull for a complex object set as noShadow. */ virtual bool isDrawn() const = 0; /** * a discrete surface will never be merged with other surfaces by dmap, which is * necessary to prevent mutliple gui surfaces, mirrors, autosprites, and some other * special effects from being combined into a single surface * guis, merging sprites or other effects, mirrors and remote views are always discrete */ virtual bool isDiscrete() const = 0; virtual ShaderLayer* firstLayer() const = 0; /** * \brief * Return a std::vector containing all layers in this material shader. * * This includes all diffuse, bump, specular or blend layers. */ virtual const ShaderLayerVector& getAllLayers() const = 0; virtual TexturePtr lightFalloffImage() = 0; // greebo: Returns the description as defined in the material virtual std::string getDescription() const = 0; /** * greebo: Returns TRUE if the shader is visible, FALSE if it * is filtered or disabled in any other way. */ virtual bool isVisible() const = 0; /** * greebo: Sets the visibility of this shader. */ virtual void setVisible(bool visible) = 0; }; typedef std::shared_ptr MaterialPtr; /** * Stream insertion of Material for debugging purposes. */ inline std::ostream& operator<< (std::ostream& os, const Material& shader) { os << "Material { name = " << shader.getName() << ", filename = " << shader.getShaderFileName() << ", firstlayer = " << shader.firstLayer() << " }"; return os; } typedef std::function ShaderNameCallback; class ModuleObserver; const char* const MODULE_SHADERSYSTEM = "MaterialManager"; /** * \brief * Interface for the material manager. * * The material manager parses all of the MTR declarations and provides access * to Material objects representing the loaded materials. */ class MaterialManager : public RegisterableModule { public: // NOTE: shader and texture names used must be full path. // Shaders usable as textures have prefix equal to getTexturePrefix() virtual void realise() = 0; virtual void unrealise() = 0; virtual void refresh() = 0; /** Determine whether the shader system is realised. This may be used * by components which need to ensure the shaders are realised before * they start trying to display them. * * @returns * true if the shader system is realised, false otherwise */ virtual bool isRealised() = 0; /** Activate the shader for a given name and return it. The default shader * will be returned if name is not found. * * @param name * The text name of the shader to load. * * @returns * MaterialPtr shared ptr corresponding to the named shader object. */ virtual MaterialPtr getMaterialForName(const std::string& name) = 0; /** * greebo: Returns true if the named material is existing, false otherwise. * In the latter case getMaterialForName() would return a default "shader not found". */ virtual bool materialExists(const std::string& name) = 0; virtual void foreachShaderName(const ShaderNameCallback& callback) = 0; /** * Visit each material with the given function object. Replaces the legacy foreachShader(). */ virtual void foreachMaterial(const std::function& func) = 0; // Set the callback to be invoked when the active shaders list has changed virtual sigc::signal signal_activeShadersChanged() const = 0; /** * Enable or disable active shaders updates (for performance). */ virtual void setActiveShaderUpdates(bool val) = 0; virtual void attach(ModuleObserver& observer) = 0; virtual void detach(ModuleObserver& observer) = 0; virtual void setLightingEnabled(bool enabled) = 0; virtual const char* getTexturePrefix() const = 0; /** * \brief * Return the default texture to be used for lighting mode rendering if it * is not defined for a shader. * * \param type * The type of interaction layer whose default texture is required. */ virtual TexturePtr getDefaultInteractionTexture(ShaderLayer::Type type) = 0; /** * greebo: This is a substitution for the "old" TexturesCache method * used to load an image from a file to graphics memory for arbitrary * use (e.g. the Overlay module). * * @param filename * The absolute filename. * * @param moduleNames * The space-separated list of image modules (default is "GDK"). */ virtual TexturePtr loadTextureFromFile(const std::string& filename) = 0; /** * Creates a new shader expression for the given string. This can be used to create standalone * expression objects for unit testing purposes. */ virtual shaders::IShaderExpressionPtr createShaderExpressionFromString(const std::string& exprStr) = 0; }; inline MaterialManager& GlobalMaterialManager() { // Cache the reference locally static MaterialManager& _shaderSystem( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_SHADERSYSTEM) ) ); return _shaderSystem; } DarkRadiant-2.5.0/include/isound.h000066400000000000000000000067321321750546400170160ustar00rootroot00000000000000#pragma once #include "imodule.h" #include "ModResource.h" #include #include // A list of sound files associated to a shader typedef std::vector SoundFileList; const float METERS_PER_UNIT = 0.0254f; const float UNITS_PER_METER = 1/METERS_PER_UNIT; // The min and max radii of a sound shader class SoundRadii { float minRad, maxRad; public: //set sound radii either in metres or in inch on initialization might cause a conversion SoundRadii (float min = 0, float max = 0, bool inMetres = false) { if (inMetres) { minRad = min * UNITS_PER_METER; maxRad = max * UNITS_PER_METER; } else { minRad = min; maxRad = max; } } // set the sound radii in metres or in inch, might cause a conversion void setMin(float min, bool inMetres = false) { if (inMetres) { minRad = min * UNITS_PER_METER; } else { minRad = min; } } void setMax (float max, bool inMetres = false) { if (inMetres) { maxRad = max * UNITS_PER_METER; } else { maxRad = max; } } float getMin(bool inMetres = false) const { return (inMetres) ? minRad * METERS_PER_UNIT : minRad; } float getMax(bool inMetres = false) const { return (inMetres) ? maxRad * METERS_PER_UNIT : maxRad; } }; /// Representation of a single sound or sound shader. class ISoundShader : public ModResource { public: /// Get the name of the shader virtual std::string getName() const = 0; /// Get the min and max radii of the shader virtual SoundRadii getRadii() const = 0; /// Get the list of sound files associated to this shader virtual SoundFileList getSoundFileList() const = 0; // angua: get the display folder for sorting the sounds in the sound chooser window virtual const std::string& getDisplayFolder() const = 0; }; typedef std::shared_ptr ISoundShaderPtr; const char* const MODULE_SOUNDMANAGER("SoundManager"); /// Sound manager interface. class ISoundManager : public RegisterableModule { public: /// Invoke a function for each sound shader virtual void forEachShader(std::function) = 0; /** greebo: Tries to lookup the SoundShader with the given name, * returns a soundshader with an empty name, if the lookup failed. */ virtual ISoundShaderPtr getSoundShader(const std::string& shaderName) = 0; /** * greebo: Plays the given sound file (defined by its VFS path). * * @returns: TRUE, if the sound file was found at the given VFS path, * FALSE otherwise */ virtual bool playSound(const std::string& fileName) = 0; /** * greebo: Plays the given sound file (defined by its VFS path). * Will loop the sound if the given flag is set to TRUE. * * @returns: TRUE, if the sound file was found at the given VFS path, * FALSE otherwise */ virtual bool playSound(const std::string& fileName, bool loopSound) = 0; /** greebo: Stops the currently played sound. */ virtual void stopSound() = 0; }; // Accessor method inline ISoundManager& GlobalSoundManager() { // Cache the reference locally static ISoundManager& _soundManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_SOUNDMANAGER) ) ); return _soundManager; } DarkRadiant-2.5.0/include/ispacepartition.h000066400000000000000000000062031321750546400207040ustar00rootroot00000000000000#ifndef _ISPACE_PARTITION_H_ #define _ISPACE_PARTITION_H_ #include #include #include "imodule.h" // Forward declaration class AABB; namespace scene { // Some forward declarations to avoid including all the headers class INode; typedef std::shared_ptr INodePtr; class ISPNode; typedef std::shared_ptr ISPNodePtr; typedef std::weak_ptr ISPNodeWeakPtr; /** * greebo: This is the abstract definition of a SpacePartition node. * * A SpacePartition node has the following properties: * * - It always has valid bounds (in the form of an AABB). * - Each node can have any amount of children [0..infinity) * - A node with 0 children is called a Leaf. * - Each node can have exactly one parent (which is NULL for the root node). * - The collectivity of nodes form a tree whereas the topmost one is the largest. * - Each node can host any amount of "members" (member == scene::INode). * * It is the task of the ISpacePartitionSystem to allocate and manage these nodes. * scene::INodes are "linked" to the correct ISPNodes through the ISPacePartition's * link() methods - and are unlinked through the unlink() method of the latter. */ class ISPNode { public: virtual ~ISPNode() {} // The child nodes typedef std::vector NodeList; // The members typedef std::list MemberList; // Get the parent node (can be NULL for the root node) virtual ISPNodePtr getParent() const = 0; // The maximum bounds of this node virtual const AABB& getBounds() const = 0; // The child nodes of this node (either 8 or 0) virtual const NodeList& getChildNodes() const = 0; // Returns true if no more child nodes are below this one virtual bool isLeaf() const = 0; // Get a list of members virtual const MemberList& getMembers() const = 0; }; typedef std::shared_ptr ISPNodePtr; /** * greebo: The SpacePartitionSystem interface is a simple one. All it needs * to do is to provide link/unlink methods for linking scene::INodes * into the space partition system and to deliver the "entry point" for traversal, * which is the root ISPNode. * * The link() method makes sure the given node is added as member to the ISPNode it fits best. * The unlink() method can be used to remove a node from the tree again. * * Note: It's not allowed to call link() for nodes which are already linked into the tree. * It's safe to call unlink() for any node at any time, even multiple times in a row. * The unlink() method will return true if the node had been linked before. */ class ISpacePartitionSystem { public: virtual ~ISpacePartitionSystem() {} // Links this node into the SP tree. Returns the node it ends up being associated with virtual void link(const scene::INodePtr& sceneNode) = 0; // Unlink this node from the SP tree, returns true if this was successful // (node had been linked before) virtual bool unlink(const scene::INodePtr& sceneNode) = 0; // Returns the root node of this SP tree (the largest one, encompassing everything) virtual ISPNodePtr getRoot() const = 0; }; typedef std::shared_ptr ISpacePartitionSystemPtr; } // namespace scene #endif /* _ISPACE_PARTITION_H_ */ DarkRadiant-2.5.0/include/itexdef.h000066400000000000000000000025141321750546400171370ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (INCLUDED_ITEXDEF_H) #define INCLUDED_ITEXDEF_H class Matrix4; /* greebo: The texture definition structure, containing the scale, * rotation and shift values of an applied texture. * At some places this is referred to as "fake" texture coordinates. */ class GenericTextureDefinition { public: double _shift[2]; double _rotate; double _scale[2]; virtual ~GenericTextureDefinition() {} // Test the texture definition for insanely large values virtual bool isSane() const = 0; virtual Matrix4 getTransform(double width, double height) const = 0; }; #endif DarkRadiant-2.5.0/include/itextstream.h000066400000000000000000000144661321750546400200710ustar00rootroot00000000000000#pragma once /// \file /// \brief Text-stream interfaces. #include #include #include #include #include #include #include #include #include /// \brief A read-only character-stream. // OrbWeaver: merged functionality from TextStreambufAdaptor onto this class // directly. class TextInputStream : public std::streambuf { protected: // Buffer to use for reading static const std::size_t BUFFER_SIZE = 8192; char _buffer[BUFFER_SIZE]; protected: /* Implementations of stream-specific virtual functions on std::streambuf */ // Replenish the controlled buffer with characters from the underlying // input sequence. virtual int underflow() { // Read next block of BUFFER_SIZE characters into the buffer from // the underlying TextInputStream. std::size_t charsRead = this->read(_buffer, BUFFER_SIZE); // Set up the internal pointers correctly assert(charsRead <= BUFFER_SIZE); std::streambuf::setg(_buffer, _buffer, _buffer + charsRead); // Return the next character, or EOF if there were no more characters if (charsRead > 0) return static_cast(_buffer[0]); else return EOF; } public: /// \brief Attempts to read the next \p length characters from the stream to \p buffer. /// Returns the number of characters actually stored in \p buffer. virtual std::size_t read(char* buffer, std::size_t length) = 0; }; /** * greebo: This is a simple container holding a single output stream. * Use the getStream() method to acquire a reference to the stream. */ class OutputStreamHolder { std::ostringstream _tempOutputStream; std::mutex _nullLock; std::ostream* _outputStream; std::mutex* _streamLock; public: OutputStreamHolder() : _outputStream(&_tempOutputStream), _streamLock(&_nullLock) {} void setStream(std::ostream& outputStream) { _outputStream = &outputStream; // Copy temporary data to new buffer (*_outputStream) << _tempOutputStream.str(); _tempOutputStream.clear(); } std::ostream& getStream() { return *_outputStream; } void setLock(std::mutex& streamLock) { _streamLock = &streamLock; } std::mutex& getStreamLock() { return *_streamLock; } }; // With multiple threads writing against a single thread-unsafe std::ostream // we need to buffer the stream and write it to the underlying stream // in a thread-safe manner. The TemporaryThreadsafeStream passes its data // in the destructor - since std::ostringstream doesn't define a virtual // destructor client code should not cast the stream reference to its base // std::stringstream otherwise the destructor might not be called. class TemporaryThreadsafeStream : public std::ostringstream { private: std::ostream& _actualStream; std::mutex& _streamLock; public: TemporaryThreadsafeStream(std::ostream& actualStream, std::mutex& streamLock) : _actualStream(actualStream), _streamLock(streamLock) { _actualStream.copyfmt(*this); setstate(actualStream.rdstate()); } // Copy constructor must be defined explicitly because newer GCC won't // define it implicitly for reference members resulting in a "use of // deleted function" error when returning by value. TemporaryThreadsafeStream(const TemporaryThreadsafeStream& other) : _actualStream(other._actualStream), _streamLock(other._streamLock) { } // On destruction, we flush our buffer to the main stream // in a thread-safe manner ~TemporaryThreadsafeStream() { std::lock_guard lock(_streamLock); // Flush buffer on destruction _actualStream << str(); } }; // The static stream holder containers, these are instantiated by each // module (DLL/so) at the time of the first call. inline OutputStreamHolder& GlobalOutputStream() { static OutputStreamHolder _holder; return _holder; } inline OutputStreamHolder& GlobalErrorStream() { static OutputStreamHolder _holder; return _holder; } inline OutputStreamHolder& GlobalWarningStream() { static OutputStreamHolder _holder; return _holder; } inline OutputStreamHolder& GlobalDebugStream() { static OutputStreamHolder _holder; return _holder; } // The stream accessors: use these to write to the application's various streams. // Note that the TemporaryThreadsafeStream is using the SAME std::mutex (on purpose) // to avoid error and debug streams from concurrently writing to the same log device. inline TemporaryThreadsafeStream rMessage() { return TemporaryThreadsafeStream( GlobalOutputStream().getStream(), GlobalOutputStream().getStreamLock() ); } inline TemporaryThreadsafeStream rError() { return TemporaryThreadsafeStream( GlobalErrorStream().getStream(), GlobalErrorStream().getStreamLock() ); } inline TemporaryThreadsafeStream rWarning() { return TemporaryThreadsafeStream( GlobalWarningStream().getStream(), GlobalWarningStream().getStreamLock() ); } /** * \brief * Get the debug output stream. * * In debug builds the debug stream is the same as the output stream. In release * builds it is a null stream. */ inline TemporaryThreadsafeStream rDebug() { return TemporaryThreadsafeStream( GlobalDebugStream().getStream(), GlobalDebugStream().getStreamLock() ); } /** * Thread-safe wrapper around the std::cout output stream. * * Since the Windows build is running without any console window * the output to std::cout is redirected to the appliation's LogStream buffer. * std::cout is not thread-safe itself, that's why it's advisable to use * this accessor whenever client code needs to write to std::cout. * * see also: rConsoleError() which wraps std::cerr */ inline TemporaryThreadsafeStream rConsole() { return TemporaryThreadsafeStream( std::cout, GlobalOutputStream().getStreamLock() ); } /** * Thread-safe wrapper around the std::cerr output stream. * * Use this instead of directly writing to std::cerr which * is not thread-safe. * * see also: rConsole() which wraps std::cout */ inline TemporaryThreadsafeStream rConsoleError() { return TemporaryThreadsafeStream( std::cerr, GlobalErrorStream().getStreamLock() ); } DarkRadiant-2.5.0/include/ithread.h000066400000000000000000000010221321750546400171200ustar00rootroot00000000000000#pragma once #include /** * \brief * Interface to the threading manager. * * The ThreadManager wraps a thread pool which is owned and managed by the * main Radiant application. */ class ThreadManager { public: /// Execute the given function in a separate thread /// Returns the thread id, which can be used to query the state virtual std::size_t execute(std::function func) = 0; // Returns true if the given thread is still running virtual bool threadIsRunning(std::size_t threadId) = 0; }; DarkRadiant-2.5.0/include/itraceable.h000066400000000000000000000013471321750546400176050ustar00rootroot00000000000000#pragma once template class BasicVector3; typedef BasicVector3 Vector3; class Ray; #include /** * An object supporting this interface provides methods * to run traces against its geometry, e.g. calculating * the intersection point of a given Ray. */ class ITraceable { public: /** * Calculates the intersection point of the given Ray with the geometry * of this object. Returns false if the Ray doesn't have an intersection * point, otherwise returns true, in which case the coordinates of the * intersection point are stored in the intersection reference. */ virtual bool getIntersection(const Ray& ray, Vector3& intersection) = 0; }; typedef std::shared_ptr ITraceablePtr; DarkRadiant-2.5.0/include/itransformable.h000066400000000000000000000030151321750546400205140ustar00rootroot00000000000000#pragma once #include enum TransformModifierType { TRANSFORM_PRIMITIVE, TRANSFORM_COMPONENT, }; // Forward-declare the math objects template class BasicVector3; typedef BasicVector3 Vector3; template class BasicVector4; typedef BasicVector4 Vector4; class Quaternion; class Matrix4; class ITransformable { public: virtual ~ITransformable() {} virtual void setType(TransformModifierType type) = 0; virtual void setTranslation(const Vector3& value) = 0; virtual void setRotation(const Quaternion& value) = 0; // Rotation around a certain point in world space, which usually results // in a local rotation and a translation of the object, unless the pivot conincides // with the object's rotation center. The localToWorld matrix should be obtained from the node being transformed virtual void setRotation(const Quaternion& value, const Vector3& worldPivot, const Matrix4& localToWorld) = 0; virtual void setScale(const Vector3& value) = 0; virtual void freezeTransform() = 0; virtual void revertTransform() = 0; // For pivoted rotations the code needs to know the object center // before the operation started. virtual const Vector3& getUntransformedOrigin() = 0; }; typedef std::shared_ptr ITransformablePtr; namespace scene { class INode; typedef std::shared_ptr INodePtr; } inline ITransformablePtr Node_getTransformable(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } DarkRadiant-2.5.0/include/itransformnode.h000066400000000000000000000007711321750546400205440ustar00rootroot00000000000000#pragma once #include "inode.h" class Matrix4; /// \brief A transform node. class ITransformNode { public: virtual ~ITransformNode() {} /// \brief Returns the transform which maps the node's local-space into the local-space of its parent node. virtual const Matrix4& localToParent() const = 0; }; typedef std::shared_ptr ITransformNodePtr; inline ITransformNodePtr Node_getTransformNode(const scene::INodePtr& node) { return std::dynamic_pointer_cast(node); } DarkRadiant-2.5.0/include/iuimanager.h000066400000000000000000000156151321750546400176360ustar00rootroot00000000000000#pragma once #include "math/Vector3.h" #include "imodule.h" // Forward declarations class wxWindow; class wxToolBar; class wxMenuBar; class IColourSchemeManager { public: virtual ~IColourSchemeManager() {} // greebo: Returns the named colour, returns <0,0,0> if not found virtual Vector3 getColour(const std::string& colourName) = 0; }; namespace ui { /** * greebo: The possible menu item types, one of these * has to be passed when creating menu items. */ enum eMenuItemType { menuNothing, menuRoot, menuBar, menuFolder, menuItem, menuSeparator, }; } // namespace ui class IMenuManager { public: /** Destructor */ virtual ~IMenuManager() {} /** * Returns the constructed menu bar, ready for packing into a parent container. */ virtual wxMenuBar* getMenuBar(const std::string& name) = 0; /** greebo: Shows/hides the menuitem under the given path. * * @path: the path to the item (e.g. "main/view/cameraview") * @visible: FALSE, if the widget should be hidden, TRUE otherwise */ virtual void setVisibility(const std::string& path, bool visible) = 0; /** greebo: Adds a new item as child under the given path. * * @insertPath: the path where to insert the item: "main/filters" * @name: the name of the new item * @type: the item type (usually menuFolder / menuItem) * @caption: the display string of the menu item (incl. mnemonic) * @icon: the icon filename (can be empty) * @eventname: the event name (e.g. "ToggleShowSizeInfo") */ virtual void add(const std::string& insertPath, const std::string& name, ui::eMenuItemType type, const std::string& caption, const std::string& icon, const std::string& eventName) = 0; /** greebo: Inserts a new menuItem as sibling _before_ the given insertPath. * * @insertPath: the path where to insert the item: "main/filters" * @name: the name of the new menu item (no path, just the name) * @caption: the display string including mnemonic * @icon: the image file name relative to "bitmaps/", can be empty. * @eventName: the event name this item is associated with (can be empty). */ virtual void insert(const std::string& insertPath, const std::string& name, ui::eMenuItemType type, const std::string& caption, const std::string& icon, const std::string& eventName) = 0; // Returns true if the given path exists virtual bool exists(const std::string& path) = 0; /** * Removes an entire path from the menus. */ virtual void remove(const std::string& path) = 0; }; class IToolbarManager { public: virtual ~IToolbarManager() {} virtual wxToolBar* getToolbar(const std::string& toolbarName, wxWindow* parent) = 0; }; // The name of the command status bar item #define STATUSBAR_COMMAND "Command" class IStatusBarManager { public: // Use these positions to place the status bar elements in between // the default ones. A position of 31 would put a widget in between // POS_BRUSHCOUNT and POS_SHADERCLIPBOARD. enum StandardPositions { POS_FRONT = 0, POS_COMMAND = 10, POS_POSITION = 20, POS_BRUSHCOUNT = 30, POS_SHADERCLIPBOARD = 40, POS_GRID = 50, POS_BACK = 9000, }; /** * Destructor */ virtual ~IStatusBarManager() {} /** * Get the status bar widget, for packing into the main window. * The widget will be parented to a temporary wxFrame, so it has to be * re-parented before packing. */ virtual wxWindow* getStatusBar() = 0; /** * greebo: This adds a named element to the status bar. Pass the widget * which should be added and specify the position order. * * @name: the name of the element (can be used for later lookup). * @widget: the widget to pack. * @pos: the position to insert. Use POS_FRONT or POS_BACK to put the element * at the front or back of the status bar container. */ virtual void addElement(const std::string& name, wxWindow* widget, int pos) = 0; /** * greebo: A specialised method, adding a named text element. * Use the setText() method to update this element. * * @name: the name for this element (can be used as key for the setText() method). * @icon: the icon file to pack into the element, relative the BITMAPS_PATH. Leave empty * if no icon is desired. * @pos: the position to insert. Use POS_FRONT or POS_BACK to put the element * at the front or back of the status bar container. * @description: a description shown when the mouse pointer hovers of this item. */ virtual void addTextElement(const std::string& name, const std::string& icon, int pos, const std::string& description) = 0; /** * Updates the content of the named text element. The name must refer to * an element previously added by addTextElement(). * If immediateUpdate is set to true, the UI will be updated right now. UI updates come with * a certain cost, try to avoid it unless it's really necessary. */ virtual void setText(const std::string& name, const std::string& text, bool immediateUpdate = false) = 0; /** * Returns a named status bar widget, previously added by addElement(). * * @returns: NULL if the named widget does not exist. */ virtual wxWindow* getElement(const std::string& name) = 0; }; // Forward declarations class IGroupDialog; // see igroupdialog.h for definition namespace ui { class IDialogManager; // see idialogmanager.h for definition class IFilterMenu; // see ifiltermenu.h for definition typedef std::shared_ptr IFilterMenuPtr; } // namespace ui const std::string MODULE_UIMANAGER("UIManager"); /** greebo: The UI Manager abstract base class. * * The UIManager provides an interface to add UI items like menu commands * toolbar icons, update status bar texts and such. */ class IUIManager : public RegisterableModule { public: virtual IMenuManager& getMenuManager() = 0; virtual IToolbarManager& getToolbarManager() = 0; virtual IColourSchemeManager& getColourSchemeManager() = 0; virtual IGroupDialog& getGroupDialog() = 0; virtual IStatusBarManager& getStatusBarManager() = 0; virtual ui::IDialogManager& getDialogManager() = 0; // Returns the art provider prefix to acquire local bitmaps from the wxWidgets art provider // Example: wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "darkradiant_icon_64x64.png") virtual const std::string& ArtIdPrefix() const = 0; // Creates and returns a new top-level filter menu bar, see ifiltermenu.h virtual ui::IFilterMenuPtr createFilterMenu() = 0; }; // This is the accessor for the UI manager inline IUIManager& GlobalUIManager() { // Cache the reference locally static IUIManager& _uiManager( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_UIMANAGER) ) ); return _uiManager; } // Shortcut accessors inline IColourSchemeManager& ColourSchemes() { return GlobalUIManager().getColourSchemeManager(); } inline IGroupDialog& GlobalGroupDialog() { return GlobalUIManager().getGroupDialog(); } DarkRadiant-2.5.0/include/iundo.h000066400000000000000000000066641321750546400166370ustar00rootroot00000000000000#pragma once /// \file /// \brief The undo-system interface. Uses the 'memento' pattern. #include "imodule.h" #include #include #include class IMapFileChangeTracker; /** * greebo: An UndoMemento has to be allocated on the heap * and contains all the information that is needed to describe * the status of an Undoable. */ class IUndoMemento { public: virtual ~IUndoMemento() {} }; typedef std::shared_ptr IUndoMementoPtr; /* greebo: This is the abstract base class for an Undoable object. * Derive from this class if your instance/object should be Undoable. * * The exportState method has to allocate and return a new UndoMemento * with all the necessary data to restore the current state. * * The importState() method should re-import the values saved in the * UndoMemento */ class IUndoable { public: virtual ~IUndoable() {} virtual IUndoMementoPtr exportState() const = 0; virtual void importState(const IUndoMementoPtr& state) = 0; }; /** * Undoables request their associated StateSaver to save their current state. * The state saver might call the Undoable's exportState() method or not, * depending on whether the Undoable has already been saved during * the current operation's lifetime. * To acquire an UndoStateSaver, use UndoSystem::getStateSaver(). */ class IUndoStateSaver { public: virtual ~IUndoStateSaver() {} virtual void save(IUndoable& undoable) = 0; }; const std::string MODULE_UNDOSYSTEM("UndoSystem"); class UndoSystem : public RegisterableModule { public: // Undoable objects need to call this to get hold of a StateSaver instance // which will take care of exporting and saving the state. The passed map file change // tracker will be notified when the state is saved. virtual IUndoStateSaver* getStateSaver(IUndoable& undoable, IMapFileChangeTracker& tracker) = 0; virtual void releaseStateSaver(IUndoable& undoable) = 0; virtual std::size_t size() const = 0; virtual void start() = 0; virtual void finish(const std::string& command) = 0; virtual void undo() = 0; virtual void redo() = 0; virtual void clear() = 0; // Emitted after an undo operation is fully completed, allows objects to refresh their state virtual sigc::signal& signal_postUndo() = 0; // Emitted after a redo operation is fully completed, allows objects to refresh their state virtual sigc::signal& signal_postRedo() = 0; // greebo: This finishes the current operation and removes // it immediately from the stack, therefore it never existed. virtual void cancel() = 0; /** * Observer implementation which gets notified * on undo/redo perations. */ class Tracker { public: virtual ~Tracker() {} virtual void clear() = 0; virtual void begin() = 0; virtual void undo() = 0; virtual void redo() = 0; }; virtual void attachTracker(Tracker& tracker) = 0; virtual void detachTracker(Tracker& tracker) = 0; }; // The accessor function inline UndoSystem& GlobalUndoSystem() { // Cache the reference locally static UndoSystem& _undoSystem( *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_UNDOSYSTEM) ) ); return _undoSystem; } class UndoableCommand { const std::string _command; public: UndoableCommand(const std::string& command) : _command(command) { GlobalUndoSystem().start(); } ~UndoableCommand() { GlobalUndoSystem().finish(_command); } }; DarkRadiant-2.5.0/include/ivolumetest.h000066400000000000000000000041411321750546400200650ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(INCLUDED_CULLABLE_H) #define INCLUDED_CULLABLE_H #include "VolumeIntersectionValue.h" template class BasicVector3; typedef BasicVector3 Vector3; class Plane3; class Matrix4; class AABB; class Segment; class VolumeTest { public: /// \brief destructor virtual ~VolumeTest() {} /// \brief Returns true if \p point intersects volume. virtual bool TestPoint(const Vector3& point) const = 0; /// \brief Returns true if \p segment intersects volume. virtual bool TestLine(const Segment& segment) const = 0; /// \brief Returns true if \p plane faces towards volume. virtual bool TestPlane(const Plane3& plane) const = 0; /// \brief Returns true if \p plane transformed by \p localToWorld faces the viewer. virtual bool TestPlane(const Plane3& plane, const Matrix4& localToWorld) const = 0; /// \brief Returns the intersection of \p aabb and volume. virtual VolumeIntersectionValue TestAABB(const AABB& aabb) const = 0; /// \brief Returns the intersection of \p aabb transformed by \p localToWorld and volume. virtual VolumeIntersectionValue TestAABB(const AABB& aabb, const Matrix4& localToWorld) const = 0; virtual bool fill() const = 0; virtual const Matrix4& GetViewport() const = 0; virtual const Matrix4& GetProjection() const = 0; virtual const Matrix4& GetModelview() const = 0; }; #endif DarkRadiant-2.5.0/include/mapfile.h000066400000000000000000000005471321750546400171300ustar00rootroot00000000000000#pragma once #include "inode.h" #include class IMapFileChangeTracker { public: virtual ~IMapFileChangeTracker() {} virtual void save() = 0; virtual bool saved() const = 0; virtual void changed() = 0; virtual void setChangedCallback(const std::function& changed) = 0; virtual std::size_t changes() const = 0; }; DarkRadiant-2.5.0/include/modelskin.h000066400000000000000000000044441321750546400175000ustar00rootroot00000000000000#pragma once #include #include "imodule.h" #include class ModuleObserver; class ModelSkin { public: /** * Destructor */ virtual ~ModelSkin() {} /** * greebo: Returns the name of this skin. */ virtual std::string getName() const = 0; /** * Get the mapped texture for the given query texture, using the mappings * in this skin. If there is no mapping for the given texture, return an * empty string. */ virtual std::string getRemap(const std::string& name) const = 0; }; typedef std::shared_ptr ModelSkinPtr; class SkinnedModel { public: // destructor virtual ~SkinnedModel() {} // greebo: Updates the model's surface remaps. Pass the new skin name (can be empty). virtual void skinChanged(const std::string& newSkinName) = 0; // Returns the name of the currently active skin virtual std::string getSkin() const = 0; }; typedef std::shared_ptr SkinnedModelPtr; // Model skinlist typedef typedef std::vector StringList; const std::string MODULE_MODELSKINCACHE("ModelSkinCache"); /** * Interface class for the skin manager. */ class ModelSkinCache : public RegisterableModule { public: /** * Lookup a specific named skin and return the corresponding ModelSkin * object. */ virtual ModelSkin& capture(const std::string& name) = 0; /** * Return the skins associated with the given model. * * @param * The full pathname of the model, as given by the "model" key in the skin * definition. * * @returns * A vector of strings, each identifying the name of a skin which is * associated with the given model. The vector may be empty as a model does * not require any associated skins. */ virtual const StringList& getSkinsForModel(const std::string& model) = 0; /** * Return the complete list of available skins. */ virtual const StringList& getAllSkins() = 0; /** * greebo: Reloads all skins from the definition files. */ virtual void refresh() = 0; /// Signal emitted after skins are reloaded virtual sigc::signal signal_skinsReloaded() = 0; }; inline ModelSkinCache& GlobalModelSkinCache() { std::shared_ptr _skinCache( std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_MODELSKINCACHE) ) ); return *_skinCache; } DarkRadiant-2.5.0/include/precompiled_interfaces.h000066400000000000000000000052011321750546400222110ustar00rootroot00000000000000/** * greebo: Precompiled header module. This is included by the respective precompiled.h * files throughout the project. Many of those include boost headers into the * pre-compilation, and they do so by #include'ing this file. */ #pragma once // Add DarkRadiant interfaces #include "Bounded.h" #include "editable.h" #include "GLProgramAttributes.h" #include "i18n.h" #include "iarchive.h" #include "ibrush.h" #include "icamera.h" #include "icameraview.h" #include "iclipper.h" #include "icommandsystem.h" #include "icounter.h" #include "icurve.h" #include "idatastream.h" #include "idialogmanager.h" #include "ieclass.h" #include "ientity.h" #include "ientityinspector.h" #include "ieventmanager.h" #include "ifilechooser.h" #include "ifilesystem.h" #include "ifiletypes.h" #include "ifilter.h" #include "ifiltermenu.h" #include "ifonts.h" #include "igame.h" #include "igl.h" #include "iglprogram.h" #include "iglrender.h" #include "igrid.h" #include "igroupdialog.h" #include "igroupnode.h" #include "iimage.h" #include "iinteractiveview.h" #include "ilayer.h" #include "ilightnode.h" #include "imainframe.h" #include "imainframelayout.h" #include "imap.h" #include "imapformat.h" #include "imapinfofile.h" #include "imapresource.h" #include "imd5anim.h" #include "imd5model.h" #include "imediabrowser.h" #include "imenu.h" #include "imodel.h" #include "imodelcache.h" #include "imodelsurface.h" #include "imodule.h" #include "imousetool.h" #include "imousetoolevent.h" #include "imousetoolmanager.h" #include "inameobserver.h" #include "inamespace.h" #include "inode.h" #include "iorthocontextmenu.h" #include "iorthoview.h" #include "iparticlenode.h" #include "iparticles.h" #include "iparticlestage.h" #include "ipatch.h" #include "ipath.h" #include "ipreferencesystem.h" #include "iradiant.h" #include "iregistry.h" #include "irender.h" #include "irenderable.h" #include "irendersystemfactory.h" #include "iresourcechooser.h" #include "iscenegraph.h" #include "iscenegraphfactory.h" #include "iselectable.h" #include "iselection.h" #include "iselectiongroup.h" #include "iselectionset.h" #include "iselectiontest.h" #include "ishaderexpression.h" #include "ishaders.h" #include "isound.h" #include "ispacepartition.h" #include "itexdef.h" #include "itextstream.h" #include "ithread.h" #include "itraceable.h" #include "itransformable.h" #include "itransformnode.h" #include "iuimanager.h" #include "iundo.h" #include "ivolumetest.h" #include "mapfile.h" #include "modelskin.h" #include "ModResource.h" #include "ShaderLayer.h" #include "StringSerialisable.h" #include "Texture.h" #include "VolumeIntersectionValue.h" // Include registry methods #include "registry/registry.h" DarkRadiant-2.5.0/include/precompiled_main.h000066400000000000000000000011011321750546400210050ustar00rootroot00000000000000/** * greebo: The main precompiled header file, as included by the different * projects and modules. By setting the correct preprocessor definitions * the various precompiled_xxxxx.h files are included in the chain. */ #pragma once #ifdef DR_PRECOMPILED_WXWIDGETS #include #include #endif #ifdef DR_PRECOMPILED_INTERFACES // Add DarkRadiant interfaces #include "precompiled_interfaces.h" #endif #ifdef DR_PRECOMPILED_MATH // Add DarkRadiant interfaces #include "precompiled_interfaces.h" #endif #include #include DarkRadiant-2.5.0/include/precompiled_math.h000066400000000000000000000006011321750546400210160ustar00rootroot00000000000000/** * greebo: Precompiled header module. This is included by the respective precompiled.h * files throughout the project. */ #pragma once // Include important math headers #include "math/Vector2.h" #include "math/Vector3.h" #include "math/Vector4.h" #include "math/Matrix4.h" #include "math/AABB.h" #include "math/Quaternion.h" #include "math/Frustum.h" #include "math/Plane3.h" DarkRadiant-2.5.0/include/version.h000066400000000000000000000010311321750546400171650ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include #define RADIANT_VERSION PACKAGE_VERSION #else #define RADIANT_VERSION "2.5.0" #endif #define RADIANT_APPNAME "DarkRadiant" #define RADIANT_BLANK " " #if defined(_M_X64) || defined(__amd64__) || defined(_WIN64) #define RADIANT_PLATFORM "x64" #else #define RADIANT_PLATFORM "x86" #endif #include inline std::string RADIANT_APPNAME_FULL() { return std::string(RADIANT_APPNAME) + " " + std::string(RADIANT_VERSION) + " " + std::string(RADIANT_PLATFORM) + " "; } DarkRadiant-2.5.0/install-sh000077500000000000000000000324641321750546400157260ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2006-12-25.00 # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # 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 # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: DarkRadiant-2.5.0/install/000077500000000000000000000000001321750546400153575ustar00rootroot00000000000000DarkRadiant-2.5.0/install/bitmaps/000077500000000000000000000000001321750546400170165ustar00rootroot00000000000000DarkRadiant-2.5.0/install/bitmaps/_black.bmp000066400000000000000000000003661321750546400207360ustar00rootroot00000000000000BM6(  DarkRadiant-2.5.0/install/bitmaps/_cubiclight.bmp000066400000000000000000001400701321750546400217740ustar00rootroot00000000000000BM86(  PPP@@@@@@ppp ``` ``` ``` ``````((((((XXX ```xxx888000ppp PPP PPPPPP PPPxxx888888``` 888888 hhh```(((@@@``` 888XXXPPP PPP((((((PPPPPP```000@@@xxx ```PPP ```@@@xxx @@@ppp XXXPPP000```PPP@@@```HHHhhh000```XXX000@@@000@@@```@@@ ``` XXX```888 (((XXXPPP(((PPP(((000XXXXXX000@@@888 hhh```000000HHH@@@(((ppp@@@xxx```888 ``````xxxxxx000 ```hhhPPPxxx000PPP XXX ``` XXX@@@@@@@@@@@@`````` `````` ``` @@@(((xxx ``` ```PPPxxx@@@@@@@@@888``` ```ppphhh``` PPP ``` ```xxx888XXX```ppp@@@@@@@@@@@@``` ```(((000PPP```hhh``` ``` ``` ```hhh @@@@@@@@@``` ```xxx000 ```hhh ``` ```hhh@@@ @@@PPP(((``` ```pppHHH(((  000HHH000888 ``` ```@@@@@@pppxxx``` HHH``` PPP````````` ```xxx```xxxPPP xxxXXX XXXppp``````xxx(((000xxx000```@@@000(((@@@000``` `````` 888@@@000xxxhhh888@@@ 000@@@888``````@@@@@@@@@@@@@@@```PPP888888@@@000``` ``````PPPhhh 888888@@@@@@PPPPPP```000 HHHHHH@@@@@@pppPPP@@@@@@ppp@@@@@@@@@xxxXXX@@@((( (((HHHxxxxxxHHH 000@@@ PPP@@@@@@pppxxxXXX@@@@@@@@@```@@@000 @@@ppp```@@@@@@@@@@@@@@@```@@@@@@@@@pppHHH((( (((HHHppp``` ``` ppp@@@@@@@@@ppp``` ````````` ```````````````hhh 888DarkRadiant-2.5.0/install/bitmaps/_currentrender.bmp000066400000000000000000001400701321750546400225410ustar00rootroot00000000000000BM86(  ppp````````````hhhppp`````````ppphhh````````````ppp @@@hhhhhh@@@000``````@@@ ppp HHHxxx888888ppp@@@ @@@xxxhhhxxx ```@@@ ppphhh@@@@@@ @@@xxxhhh888@@@@@@@@@ ```@@@ 000@@@@@@000ppp@@@@@@@@@888@@@@@@ @@@xxxhhhPPP000xxx@@@ ```@@@ pppHHHxxx(((000HHHppp(((@@@@@@ @@@PPP((( ```@@@ HHH```000@@@ @@@000PPPHHH```@@@ @@@ ```ppphhh@@@ @@@000PPP ```@@@ @@@ `````` 888@@@ HHH```@@@ @@@ ```hhh @@@ @@@  ```@@@ HHH```  @@@  000888 HHH```xxx pppHHH```(((@@@000000@@@PPP @@@xxx000XXXppp000xxxhhh888```@@@ppp```@@@ HHHppp888```HHHPPPPPP@@@@@@@@@@@@@@@ppp @@@xxx(((PPP xxx```pppppp000 @@@``` @@@ XXXhhh``` PPPhhh@@@xxx@@@888``` @@@ xxxHHH(((  (((HHHxxxhhh ```000  000```PPP(((  000@@@pppHHH  000PPPPPP 888pppHHH@@@000@@@ppp @@@HHH(((``` @@@@@@000 `````` PPP``` 000ppp```hhh```````````````hhh````````````xxxppp``````xxxhhh``````hhhxxx``````hhhPPP@@@pppXXX XXX @@@@@@ ```PPP HHHppp@@@ @@@hhh PPPxxx((( PPP@@@@@@ ```xxx@@@@@@ @@@ppp@@@000000@@@000 000@@@(((@@@@@@ ```xxx(((@@@@@@ @@@@@@ @@@888 @@@000@@@hhh000xxx``` ``````xxx@@@@@@@@@ ```@@@000xxxXXX @@@@@@ @@@ 888xxxHHH @@@@@@ ```(((@@@ @@@ @@@ 888@@@ @@@@@@ ```xxx```@@@ @@@ @@@ @@@@@@ @@@@@@ `````` 888@@@ @@@ @@@ @@@@@@ @@@@@@ ```hhh @@@ @@@ @@@ 000@@@ @@@@@@ ```  @@@ @@@ @@@HHHpppxxxhhh@@@ @@@@@@``````XXX@@@888(((@@@ @@@ @@@xxxhhhxxxPPP @@@ @@@@@@ PPP``````HHHppp``` PPP``````HHH```PPP```hhhHHH@@@ ```PPPPPP @@@PPP  @@@ @@@@@@``````@@@000@@@@@@(((xxx@@@ xxxPPP  PPPPPP @@@ @@@@@@ ``````000@@@000hhh@@@PPP@@@@@@ppp888000PPPPPP 888888 PPPPPP 888```888 ppphhh ppp888 XXX```000@@@xxxPPP 888xxx@@@(((```PPP  PPP @@@ @@@ @@@PPP@@@@@@```DarkRadiant-2.5.0/install/bitmaps/_flat.bmp000066400000000000000000000003661321750546400206100ustar00rootroot00000000000000BM6(  DarkRadiant-2.5.0/install/bitmaps/_fog.bmp000066400000000000000000001400701321750546400204320ustar00rootroot00000000000000BM86(  @@@((((((PPPxxxhhh xxx@@@XXXxxxPPPHHH`````````888 ``````((((((```pppHHH((( @@@hhh``````PPPPPPPPP```XXX(((@@@000XXXXXX```pppxxxPPPxxx```888```hhh`````````hhh```XXX```PPP@@@HHH``````@@@ @@@@@@ ``````@@@ @@@@@@ ```000@@@@@@@@@@@@@@@@@@@@@@@@PPPHHHPPP```pppPPP```hhhpppPPP``` `````` XXXHHHHHH````````````````````````hhh hhhhhh ```hhhXXXhhh```xxx@@@888xxxxxx@@@HHH ```000@@@@@@@@@@@@@@@@@@@@@@@@@@@```@@@@@@```````````````````````````````````````pppDarkRadiant-2.5.0/install/bitmaps/_nofalloff.bmp000066400000000000000000001400701321750546400216250ustar00rootroot00000000000000BM86(  ```````````````hhhxxx``````hhh```@@@ ```((((((```@@@ xxx888(((````````` @@@PPP000ppp @@@@@@ ```@@@ PPPPPP@@@ `````` @@@000hhh @@@@@@ ```@@@ XXX000@@@000XXX@@@ PPP888@@@(((`````` @@@888888@@@(((ppp @@@@@@ ```@@@ xxxppp@@@ ((( xxx888`````` @@@ppp000xxx```000 @@@@@@ ```@@@ hhh``````hhh@@@ 888`````` @@@HHHxxx@@@ @@@@@@ ```@@@ HHHHHH@@@ 888hhh`````` @@@(((000ppphhh @@@@@@ ```@@@ @@@ @@@@@@ ppp@@@@@@XXX````````` @@@ @@@``` @@@@@@ ```@@@ @@@ @@@@@@ XXX`````` @@@ @@@``` @@@@@@ ```@@@ PPPPPP@@@@@@@@@@@@@@@@@@@@@@@@@@@```xxxHHH(((`````` @@@000(((hhhppp @@@@@@ ```xxx888 hhh```XXXhhh@@@@@@xxx```````````` @@@HHHxxx@@@ @@@@@@ ```PPPppp((( `````` @@@@@@```PPPppp@@@hhh``` @@@xxxpppHHH@@@ @@@@@@ ``` PPPhhh hhh@@@````````````````````````ppp@@@ ``` @@@PPP xxxPPP  888hhh  ```000XXX```@@@ @@@hhh``` @@@@@@hhh@@@ ```hhh PPP PPPxxx@@@@@@xxx@@@ ```888  @@@xxx``` @@@ppp000 PPPPPP  888hhh  @@@ ``` @@@ @@@@@@000@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@``` @@@000xxxHHHppp@@@``` @@@```  ppp @@@``` @@@000 PPPppp`````````````````````````````````````````` @@@PPP 888```((( DarkRadiant-2.5.0/install/bitmaps/_pointlight1.bmp000066400000000000000000001400701321750546400221210ustar00rootroot00000000000000BM86(  ```  PPP@@@xxx@@@ (((```XXXxxx888pppHHH````````````ppp@@@ ```PPP 000hhh @@@@@@ @@@XXX@@@```@@@ ```xxx@@@@@@ @@@ppp@@@```@@@ 888``` @@@@@@ @@@888000@@@@@@@@@ppp@@@@@@@@@000@@@@@@@@@@@@@@@ ```XXX000xxxhhh000@@@@@@ @@@ 000```@@@ ```000 @@@@@@ @@@ @@@```@@@ ``` @@@ @@@@@@ @@@ @@@```@@@ ```PPP @@@@@@ @@@ @@@```@@@ ```@@@ @@@@@@ @@@ @@@```@@@ ``` 000 @@@@@@ @@@ @@@```@@@ ```HHHxxx @@@@@@@@@ @@@```@@@ ```ppp```hhh@@@@@@PPPxxx@@@XXX @@@```@@@ ```PPP@@@@@@xxxPPP  PPP```@@@ ```PPP@@@@@@XXX@@@@@@```@@@ hhhpppHHH  888@@@ PPP@@@ PPP(((  888pppPPP  PPPppp@@@@@@@@@000@@@ @@@ @@@```@@@ hhh PPP@@@ @@@hhh @@@ ```@@@@@@ @@@HHH@@@ ```@@@@@@ PPP@@@@@@```ppp``````hhhxxx`````````ppp@@@ hhh``````PPP 888@@@ @@@ @@@ @@@ xxx``````hhhppp``````ppphhh``````xxx@@@@@@xxxxxx@@@@@@xxx`````````@@@@@@xxxxxxxxx`````````@@@@@@(((@@@888888@@@`````````888@@@ @@@PPPPPPPPPHHHPPPPPP`````````hhhXXX@@@ 000((( ````````````@@@ XXXHHHPPP````````````@@@ `````````````````````@@@ `````````````````````@@@ ```@@@HHH````````````@@@ 888000 (((```XXXXXX```@@@(((hhhXXX888```000xxxxxx000``````@@@xxxxxx ```@@@ ppp000 (((``` 000  @@@PPP000(((xxx```xxxPPP 888ppp888000hhh```((((((XXX hhh hhh```(((@@@xxx  ```hhh PPP``````@@@``````@@@ppp@@@@@@@@@xxx`````````pppDarkRadiant-2.5.0/install/bitmaps/_pointlight2.bmp000066400000000000000000001400701321750546400221220ustar00rootroot00000000000000BM86(  ```  PPP@@@xxx@@@ (((```XXXxxx888pppHHH````````````ppp@@@ ```PPP 000hhh @@@@@@ @@@XXX@@@@@@ ```xxx@@@@@@ @@@ppp@@@@@@ 888``` @@@@@@ @@@888000@@@@@@@@@ @@@ ```XXX000xxxhhh000@@@@@@ @@@ 000XXXHHH@@@ ```000 @@@@@@ @@@ @@@hhh(((xxx@@@ ``` @@@ @@@@@@ @@@ @@@xxx000hhh@@@ ```PPP @@@@@@ @@@ @@@PPPhhh@@@ ```@@@ @@@@@@ @@@ @@@XXXPPP@@@ ``` 000 @@@@@@ @@@ @@@hhhXXX@@@ ```HHHxxx @@@@@@@@@ @@@hhhppp@@@ ```ppp```hhh@@@@@@PPPxxx@@@XXX @@@```000@@@ ```PPP@@@@@@xxxPPP  PPP000@@@ ```PPP@@@@@@XXX@@@@@@XXX```@@@ hhhpppHHH  888@@@ PPP@@@ PPP(((  888pppPPP  PPPXXX```@@@ @@@ @@@PPP(((```xxx ppp@@@ hhh PPP@@@ @@@@@@888@@@@@@@@@ @@@ ```@@@@@@ @@@@@@ppp@@@ ```@@@@@@ PPP@@@@@@```hhh000000xxxppp``````hhhxxx`````````ppp@@@ xxx``````HHH``````PPP 888@@@ @@@ @@@ @@@ xxx``````hhhppp``````ppphhh``````xxx@@@@@@xxxxxx@@@@@@xxx`````````@@@@@@xxxxxxxxx`````````@@@@@@(((@@@888888@@@`````````888@@@ @@@PPPPPPPPPHHHPPPPPP`````````hhhXXX@@@ 000((( ````````````@@@ XXXHHHPPP````````````@@@ `````````````````````@@@ `````````````````````@@@ ```@@@HHH````````````@@@ 888000 (((```XXXXXX```@@@(((hhhXXX888```000xxxxxx000``````@@@xxxxxx ```@@@ ppp000 (((``` 000  @@@PPP000(((xxx```xxxPPP 888ppp888000hhh```((((((XXX hhh hhh```(((@@@xxx  ```hhh PPP``````@@@``````@@@ppp@@@@@@@@@xxx`````````pppDarkRadiant-2.5.0/install/bitmaps/_pointlight3.bmp000066400000000000000000001400701321750546400221230ustar00rootroot00000000000000BM86(  ```  PPP@@@xxx@@@ (((```XXXxxx888pppHHH````````````pppppp````````````hhh@@@ ```PPP 000hhh @@@@@@ @@@XXX@@@```000@@@xxx@@@ ```xxx@@@@@@ @@@ppp@@@ hhh@@@ 888``` @@@@@@ @@@888000@@@@@@@@@ 888@@@@@@888@@@ ```XXX000xxxhhh000@@@@@@ @@@ 000 000ppp000PPP@@@ ```000 @@@@@@ @@@ @@@xxx(((@@@ ``` @@@ @@@@@@ @@@ @@@ @@@ ```PPP @@@@@@ @@@ @@@(((@@@ ```@@@ @@@@@@ @@@ @@@000PPP@@@ ``` 000 @@@@@@ @@@ @@@ppp@@@(((  @@@ ```HHHxxx @@@@@@@@@ @@@```@@@@@@ ```ppp```hhh@@@@@@PPPxxx@@@XXX @@@```HHH@@@ ```PPP@@@@@@xxxPPP  PPPxxx``````XXX(((HHH@@@ ```PPP@@@@@@XXX@@@@@@(((xxx@@@ hhhpppHHH  888@@@ PPP@@@ PPP(((  888pppPPP  PPP``````@@@ @@@ @@@hhh(((XXX000```@@@ hhh PPP@@@ @@@```000@@@@@@@@@ @@@ ```@@@@@@ @@@```XXX@@@ ```@@@@@@ PPP@@@@@@```xxx@@@ hhhppp``````hhhxxx`````````ppp@@@ xxx``````HHH``````xxxPPP 888@@@ @@@ @@@ @@@ xxx``````hhhppp``````ppphhh``````xxx@@@@@@xxxxxx@@@@@@xxx`````````@@@@@@xxxxxxxxx`````````@@@@@@(((@@@888888@@@`````````888@@@ @@@PPPPPPPPPHHHPPPPPP`````````hhhXXX@@@ 000((( ````````````@@@ XXXHHHPPP````````````@@@ `````````````````````@@@ `````````````````````@@@ ```@@@HHH````````````@@@ 888000 (((```XXXXXX```@@@(((hhhXXX888```000xxxxxx000``````@@@xxxxxx ```@@@ ppp000 (((``` 000  @@@PPP000(((xxx```xxxPPP 888ppp888000hhh```((((((XXX hhh hhh```(((@@@xxx  ```hhh PPP``````@@@``````@@@ppp@@@@@@@@@xxx`````````pppDarkRadiant-2.5.0/install/bitmaps/_quadratic.bmp000066400000000000000000001400701321750546400216340ustar00rootroot00000000000000BM86(  PPP@@@@@@ppp ``` ``` ``` ``````((((((XXX ```xxx888000ppp PPP PPPPPP PPPxxx888888``` 888888 hhh```(((@@@``` 888XXXPPP PPP((((((PPPPPP```000@@@xxx ```PPP ```@@@xxx @@@ppp XXXPPP000```PPP@@@```HHHhhh000```XXX000@@@000@@@```@@@ ``` XXX```888 (((XXXPPP(((PPP(((000XXXXXX000@@@888 hhh```000000HHH@@@(((ppp@@@xxx```888 ``````xxxxxx000 ```hhhPPPxxx000PPP XXX ``` XXX@@@@@@@@@@@@`````` `````` ``` @@@(((xxx ``` ```PPPxxx@@@@@@@@@888``` ```ppphhh``` PPP ``` ```xxx888XXX```ppp@@@@@@@@@@@@``` ```(((000PPP```hhh``` ``` ``` ```hhh @@@@@@@@@``` ```xxx000 ```hhh ``` ```hhh@@@ @@@PPP(((``` ```pppHHH(((  000HHH000888 ``` ```@@@@@@pppxxx``` HHH``` PPP````````` ```xxx```xxxPPP xxxXXX XXXppp``````xxx(((000xxx000```@@@000(((@@@000``` `````` 888@@@000xxxhhh888@@@ 000@@@888``````@@@@@@@@@@@@@@@```PPP888888@@@000``` ``````PPPhhh 888888@@@@@@PPPPPP```000 HHHHHH@@@@@@pppPPP@@@@@@ppp@@@@@@@@@xxxXXX@@@((( (((HHHxxxxxxHHH 000@@@ PPP@@@@@@pppxxxXXX@@@@@@@@@```@@@000 @@@ppp```@@@@@@@@@@@@@@@```@@@@@@@@@pppHHH((( (((HHHppp``` ``` ppp@@@@@@@@@ppp``` ````````` ```````````````hhh 888DarkRadiant-2.5.0/install/bitmaps/_scratch.bmp000066400000000000000000001400701321750546400213060ustar00rootroot00000000000000BM86(  hhh``````hhh`````````hhh``````xxx``````hhhhhh``````pppppp@@@(((```HHHHHHxxx```@@@(((``` @@@hhh PPPhhh000000``` @@@``` PPPxxx@@@```000@@@ppp@@@PPP @@@``` @@@XXX```PPP ```000@@@000@@@```XXX888@@@(((@@@@@@ @@@000@@@XXX(((@@@888 @@@``` ```@@@PPP000```@@@```@@@xxxxxx@@@@@@ hhhppp888 @@@```xxx@@@((( ```@@@ @@@ @@@`````` @@@```hhhPPP@@@ PPPHHH```HHH``` @@@ @@@@@@ @@@```xxx000``````ppp888@@@XXX```@@@ @@@@@@ @@@```xxxPPP``````hhh@@@ @@@@@@ @@@```HHH888PPP```@@@```xxxPPP(((@@@ @@@HHH @@@ppp``` 000xxxxxxppp```XXXxxx``````HHH @@@pppPPP``` @@@```hhh888 xxx@@@```pppppp@@@@@@``` PPP``````HHH```pppHHHhhhHHH``` @@@ HHHXXX @@@ppp(((```888@@@@@@ 888 @@@```@@@``` PPP  PPPhhh  000PPP888@@@```888@@@```hhh@@@@@@hhh pppppp888 888hhhppp000000XXXhhh ppp888 XXXppp888  888pppPPP  PPPxxxPPP  HHHxxx @@@```000000xxx @@@ @@@ @@@ @@@ @@@ @@@PPP@@@@@@``` @@@ @@@DarkRadiant-2.5.0/install/bitmaps/_spotlight.bmp000066400000000000000000001400701321750546400216740ustar00rootroot00000000000000BM86(  ```000  888ppp@@@PPP@@@ ``````HHHhhhppp(((xxx`````````xxx``````````````````hhh``````xxxxxx``````ppp```000@@@(((000ppp```((((((hhh@@@000`````` ```XXX000 000PPPxxx ```@@@@@@hhh hhhxxxhhhPPPPPP@@@ `````` ```xxx```@@@@@@xxx```(((HHH```XXX888@@@(((@@@@@@ ppp```(((@@@(((hhh@@@888 ``` hhh ``````@@@@@@PPP(((@@@(((```888xxx888 xxx```000 ppphhh ```xxx``` ```PPP000ppppppPPP```@@@@@@000000((( @@@pppXXXPPPxxx``` ``` ``` ```@@@@@@ XXXxxx```@@@((((((ppp```````````` ``` ```PPP ```@@@@@@ ```hhh(((hhh```HHH`````` ``` `````` ```@@@@@@ ```hhh000ppp```HHH`````` ``` `````` ```@@@@@@ ```(((@@@XXXpppxxx``````xxxxxx`````` ``` ```HHH ```@@@@@@ ```888PPPxxxxxxPPPHHHxxx``` ``` ```((( ```@@@@@@ppp ``` 888XXX```XXXhhh (((```XXX000``` ``` ``````000xxxppp```@@@000ppp000 ```@@@@@@@@@000``` ```ppp ppp888 888``` ```000 ```@@@ @@@hhh  hhh000```@@@hhhhhh ``` ```000```@@@xxx`````````(((000XXX xxxHHH(((```xxx@@@@@@xxx888 888```888 hhh```(((000000 hhh@@@@@@```000@@@xxxhhh  hhh``` ```@@@@@@ `````` ``` PPP@@@@@@ `````` ```@@@@@@@@@ ```ppp@@@@@@PPP```@@@@@@@@@PPP@@@@@@pppxxx``````````````````ppp@@@@@@DarkRadiant-2.5.0/install/bitmaps/_white.bmp000066400000000000000000000003701321750546400207750ustar00rootroot00000000000000BM6(  DarkRadiant-2.5.0/install/bitmaps/active_layer_invisible.png000066400000000000000000000004571321750546400242450ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME"IDAT8˥ 0 E_"HGHGt8pˉ ]+0KGH/FJ#HɑgE8j\Uޣ3܄s`ɻ[TZr+50GhC%3p+% w`8^LBTis!o`3d)l `.@]/ <ġEk$Qs2="0abJIENDB`DarkRadiant-2.5.0/install/bitmaps/active_layer_visible.png000066400000000000000000000004171321750546400237120ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIMExIDAT8˵k ? XZja-B+гr !@{͑lvfeTBqDn/@=)3.Xd )ȃЪ 1v.t:ɳh|yMJ !(PUmޮ qXm%IENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_down.png000066400000000000000000000003161321750546400217050ustar00rootroot00000000000000PNG  IHDR 2dbKGD pHYs  tIMEԖ[IDATu1 @Ri/ba7Ha^ 㲰H;_' KၵO(0[ acXI |?g9I IENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_e24.png000066400000000000000000000004141321750546400213270ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIMEԖIDAT8˽ӱ @ /! P :6`:%J -- :KA@K'KOoٗ:fأ@Y<jHi;LpyQ|Ũ-c^©ѡ+1Hp֛G :OPaX }WCz{ʽq,'~;NfVIENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_left.png000066400000000000000000000003121321750546400216640ustar00rootroot00000000000000PNG  IHDR \RbKGD pHYs  tIME! RWIDATӍ1@_Ј 7(@rĖ;[%O2 qq- )x)oPay(14WF9MnIENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_n24.png000066400000000000000000000004231321750546400213400ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME {bIDAT8ݓ10 EQE8A9G3p^}TVK`?'q$;wIe5"ޕ" {H N<{H $Lfvv%1Ou7O4fcC4MPm Wh|)*snIENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_ne24.png000066400000000000000000000005511321750546400215070ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME;nqIDAT8˥ӻJA_6[,-eltB| "ocgᥰP+? ,agWt`7L'"gtjC_1e \^t97.XEpant7Y@ <ʊ[㥦3=FmE]>IENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_s24.png000066400000000000000000000004371321750546400213520ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME:^IDAT8ݓ1 1E߈{ maiI @x A$K ɶ~$o$̌Dg`xw$#W8+^Ь?w?/$AnB] cư\%r$c ||+ vlV]a5IENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_se24.png000066400000000000000000000005551321750546400215200ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME9{]IDAT8ˍ=/DA]ZB"^*Ukjj&tDSSKu43ɵ>d3 MЩX?Va.頬S|#RCQQN,Rq!.0[*kU_8OŻxN{'U}lM2R>6mu yjIENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_sw24.png000066400000000000000000000005641321750546400215420ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIMÈOIDAT8˥һJCA/' AkAATVym쭵-_F@ CXN897lf5Ԭ%W7Լ9>qP`*#lH8qxgs}^FN8>`O1E͉خZX.XhK<?fť MƗ媇K܎;s4,7xdx0vЊ6q3ՔFc`.R3D %z+8IENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_up.png000066400000000000000000000003171321750546400213630ustar00rootroot00000000000000PNG  IHDR 2dbKGD pHYs  tIMÈO\IDATu!PjMoQL&=' 1@^`Y;f%Qq7&=Wq^uX_<\ˉ/Ƀ\9I(IENDB`DarkRadiant-2.5.0/install/bitmaps/arrow_w24.png000066400000000000000000000003751321750546400213570ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME9{]IDAT8˵!` oc<'(8I8'@X,CYҍ&5m{IkQ3΀+f&ܛ*b7X`W(+hPgV s#q t,6?SB[7OIENDB`DarkRadiant-2.5.0/install/bitmaps/axes.png000066400000000000000000000003021321750546400204570ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME8 ?OIDAT8; ! BB6m!_&%JJ8%HW . s4g D``l`PfyIENDB`DarkRadiant-2.5.0/install/bitmaps/bgimage16.png000066400000000000000000000004041321750546400212640ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME2IDAT8˥ D8r%+W2$4gQ p !ܽ|JB YDo;Lx0X⫿ۡǚ s䆣o _Ϙ2ڎX 4sJ>җ1vHW+A;(&֎ r:L|EIENDB`DarkRadiant-2.5.0/install/bitmaps/black.bmp000066400000000000000000000003701321750546400205720ustar00rootroot00000000000000BM6(  DarkRadiant-2.5.0/install/bitmaps/book.png000066400000000000000000000014111321750546400204530ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME#.IDAT8˥Md{:s'sq0 !%Dmp&Dj'-\$N(R @[Ce AVQ$ fJIg}紸es=bfNsj+ѵc9lst+% CB1j`x(/!"h! X,k-d4&$gn7{|=GV5(/WHFN=0aLLLz.Án#aDM!OsˣzYF!I$IdY<^033ӤJHzH&j. 6(v1==N RU Nj䓗":;;Ԅ94]2rδ:bgoD"faaa4MCєRp":::~)˲~cvv ÀM&wCΖ/F3YAe&=mt>՚B B P$bSUUahhX +++8 9 tD"Qtf2qCeP`lP7N166lnnd7(,L&Sc6*++_Fk9Ze:B B@Bv;]h=?O*@Ų[AP.|8>>(l.+xMB888ѧ) TmmWOD"P(=yR-NU !0L8{yخT,D"*AlllFd2|>ↆbuu?~QnmmQ%lr#P;|nH1p}}MmmmІ$9O8IENDB`DarkRadiant-2.5.0/install/bitmaps/cap_cylinder.png000066400000000000000000000023761321750546400221700ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs(K(KV{ftIMETIDATXýWoHg~rsis/m2P#2KvH"B1밸ZR(>Ba⧲X#'66FE bM̢&w3KV C{} ǻΛs555f8H$4")J➓9NYЀ, qBE|>nw<|[㸛}}}qOdY&db1J+X,FDQ$,rƍdi555MNNNUU $iq*!/ŋmE`@<XhqqcۥK} t:ݡ3 X,Ǐ?H@(700i0]hă2 يb;m= rmnn~]EQ,2́Z2N\.Nka;^ok׮=88ضg~驐$ uIzF###Ohvpӧ!IҞݒVqzS/Eܾ};F !d٭&"@DQ܊dlB{jZPpfq:%uuu(**0o i&A<G xX ~kXhvXֳfd2EQD"D6x 3lv@p7E~`֧͘IENDB`DarkRadiant-2.5.0/install/bitmaps/cap_endcap.png000066400000000000000000000017301321750546400216020ustar00rootroot00000000000000PNG  IHDR ½"bKGD pHYs(K(KV{ftIME4PseIDATHŕOH#Wǿy ILĄYMARphZnBm/,C/RS걊VE,%R5z% LH%dljq}; w{{{G.BI$OOOO<>>~lƯ6{~ُ݁@$80PTfqvvvvv~<99tP(L&4KR,Ld2RK,ɰ|>...t: 4whh#rX,EGGGX^^~|xxu<333?]כOE,--omm='QO@y{{͆|>Ԕc 6 077{Q|sppiuR\^^brrxI$OT~YoRۍ:jEUT 3rppE p<ϿL|hazU²,ѫGR Ba@ BwZ_ `kF@\FOO B|YJRJLVK(nUUNr EQ ˲k Ȳ #.MA*ntUUeՊvnB0͐$I%Rl69c PJnK'@(v"E|CX37yڹX,@P(Wdȼ٫jocX_fAGSIENDB`DarkRadiant-2.5.0/install/bitmaps/cap_ibevel.png000066400000000000000000000016001321750546400216120ustar00rootroot00000000000000PNG  IHDR ꂣAbKGD pHYs(K(KV{ftIME틽 IDATHŗMKkG5!ƀ QKᮥ5PbW] *mpQ~FXpzc<'1^ z,f6; P mۖ{FCDDឿD)Zy^-B4F**r?߁ ?J¿ADZzZS*H&qY2Mm9@u"LLL>8۬7NNN~o_'w022r&"%HDX,id>lVJr9`0x^BPFlۖ rxx()BA,˒E_><BPҲ,d2b۶lnnJ89NZ%lVL|`ttTNOON;gxJJ),ˢZ,LMM}y{{5όXZJ)F^B (`aa/ox<POW|>ToyJ=hF|>?(Y^^fmm+gm3==| 8NiDyJPP766.5" ~ﭮ.q4MreE"I!3b JD:YEDDJ߫1w@|;66mT׉hnnx<^-r Ày0 iBu4M.wrE; ۶׳z6}43334::!t]i<Z ,4M1Z;0* 㟔Wo,Z>(Z,:җbFd2)?^[[l```P(햄% Id2\|144n}o9tvvbzz%K>̕RGG ~F$ i"N`z$a!H-q]` c~~1 KKK3B`ߴN*2t].+XL&KfOVU?(R_\\4MC&YE@lmm|rrEQWUUFV-(۶d2 B˛%"8 cLl6@P۵,KẮGGG;|>Zr^.GA \\\Ţ̫,ifRx-i.Eey[){ph92\@S4>-9ܣQ/O.FqIENDB`DarkRadiant-2.5.0/install/bitmaps/check.png000066400000000000000000000005071321750546400206030ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME4PsIDAT8˥;JCQjӈ X,@TW*nmXfZi%X7,f܇jǩ"Vz0DZܕ\&8pշfE}R>v!$>fp{kZ_a\b3peF8+zE# P>M勬op#"'4~I`74ϠoiA䳮nV 4E a3pPq]}Z{ׅS L]Z5Jn^+ +FzdoYIENDB`DarkRadiant-2.5.0/install/bitmaps/cmenu_add_model.png000066400000000000000000000003601321750546400226220ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME+}IDAT8˵ D ۰k8^ "R\.RgԐ[!MUF{\3-d-*t0"^A:p  Jxؠ Zp>$wb6%u uIENDB`DarkRadiant-2.5.0/install/bitmaps/cmenu_add_prefab.png000066400000000000000000000004571321750546400227700ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME+IDAT8˝@DlB$5F00f>;vI%X&MTKz>K0£7CQc']XҠ$\4QKڽ3IdQu:?іC(i+IENDB`DarkRadiant-2.5.0/install/bitmaps/cmenu_merge_entities.png000066400000000000000000000003731321750546400237210ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME1u:oIDAT8˥A 0 D߈Ǜ~Qb"m$#X `7I xf>.7@V ` u];;uKn߳LjxDĨq¢]ČF";g3ev~o|7ipAIENDB`DarkRadiant-2.5.0/install/bitmaps/cmenu_revert_worldspawn.png000066400000000000000000000004301321750546400244770ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME$IDAT8˥ EQ$1'؆F4A7Iy2ՋHbk\|SD$MuY5(Զ`̀J3pFrQE$uN"S8}ӕ:PfyW FZ,_8LˈAKb`V b`IENDB`DarkRadiant-2.5.0/install/bitmaps/compile.png000066400000000000000000000003771321750546400211630ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME$iTXtCommentCreated with GIMPd.ecIDAT8R @ sNLCy df/BCttȑ1-2aOϵ@tHZ[")nLi7RL.ujqfGJ WGIENDB`DarkRadiant-2.5.0/install/bitmaps/create_decals.png000066400000000000000000000006431321750546400223050ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME1u:o0IDAT8ˍNPm*Q $@TN`x ay҇Ԃ@@ fvn{EDm bbD]\,D#5>sNBM4v|\c4%H4Yy2l5AgX9UpGp լS*VpP`^)Q$zTUE̠/;la?/pЮvDk}XEK#|R$;)m@^]J _1klep)AIENDB`DarkRadiant-2.5.0/install/bitmaps/curve_append_point.png000066400000000000000000000006341321750546400234130ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs+tIME0=)IDAT(ϕKBas\","Z+Z#"ScJ\MZ$ZTr3;{>s i8n$]3ܭփ_0^ai;4!TIlb-٨e7B`? CIP= #}~_6@R x€KqAdc&6!a$iWR&AduI;^-I?ʒ%Kv?_vs*y4p46;8q&Hnh>ǿ)nIENDB`DarkRadiant-2.5.0/install/bitmaps/curve_cap.png000066400000000000000000000004041321750546400214710ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME0=IDAT(ϥ ! Ce3ͲSN1pRebIU@rIJ,Zw8@a  ePU%:/vNk b>/RpsyfANV0\'0}ceKG3IENDB`DarkRadiant-2.5.0/install/bitmaps/curve_convert.png000066400000000000000000000006611321750546400224130ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs+tIME/5 >IDAT(ϕ1KA=,AF?RH,Dl,wF+;BS[$hu@cnXݙvfoƙv3.3y/G%]=:V>4 9Q+8zpfݜb$@rΪy>JZu9L}g 2̌y+@?'i!苄G($=2u9 8I.qwa6 L%ԁ`8*kiचWi`?^6g7ɀIENDB`DarkRadiant-2.5.0/install/bitmaps/curve_create.png000066400000000000000000000010321321750546400221670ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs+tIMESIDAT(ϕkQsX>"!E[' ]AtTZ˛Ժ-.Nš*!R3DژZIKX3s99/ytx Jz˵@NRcy  ] {w3|m?ӉF_`X4=$MI:ڑ@[ತoDy59IWG@Ls ͿsO_ µgϿԖJA\ˡ^=c&(o3C)^/0 FNwHJ%| WXHOOơ&ǡ++=r:xk3Y]`ԕsnW8Ӿjh6jyO5=D` (^{eiIy)V0 IENDB`DarkRadiant-2.5.0/install/bitmaps/curve_insert_points.png000066400000000000000000000006271321750546400236350ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs+tIME[ǖ$IDAT(ϝ-HCaoww""eڬ. VMULങDi &EqIM7]ƼIqfY8tJB"OI\ fHc<uA~9`sIzF"#?mm`Z/|\a.b}X{ ׁh5kfͲm5`;`E㿀h.KC۳>-YL9\&[./ak/G XH1$-q<$рIENDB`DarkRadiant-2.5.0/install/bitmaps/curve_remove_points.png000066400000000000000000000006441321750546400236250ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs+tIME/5 1IDAT(ϝJQIL6 ""*&O X B [[ w(+ VbB ;?nd!.30p9\13Tu8jzƀz3T/$'oGafH*K#"R' NUwЮmUk )YCJif=9qKvMFw}"nDU>s,lS@ UQy $d\i,P/!9A`I8U2x$92Ol:2<@Gd |i.aIENDB`DarkRadiant-2.5.0/install/bitmaps/darkradiant_icon_64x64.png000066400000000000000000000065421321750546400237020ustar00rootroot00000000000000PNG  IHDR@@iqbKGD pHYs  tIME&:6t IDATx]]uk}`͇11`CP$K(EMQ6RFR_>TU#*JڧJDBQP)-=3w~0DHWgZk3|8> 3f0` 3f!<|ޫC6|}u^D9/9Qq5O_Y fp~n+ !Nć(,mx((?oO=̃7yO<sWNYBD#{}q OpkHF#i40LMAY04&:l!Q"}0\ q6iEțwjmQd:?x& AvJR+ Imw\FZ-c<8}O<@{sP:s*#3PzO =}bi8rXNg=:v 8}~?E4isn 'QQeȣ"G3 5C +{iknO3},o:=-8[vˌ˶U3)UG@'_KV\ hKb4C"RAApno{r^:,A R:3'Ó?_ݠVk߮V-)J2{[ٵ̈!?TD}f$U|9Lu& m* 1Eqzi[m}&D>I bRd"}?^}k1ho6ќU& 0VeϛrSS= U Y=7+#UU*aF< _%5VflV؛V z2ʒ2hF *L,.rv88sf˵ߑ pr觴 ."ksEUb!5nu̡`"52IUIfwǒYX,n Vkek՜rZ+90cqy~ɱ蜛]щm`^Uތ foXؾw޽קR3w5*aTo~(]ųl& |%UfM6KιBݑ=Ŧ&SK++Tuf$zU̞^oBM6SWŽ܋h"2qJd[gNꆲ,s`Si 8'mՕZ=N /uyZE]a;;+ucG.wdʾk#27Uh*=y~ڵh4"X79FrcB5ὣi!N&=ΪH)fX6)n6ĘHQĪqB'?J#WfLXDύFO3^dR4=>vO+,':_"㊪x_[?5 V 07ဢEם/P3N =u(Vb9RҺ!}=<'y㍷ ;5'>y/o3[an^kk5q "XYgΪg.ŇO)7L>--i7^0wn I=jT*8Tejo~hfKFh/px"Oi-*׫ Cf.?3f0` 3f0v/p¼0rIENDB`DarkRadiant-2.5.0/install/bitmaps/darksplash.png000066400000000000000000013242121321750546400216650ustar00rootroot00000000000000PNG  IHDRXwbKGD pHYs.#.#x?vtIME38= IDATxԽi$Ir%gu3 3cbYbe)kKbR(7 ,飺*+80SwȈʪn w;T>}C'gQ`p{kl\/!ǡIbC;7xXZx+Z"WPSʨ:Ql`v;0hHEA  PA7`wI{'m?ް' P wϫ vCv6ȉ! T0 ) s)0gXH1P]̺QKX Rڴn&Æ@p]X)G{2^e'h1 Rˠo6LYҀ6&J`\r\_ղ6|یқ.{%Pۛ [۴-WCG*fnjvmn{Aa-P 7ۜf+,w\Nd-B?S_y{x"L0JeM~2tt,:fBU Uxy9fbH E]bV*Y̓ΫX(ke⍇~Q(\H;oz޳778g]luڰp< 1 hY. i h\&P   G030"AMkjCikc)"uJ`-V8X][BP@hPӠZ[ |ά率no,@5ӭoP(Yվ1F Xg=7F `4!CW̷;"۽0,h7~;]^oGv9Mps &{4H3Sh\ʣ=JVEOe)>ԈiQ%aQ]>O|j?]rR]OGQ2.Η򺶫2RGlm#\mzLmGb.܍~XrjV~qpkh@ ZMrތ$ il" 0$@+A[%k"7^o5#[B @6xQA IteEM ̑p&V[H.̢}{6KKzᭉpl_Inv >eSxՐVam1_dl4kߛ# (-صhٶN8Mpߙ&F F/Ϗ`X,u չ^eˢ|yqQ0uPP'w`4$qGc|ճEOl."H!v70i6{g{ 7K^u/6(sNf<(3'E`x" ZR̀(ָOMqbNu/.5f*Xl:av> O WЃьͯ%Qb 4 !(Pz44_+5?pK,"/{o(mܙPB&D=Z@PNϬx˺ņu Ekm?W3~Ggu]ַ3>O?z07ICYUx.)]".``Rϗd4H_͖_8y''/N 5PO, Zҥhiټ:Zг".v5 p" hn} Lf3.`z՛֡mlu55~uL@Dr@GA*EyPҬa>" aAYFH"(HԆ&wk PBPNߢz5\\a^N% &.𤁕a±+ՙF@Kpbkh[`kX\ˆj|s=c]$W@Ni~cj}6fWȀPeKo%_m 6PAUۜcnMa#kd^ $L|:R;ມ#\d|8`΃ó@VuUK$3Hk-՛!c1+QDk#~S5_#[c4"rס i3MJ=#ۢTo><~puo:[.k4Iӑ#$jRs1PQH@X'*C4el^qQ=:V\Rӳxh4Vuz4ѲNӤ*A6Uuy{cwrk26oq2nN?d[wzͿaS^~"!nayPfXHh"V'IՀT3!w2NXU"Z{vsV09bn֬zGu "jێ"3 6]\4WkVeAFCLG޲ ӥY^&+K k  ;7w`BvĦ3P;XN^ -lj6({uUz*$HݒOtmr fqGPZI-_Qfa'ƅkW]kS'Hyߊ l*ۢ1& Vl}<{?~?u$bx GS*hգl@&*Kh= EeQ0YYTU 4DއP ħ!֡#%I\,eY$ӳ僋˫".TmbLERVꈝ`ּϿz[̟nFH$ V=rleebVE͘|WAC4 V *PJ*Э^ʶld[2U7J{U'^ 5,#f DdB>qj)}P6k$#xHp&p 7-;\#pnt*GX▤V<܋0Xbq YuZUBd 0*zc[X#7GVz%? ]5_Un-=[Uww 6ܺH4Y0un5`(XHD3ͥh?겜]]a!M|+qY]08qx8'8$9鲆FŒ|Og_WNx4W~20FM|"pHJFvԫRa2DѢFBrA9OxzXŢ.|&LJ?{ʔ\L;jqW!v`Mbz7{oYumq%[!t'҆_F !”Cf5Ddb-F3}䷮;*hPP:!*f3hh莲^Ju8 VtCPrriȻ-s{G>>??EBXm*́"^<5fj37$u{R-bl;) OPrGG ղڟ%/ϋ^,KU5UuBp#pjལYeޓftZյs uEQBgYEEBwp^hh2}v%`2$I' #0TLPl +X0m.cosBf2ͬ@#N4E:̦fj7<*R KXm5vAf2`$4U\L YGֵO.eg -o:%]ύq?"+V6}5^w%hTӹh[nO a% >"G/ %0A8U5FĞi+sA)p/?_2W!~vtep\|ibDQ3#]uMg  K{Ss7^6 .ZD)(e$̩48yF†LbQjTaP4m9@`{/ Lӟ٧_ ʆLJ2GL\W10KS悹VMSmڌk]w ][Pux"NMm[ʄ6s8 3pil{f%oN(:'-5U1"'##ވwgP7QO;q׵^I (Ԛ:Z66UشCR IS & l9LCJ̧7>Wvb^Ko3P( ;: |pm|}fd ^WX8٣>F,1:CS uŵhn~ti偍E[-6>Ɏ%-=[z0J<F`؛d'$(jn`r돳O&e 4d0~٬OܓQCI\UbxV5_T "2V~:J 9`o^$I"U $EmtHD5 ZC^ y(!1R5M7=ճ|2ԕR&C 1jZn,A?N/>yٗ畦;_??>="y1eQ(]ı.ELu4ZVVb}ןi$21 1,+ ЖW:?~ۮ]IMT}(f<)vr=ɽq^N8-i /I0O.ݬ b،fYkZjX<UmUm뗳:|V @gނ&*n#Qqq IDAT $i!Ο"RȺivcAȻmn(M !DVi s3d:b6qbRQSϑCmpDvSґdE}n+oOSu'_TY(wzuvHF~}uW5/NH]ʡի:,K8t#*ݰWjj댓#?|{^^_^]_?>'V<8+_}yQ⓽tod0G+QYVHP,ڐ.:F+8 r:L?x?~6__~}2{zճOO.??˳/>|q6(峳rYF^TiǣğK6b]:.|6H( ~PGOhYw _=>~w߽P%yRj4䃜Ib|uQǕX˵u>oM+wnP0&@9AqxʔNhH;ń90%jW_9*#q끚ͼUIYeQJVƵcU禤 m}soCZ|t/wG#"-ȴ/ vaܫC-9Lu ŵY#I!kPrfƗ \/f瓄/"QӈSbiH{r!>v*^dM:fklW[GB{x(e~%ʀaG4(4!l5GH)l&#wpyGT˖Em ^a8ɅjTac^(@0tBjovsp_s?a{eѯ~\_C-4z~}b4={'铓_8珏././O}`-Y#gu0Nz7$ۍaʘImyJ\J7nD"R\z*Mtܤ9p$fFGLK=PuiZ.곶i+W""]x}7#jiRk|42^m#^gܖ;k% g7#NQ0ʀzjC4`8N_LF2zu2$I?iU${[{{+Qp0^_Q5 'dCNkcg '=yZoJHޙ%>skWOwSߎJֺ'fBS[ Ka~@ $n 1Fl pCҌ벨wG'c],4zΎ~U|bM> WI4XҢEReWʋeO_^;px>㇥? eZEbL Naj?h}c6'/e1WuYc5̒$CUU;C$/>{1 O=xh uԐrYdINX]a&Ί8?j1+"q͓%DΦw:U1YOk7Ӣێ `oɦZGf"S!3dH.;P8ûp]F+xXʈPZ.p mPtd+,]Vs.C+a+bߨl@fmaXl۬s% vȅNoW]38UiAHVrxfh#t]~ld:\?JTp(x@ ]a6p8=rpq] ɁDUY0YxTe~ۑoXRg4jM9$J~=s 2.+ F"6[iBn$nxkݷ-[x :O4M(0\N"{-9_m ܨЏ~4(7P}qZ\.Vwh${O?:<;;?9>瓤*kTd4}N>9J܇<~KK K }/~yv`iG?{'{{y?R*t Fii#*[ewޟMyE''Ws$>*I*XUYI\6:U-_>M AB`B9ofih4a>:eږzgkM.ړI Y#GhZ=-_zz)`0]^;8G'Bh2~UUYLò(tG~d7ٛżK^DGĈD } gz#^ՆgiDtL0r %C,) ²mkLm-r s=m-[bI 9YI#iU],p pzfböI#޷y*"(L_dZm\K9 #Sw"ClI!RhՍM^@K2i(v|Ѯ˩2%kBat??]O_ԘUO&X 1 =%Wj6hMcTyxFdtl_̉oMHhIă1^V?8H?)* VvjX,Ԃ} \x\x?]TUXVNDPi/FlC6vH ir[@J 1 `莌Z7+p(,mܖhWi[Jt ::ԉšCڝVLNkiQzۅ%ҊRBUSU5CPAU ü 0 nL [ LQP=y|]Op|O3ܿ1j 3BHtyUUVl䳯O&4MCXgWgO>8>ݛ<c"1$ #\DHDVfrKWH4m+F"mc&Yo Sj4g@ yxaȃ9AuVQR kծƜGzM @C.PUWD !6]UAB4RRF F}Q(;JD ~N9^ٶ\0E$kb+Yw6rD|NZHE<,3 A1efbD"hV@Rr$P{{LuPTb&]̇pPUA.39yebY2Oz$NL 쟑F-^rDD{-MkT4:kE#-NHpf=mz5F Ta9pyk“Ȑb6ts8)BQ(*CE6!˻|?ɓ_O'Qt&L4`0E3U\U*( epu2ý-kɣ=c{3I8)~⪪_<~??;_ӓpUyQ0I>p*pq<̫?'|6"4r:'c2bVYNFP}qKIҽ/.Ӝ%I!UIjjLi$NOOɓGq9[qt3AF19h(*0H=S'P5VuQZ;(5&D;zTEPT֝MYZ5Zj%kXsBjf‘Ьmlq6D |ܭySh?S& =ygczs'PrQ3F!I&+@/%c5`k6g; +YeFޏ)f ɚR X50s֬upɀ(ijz82mԈeZ%'KxtY0ifְA.)0vSR+<}JC_ '^.~>v57Mvq8H:憛MtT]6CGςo-b4K"@~|"kNS$~f+avgx 3YsvRWp 0[FUdaVT]k8 yLh97 [HǢmGs/:Ø+^(DNFٟ ƽ>mK %Q"bM<_ݚW^y{)*Ѧ#9A0%\Դ\DCWK+s' `  8J`mY_0A38*`XVlX"NoUouٕ]k$O<$((mCˏݗOxedmꡲGCh(wBi*ȭY,y@:N Gw'r\Z!crL "0z``nD!q,$ޝU8~޴ol0VddL6$ ƫt,I/A2(ᄥ5 #5%u"#_@O#$dHD$v4 >QA\A] yEfOE22糃@YPR XHyZhOs eP:dоU86l 'f- mIsݞo3_y_yx~BmͶ>P9\\2 )FQ. ɻpl'<ٷ?ܛt﵃r?='?tٯznl7U*+&a~.nۧO/~*v}d6vV`J;8gս;'/wьnb@Y =rJNw, r Cܬz6I,5MariESPD)BaqX̧guQ>ծM[dѐlN(S,7z(D0d֏D @e,i =VT{L{qOMeΊ#`ω?mt@t-=+7++@ 'rt2DD aU^z,$\SN>Fi,\I `Nx"ij ݾo6,*( 7ZӒFm%F$j]1lU9̓Xm1Ay\{1 ,wz󧭁x0 Hcݝ7SmޔM 5ۍ_{'#T`( ֞ĵiO(Lw:pxÛS4\8Pi o/|_0;Pd̗@,cu5P"u*tgR("4 ڜ$f-Wce%rD*1[ GTuͅ :hf:8罚v\nRm_w/O~w<;iutz}1=?ݬdtmI_xy~{nT3Փ%R;߶}Lɍ`mOG7nikcS22l5D+0mf%h,/HIܪQKc$UP FoASn08a$L2Ev=D'lG+ҕ"GwA˾1/{̢"?ž5♮?-Xx۟S_^/(6%#:[lf 6qQB0$"0nԢqVЯW.7 x3 DE58|qP_=E00z|\LlO &0Dp `Z 2UN+%q2(,C>&D`pY곒( syPG{ms ;`L.PL"5~э}ըQ`α+b,(,3ʣ%kxIƇD9Kgq_'G"(| ZOnA\ȢVfE~bᣗ?~b9j{7N4;ODlӵ0T +uJxl~o}n/W?Z=_vH@#,KM)jrދvӛY&G]W坛7:[|)Ew(,ahJ5~__vJ\Jۘ9`_k% ~=OL`X^A!*h@-͞{AI:Jjt&AanopD 6{ 7ƙWZѕ4C!V#Pb[5L3nOU&H`TITpmԀ|`CQ,L6dkihba3ѡpj|JbX_Jp 0btǻ(pix렸5wMaȼs°iccX?oy6ГL:(`zГ5E*8׶9"kh@|YO_u`j/penyW]%CyMF|۲}#S%08TC4E=k*oL`& 3)?mPZSkJV\Rך- fiFGhsi©0SE.s$HGֳؾ2:G_hU{bXPړo08\PyLj0&"UtF2s~H^& "D< f{e-N 85D`Qz'R]gPYt/k4z)f:C V@1}CBD*̍D0΀_TԁQe5Xݾ&HnsyzvtR&H]\n\uPz{z9ta"6Gɉug_o6i~~?]|Z4 LT*HZ2JrR'~;-&>[~|y=Xg[}֗15mv>?͛|{~nmPHS`iiC(ʫN|ض.Y"B@<g7f!7 i"E)t\p^0eMiP,wbq8UdvBU )feY;]"/°/"hb,k`U"px9 #܂M2V̄ثVҘ-`tM74i䄼:C6HE7^^*l8.<;`NΘ$v2P&*YdgD{#_ r. ka3DM\ޘz㖚80H-ҚJ,^oayiJY:ߤ|.ufTJwJW?f8 >BjDk,W Pgھ1Y"yCO4KCuD#,@ɪj7"m{{;uMvэp-A1*\p,Ut>=:8]E5DCҮn$0Y.7fR6n<:lB_҃{z/g]d>yٶ42+LNAi`^~gƟOo~ˇS'.bZfյ|p\~1YS##htFL"N'L& v蕾蠁еbRŘھ&H =5Mb/.vWz\89NƨY#!v`gvw,E5o{Y#l *J)f@…SoͲv\!oA`0xF:r(۩C W4k<^9QzG3;5f_]Z 3NQ7<$[* 0fkCM8T`}tk0X(zSy>":#-"^g'y__qe2>YSZVt| f GU8p8iʷg^6;J15ՓyU3emN/Xz30qga+L>Β-.MJgnNڶtN8Y0m;[B2SS3\]@`Ρ beqɶИBdCRȆ(HS ÝpA <')^$LitGF|OɸP+PEfjPEaRg΀qB]g52,lgp9_ rj][CLaL(:8PZռ iiT^,ZI3V fQlQhVsAmc#f!u2Fj:XJD0hATEFCnEb^xjLl,@Rh(gf>([a.̛]n<{ 0p*E*A(}W7<:f7MT lߺ{td: gy1:t A{SE~owV6˥sr;~qO{3o= sŴOf( LDk7hOw̥!&޹{٫0]/&H=,֍kۿo|_VGnּfIE/l) $o7f}m fT<^Q_WYБ +A:5<bFe>ܜ5`4UAXfwt Jp ▯*" I@I87 .LK^.+c8A `O$d)02F^4 l6/7K9p£M6(Eg# 3)Q8E f9X0+pofN^vnV+fBو:FUs獀U%s4qfȠV eu"zPDo5<N|;ŧӋxm.?3! 8vTv͓Ip,&W%4Xnbd(+avm( ۨ6pܳVAQ.?ݶpBLPLT;y!^j9qW[1+|2=;fw[I?-14 8i|]ƽ|!`Z˽.FzKüp"ugyo@G*vjv?y|Aݶkxf{l^7~Ϟ>}L4飾XB<8^@Էn[nF_LC|v1\ #u^6M?4l6~70߹K??t|@pn( F_na6[TE}AI9o~ jҪ,`*Zjdiٴ~m߽l.7)} HI ̽r}щߪت>!* (c;rdh-WfFo"3OR+C tqV8[)BTh<\ZSTy :EcH~V\l)U{@€V B8AQpBL ̉^ĤD"SU*2HS,)0W!L#tSsm7fӄ%ʢ(}oҋ6-;[*&@%Ix<4.2!()v`{zq:|vW?۾jу&xaT(E7g2R@_SҢc˧\qL`a/T'$YYx$'tF lrkEmJP[0Jet |qx XEnxpl -'w愽^6[vɢF3 \/J4N4PEC+@q)lMaU`nQۻpEH %Hي%"-#ftÀ]RlK:B *c}^{m0ҵ֜\hX%&jϺt3 hHuPO`6zX[5aڕn"̛F^ 4q/]`Rvk5? gCY|z ]m?ܘG_qŠً.>" Kz54ܝ_{s8#c7 RX{//wz5~ݓdݥ8ƍAqiӽG/6DҘlyk7I}ݹp0G?xxo(ѳUfY tڢ8;]/]Y>?9Κz NŬߵU6&>zwp30'ggO1"@ Dv>/h4YgV%@Z8 )x8.BA ,02pcAU8% y%Ckд6$#(K%X=Fp_ cW"N k ׼ʭ [ )TFOUN1xpXbE\M@n;EY)E"9c]JdKn.FTcEQ]%o8Lg{Td-5_ w[=\ i- ]XU]S`1 >&;``t]U3>6%&  6Zl;!Ԍ4~1szpDan铳͍; `O (I99 O&A^QjBDrHÐ"]ǴEu`).Y? CRL@8Z1Uwnؖ}8-(,Bz'FX^N ѓųM||w5)5۟aG-o#\dqF=͐}m6MavHe㘢LHL jG3m7F|,Ri&sTUC#at:fov>]!JI1 d]ȴ4`5ܙNQАSR1 dJʩ֯icρHr Plu<ߤhy3VMuJ>$IA39kr=8//gե.6ԁjohE"Y8&䃧..y}WU?^Ma ܶ]7yyI??w7v]K;w?l}OO7//7ˮh{s?goyS(0LפnLJ;)ʢ.}NnÏgG?̃; ,l`@$ArC%YB6CD2dZ0-\82L 4crXCOXѷḳWɒQF Tk,ZJ$qߩWGdCH܋s#7҈/ O:x CxVmdQ jakuAWS 7Ч4D"ڞkGe? Ĭ Y%YTk|8^j,i8T!na纎]yQKKBCrjA)D$a8M34:x! a6_j]ܮ5hCU1wfP%˹,c]B\T6M RJi IVqJzh]DqyCJi*Xpz\k?pn/ g?_?~Խ#+}Mi஺PvTݵs7$ʹ< vM2B& `DbjZ3ebalUfԪL GR7/S*1ue8>zQ!$APz}VnZ!JT5fmt G4C s|pɽ]T u(0`DYEuf`i@H|Aa,rTdWqhEAO b) >)^iS" ۴)s\tfǧW4D D"d~R;u?U >g T k*ؐh7L:Ž/waӦl<7`bu,â,ǫV@$Zo>&WQDw>,>'/?>:])|Ev|}s1J/={3_n#jm 3w8 }yݏ΄L`0MH4F!SmI|Ӕ0#^Ŧ//#LX8Tf6+ IDATSE F GMUq1" Ri;X%U-iM6#u{ZZLfpFno%Œ%1 G, CcA)@fcF>J0hfH m"q@`R%+o;K\vX ! s0M̔41)ivTW-k=1qMg̚CG53Quh(PP>Ҥ!dcz$\nNVT;tC^ 'B, Ty3CQ!L-G]chT bnA07kwzY\V}\Ǜ;o߽ySv;Lŀ~ymhD=^ߔl(ԂaF&3$3oa[0'N_lfM'THb^rUx$F1N64K6M:W]guse6j̅ ,Z\ t1/ffRo=X >jl0s£Ad];$˥#¬ɇW^1I=)ΑƤ p],4MsynS{>~<>AEJ*j@/'OO9|Pt~v0`i7o}ڛo9lͪ]pY<ޜ~f_xxiʻ''ÀHQYQ:}ZyIRY,qbZwMY5[[ˢ,aav}R^D 7}ŋ]g^5jU];=~yg\LUY/9LgwWG~89>}_zpxO} ~X4oyg޽9rH\4XFhO-I/>?@x~x4ds&! bnO d\4ހS5p>$he:jYwM"3^љBePD>YWV"4ur,}Uؓ53rNH"kAu؇Z^"X=)Zۍ5,u樚Qf8ٳU9( Qdrs8,_.rml#OWvփLN~{@@aΒ@fBϧ/P}/.4Z:md}lP޶2:8/1O8$1ŬYzpxzb>O tML96/a? jcK +_/[|_[W/ͻ'^u͛oyt~g?>o}P[l]7wE}xhn_6ˋs*v{A%NTxp(m,{__8$qeR$<98"YkqcL0s(.8z%HҒUoN:Wt=]G]Sox9XwgM81}Z^8^ MhF fͶ *KכXdg~9fv71<ȩXTTl-Ѐ$@FVZi?@;!V7"U5d{osϳ "O}ߥ7mML!RqYdG 4@LB옼 !N:(61*npB9$1Uscǝ Y&v\1j o%8.Dc{Q ` ,yӍ50FTcZLŤ9"s K"DJ6͋p8 n~d8kc43nZO: A&MK~\zч/Λl`GPopwfIɪc,6,qAqY퇟}okww~w<}rz2 bV灋;]6Kzo~GG?_} .6[iM'e=rN_|5/O΃sr_~xGO׫g/mQ$ &ILi9}pr!Owv~k9ߵMIXA.TVI֪v(k^{|"S1KŦlLf0L(1h"X61t2 ck1z49{?OtV;C$VɃ>zIJ(}LQh?^Nw'Epp,E/,7LU)Dק˿}Wg 7ןɛwWgg> r9[i(`b,DL;OUɝ]&[BHZY8)AA%~ԏʷo0; 폞!ؔ! < ud…O>-e5TMf ##33V*=we #2cEr@-{;z:ײ;gZEJح̶Y3;﷜WX,(#`G{޿^4t#02\]-(VuK\6Fc;I\g6_ea ƁR"a'`Z+-/;Yԙ *QeM)c8OjbbP D98 SXIxnbFr ̜b{6~0?S&%GCc7yk;VBslb7VajѬUpF!=oګT@@`"@oV>ǸV NUZS cؤJP`xݚ&s%;iן !"09“' FńTЬ~nX͒pLD6s7s@6 *2 H) 0.bG 3%o&f>͵A9kH KsbdYRZd'1}CM՘;L9׋@z`w)J1+*b9k拧eDb,~˯)~m|گ|ego__7|1?WߩV]=|UESt"_]s{{^Nh;;]E!fߦ֦[[28W{?o=8I"}K5*kQ.΂Ջ3gvfyIBuyL4 a f٠"*AK:ܢkA6a*r`暾 pI "j94(^oBø?B6!xUFrFeZ n B1א$buINÝxtѣ5>+]oxЃ#OIAd efK6ü*PRu^,:H YA01kvΔY8 6CBBP *2f6riT@U0lպbg1qQ^ƅ Y3*DaizL7xW"vēJk`~Ɲ=~Q(! tg[\WPX4vUG|at1q7k4 Ѩ3V{T\0k261N;pbi YԜ%FAX|gSտFYg w Tp@l?_mR}jX.J)xˡS,lQ&AUFp ?pMIUo ĵ;TH00%(^6DՒؘ4)0C5f2D1a[!*dD3&bf|IRS 뢜_.`"?Q1۩䯼༧"վOJLP(.?odӫ,ۻbu[QZ&n+o՟9?;_zXOnw~wo o}?w~㷾ß.tTIb;_/]7[Vިݙ:9:2\L/>Z9|>ҽvUI:|'G϶fS'}|!x׮/o&ۓg'ga_ ɫ?c{t|X) o0^-,0CၑlH =Y0whuCg 9kA6CYܝLJ/,{̯RfZSs[Y&K 6/ݯWo{[:i2;o/_ W7 O>~uf|ٜ7_ыDڳ }ddnXG˓Go9^]_& (; 71%'%}՚pkҧ,Ӻ)YՓU+O>=SƽKw4N(hQEHC\l/NVUamY$ U(TqivlHHɃP *2&hJ~si>؄ 3'F^G%ᜄak^Z6c "v=am6 t ,:C%vi6"I}9}p]R5LPC99OΫx<S(dKY9rj예y~49vfUV{Ug/8R[?Oݿ'q{SV9o>ɲ$+TˊW?O}gOI2Njgzuy-ӫO^.>8^%@6˿L 5]~K"צi>ٓjgZg'OlS_WR?Rw{{6?~ugo{}\6kc.nKfFIFgp0DH"]Ƙrvt» ï C^\¯B!>2{YL۵p٪$%z$dv\>]v*ʎҁγ^D;;XzIAeQz &E@Cᘼ! YyxIQ*9-Y yGYN*E!T1<*`SguRvV2!$g\m]wg~e$^ Vx"q}{*ۘnE0bV5cU3l9IUc` 9̡c8Y՗TLL%54V8TҠ~23ȺDCi`h bIU7uy ♼nB.ղ<6} % Y Y1OJp,jY"ȠLU At1aq6hJ矫K1qz-/\nbo>|no\_W3l\t{{j՞]^u^w]'";#WԫvޫtIDs1IΡ%m["/ FƂhN㲘Mȹx,4Fv,m<{.mS9%V_jM޻x={[l:8r.K~ˋ%9{nWڿ3xzd[zk !5JK f(RJSU1 sfZ&?9! ަ rb31#%1ʛ.:"fɭsd=eNUm0PCR&[gaHy:6D1O>^̣bl9\ bP;]f^ -Bbs{q`aR"wN`&]!*a3E Bբ@Ҳ9, @6zT^hH)AbFLF䆮qI$bHLg?,`W"C]yU'1HF jsTCeuEF dY%MK_Wmmb 43'-zmH᭝W?yNf:Z%H-ڳ([>)FZ%ﭼi9@բ3lP=QPmA)Z4g],9*DAw&qd7V5 0FW%gȄDjX8PqvLÄMN' ile~;hU6$:08ÌݷgEɺ=y?RgGGoM'UqXGݏ]V]3={m~﷫oT'N_!kj^ǣqUsVgaYUL2ab)Y9KV]VV۴ Ty,H#\9圾'QK:ڳ XX9zEi80j<ɐ$^GP#bΰy֤{a`S+|CF.+n2U{a[:aNK` *vߩ]Lu'1$A5攭C3{VTrBizf9 [D GY\S$1$QaXgYd6@-ĮZ_!o+LFt~7/i]GBDLIWcEIx*Mekd[BNv j4 )P &^L?qؘ,E0ڠ-î*CJ2p9[ #(\&EdD)kg\9e1~0eYءS2F:D%`DȆ94Hc /<3 o8KSᜩ()1Z͌Ǟ8 A!cS]`kR\v9:PbeX17g,뵭ᄍhO>~ã/=@2rH;o.=Qc :*Иb %eӋѝ;q]K;Iuo<4qҟu8L7nỌ|ՇW14Y؊lowUF}I}{wtبr8;9Mg{GGg;VUqkz{7m^W{o}1|Ujt2LѓW{(>PtzD@.p;w?cnP9L+?e루s&9e cؕLfDij8ڰUNPGc2dLdnyӿz< ޞ.LpNEJ.< FQ񲵂qqI['Qɦ10FhvA!I#a2iV1QU1r^KjZ`WITU셢UwaPp9gx"$16 C,􁲥leՔL!j/\̥>L`"l# gcGD XQ"'#EYRt.$\\kR2{ع6cx+ .(jrҕ]3TJNώ#nU8N:nOw^Q.LzA E`*]:pwwY泈g*W2 8@B"p@/:bC"A+&G$@@F7L=3&="[JIv†q{M Y'`Jj9}2]r@2l`LwMlX'G6珟lf ~ M3ق`RYx8;vePO)B֮ %ׅKpd F6U"fp9bM 2T~u{lzrMǫڈ"G"6&wf޻E{RfͶ'R4.v㭑qςYV.{׻[ۓ4ɉFe][oݞwp.Ʈ:Y븺U"4ߚ%k{տ'W2,Wf//2ulL.V|y\'ӽI9ǧG(7"XDygJi;EtR:7w+^>E_Ux<󽃃g'?y|躻yy4z^ɶj־tٸB@j^dH1c$daD)M=Rowo;]o/io3 }y?MoOXA]7n,ÅȖjP) UbbaX)^u:Sa#[T30ٶ}yF[^u=5Qlkj1B3ɜSב"vSOs~: N Cf. &+bL^Sdeu4I"n`6uYMhkI,u " ²GQp]3̳*WL-J1G%G@Өq'R+=gSZ(eww򉙈c;9K6} *v0PY;k%~u޻C[31\ٷXo5Qժ;]r̶3swg@9](qk~V'OnyKKɢi/9ƀYa M<V(ؘDݨt̘pCʪ>=+٥.v5hAV56hDtݙλjbe9ach׫uVAnxZL +X2Ҫe-'Jlxaŋy[y3)yD$,(ʔK5.;\gKpaدy:<*"(&ӒLYMugdc3*۬lr6SBdQB(gdPeC3H}#Bx/'C f#B UE4iQ4dL&F&:u2"E\ե(P8 5!)UD WRL`?GMcպg=JS(q kbV֎} 6+r! 3xJzv2Ps+ڛMd$;^_b蹝s_./ne&7lpalm;ISQj=\YK[/V7&DhֶXY#!DcmҀzA՝1Ϟ=s췾7޽r4Wgpȧf-/uΖe >F_RVч agP[zyhLEaz] nfJjUB'dPChSsjSiU0}آS`n+LP.],E0`x%j\$cuұ#56 {qud2DwZ4,"2ɼ&zS; !,Ui XrҦDu.&lVJ3Fɢ15Ԧ,L9R\0)wmQsR0uX f }G5M(y,9 A誗6N[ % 6Z0Ɔ>X3*ʁ4=r"I,*-'?k`X*QYaV]Ox42蠴 &5ǫ"KWɂNiq;ݹͳa\bǞi`*[KQ=i ˈ5|jcȠ$ 72t&դ:=3t(̲+ *ݳ|9^R xw{V\NZ(ZcwJ_,xBƜ3İ*ci:(ׇ#EavQXkZxlرZҍxt@PEgѱ㡱7uMz0a813,}cPQڲj;1iLȪ`xư*֯sP1!B$m (ap#RNQϨ|Aۢk n> u*sZP1fmDLPZJg#*a,dYN"Xش IDATchi8'ȡ C(6u d|G~n] Oy۴)ÃwKԀ 9Ua!}&0A5MQQuĎ9FY'jƃPOݻ?_o/qY]{tgϽz_y۷y.\X=>njao_qO>qFjڣY[9&ॷn gloªxwǏ?r#O?ڽ'*JD2 c[['/pgk)ei\{wNWn+ޞ M nÒsq_1co1'bkX)() ԕO!]n Q )t5$P2ՎqF+B0 EҡȀ!hk\"l`<NahE +3G EȖ u$gY2 &R[3tg.Qlz**^U0`zk.A!Yt5LxadN"T9.Pm1-͢)`jnY!Y l:խ/I),9 ;Y' ĺ 1VbzDr۬!{]cuLJmFN."gxAe|J}Tz[ZC5g(T@&SKoƒXe MY#) Re~ʕY{xE`Κ_;2vz4(6nҲ n[#kL[>:xfrE煙-ŮtpNȠ0n>:*NPyGj}eA#P ``kJbUd]Oa14:OR@Jj ٞ_1T5diqxO*CM ظQ:74Qc;aDr 4|7(#cDd-o$iYdic#SZj8V1! բm جM!)ECؘXT Dz}zÑ`6pÇ9Q!)AAX(")QR(21TbB-*)yG(grdD cAN9p^Yi sI*,BA=K+eELipD)ks>>KVȨO^?ZELޞmLG{b+?×;3Opf=HH R%KdMJ9V}1i*!nݽ?_Mdbyq:B]6r⑋>mO\8Q;uڭ#ɜ,7ʼ[;ǧzB2{FOU?̛m69ޜn.o޾O^:ASW&Z?ąB3pfaEswmIfN桃1L.uaQ·w,*6K$*MӴ4@?sߺdBahRA8T5A% mFV:¶ 6VγM!aYЍդJ&c[VZ }T" RҔ1'2DCkyͰ [TE^^$FIAMG Z$}|mbQY |)C\3 SМW zW/uv{_fqA9 츌,.jբcfruX.AiՌ6욹1nCRKPѮ 'yՆn&央jSO=8q`#W7F9WϟꑳݜNoݼH6ɾ7|tZ\Jyl-&s΢"lHRISJޙMiE.+b IfeªQ6>6[ht(nymX/zܣɰLCKm/vw&e2FG"΍-G&1AaB9*3 /: I31B~eĖ0L ̃8cQNvF7\m]=33v<7B﮲:HXRjhLAEr u P`Dl\غzSO|vc3s13z{U0ʂ !:2L%k$2ph!" g6=ICS ڒO]5~D%,C}2F%.A-yG΀kW+3$ǶMTdd)I(u;#Kw7d /Cϔ](eJ0(+rXE49?/)Z"hG~> L2oo(_::KrnpbN (m׏=wd;*;0djb" T1JRFeQXCN)@5Fm(}q z pIɃv#ax˖zh|g]4)xVtK^v7q>h[7kԶFDbFWiaW][|yAQ V-'> BQ)a`P9>Uţ/ Īҷw 0@Q8"B$gj-kmF%"f}@rN9gyX6kg6JTPZ@=MYE#LkRo  J&LuXSX#)1%b%UTU --f)dl2xT "&PMR9H0dD%$/<ߞu췾PsnKi0n~wpX-g'["q=kB,RMڔ{Qx8`VBI.ھcx#BB.o=1'mfea(O(˝hwktfstagGhG/_N6[gw͚)gTŰ]g ΌUEu8u5.c9k˒]MQmcJv1AAhUO_UܸC$A^:4.aU='ԄG%eElǹַB'xaRNy}rnd%C'Y{-ta0g& d.jIF hDT UvooYb|1,t>WhF=",;4֚͑I!u(Tl6t¢\ːƃ/S]̏>=}ijnTs{~^ec &$`DpzRr"6BD)zD`}"g7,_B]$%Eve.B뭾8JC>YuE֧f^ CxSul Y+EB-k)3Jh~>- ud 'G7MBɆN<5)=+v`wBO&X')Uw&F# +7ڂȝ!0- ul\`{ӸvÒ"K8TU VoBpҁu}]*aeKfs. ^httR87hIMtd J**:oGuP"$xFeP0CF.i'KӒ^ RK$)kd笵ٙlY+5Z=YbH4;k!BPQTUV^ې@j,'ca *g‚C?,kQ%Z.-Uܚ\)kȒڢveݦLT`u$ @ J"RkXcjR";X-tQ*4*@aQiLJ c,)jXYps[o(yj8pү|aiBܬV!z` ys76'nR/S1.~6Ţ $W '5wVpl~pιyl0K_xd.ſVq0z0ls:?#kB{wsNQxʆJď?_+@6֤6U79|c[-7޸؝ iWκd:/ht.!fpT{(yHt}V.qOapТKh"N]=wq}Aǯۙ7c 3]r<{{#jkrB̞GX46 >v3zj2ߘwn}k߾25!Jg/o|瞻tk?_;]b~klR:9=r,Rs|cNVm CGARQ# Y g=huT/sO^w߼~q?t'/G?v%ή`1VA!=d; CW/]9UVro^; P)JKnrr~ܹ󻗮;~X.^~~n{oTitc`x<392)(L/Ĥd!.j]یRׄL]+Π*TAd-VAn~I!l< tݸxI<)ViGf"(ZUHEpB'UĔlxlSPDEG%J,pw6 kQIterl\۸'txO[~Cs'9C|(bgGs]e)HDCĽG/p}uRVNZ\NO[/<2޹RSӿ}4ss8\؇!5(f͹)B-7m^QN- ,CT gjG!ʪQWUY׳@@#nXٵȐ:$ 9[QJQ,Yc@eA0x!{Ҭl(C$d@,h8$kXRFlȠdk9[É>wS@H11:yάրX$gɈ˱G%5 {gdfNxn*6C/ ejVW#*")ҠpdZKQB 7*E/s{7WܟʹY|VۭLe{Kv76b)Yﻰ*Y 2 u,ȥݡ5ć=s}b`T~hn^hQsiQ3f42xgOis|p4[w,G,ڹj;}kc]*V]7Yt C{jw>)vNLDPo82\ZSs4;ydI*Y55 rD~0Ov6]>-GtM PIEWN2,Ik_m[Wu)AM 'r /ݛ73{+awql7*3 9g/Q9&۷O^ŝ_o?'/o}7߾+_ş~ŰxGMID5d/S?~},"HMKq_'yͻ/d5Шyw_?wxpO{7߿΍KYD- 6.&017wofqXwlQp*6`DHkY'|Ot4o^c_gy[#GD|Peڨ9?l ŪHi19k]XԞ >G{ f. rP]eԆU<% C լ -C%8$ uAЊ|Ko?Xv`Ն {_{sO)}&kKA%5}:iw/7o?UjjcdF[lMwn_RopS6tclywO0[U&"t͊%]^;[EpE5q]х||<){+hq8/_rfn<婨VuRb iՆ3&f|XW"iZ'e-+ qet(.z5TIk'. |UJm:@M |~+|gW^3.Mad)1H?twŁ֖ Hwl{s紹G믾?G.^zhס8]nРx~S_yozfˏSXqH uQ0998hEI1޾߾v 9w'_{?xS?O],xVeɶl F2Tx+~}_o}]c>'? ~駯շhfw֜x};Pi4]{__xͿ}î ˨ bR [C",P }V .]{R2/ȻD".|5ibEO{X IDATBR۷Hqu}t6D:G?mKIQvZ9+*ޡ7ƀ#X@Z)j   ҢtfoQ8-<؀e F@K,ΰHg@ ae*&,%G&51֐wl2&ƨkӺ5r0yorάː put1=h[W:W++)lsfvt G+ʆ,fpt˅- J.ljFr}Ĥ=9dsxO}a]9]̻. kV~otg>;s&K!]ccaV 9(H8rE*Z2J’!Q*MZJ8Ќd8}O]9ʛw^Η8;Fe1" 6a\_k ?g?LO·2"yXCäV_՟7nޓí5lm&EG~oq{ѝ̈(*~\|Ofol͑މ|7&ۯ6t%4 ;V Цն/X(!zxn7nk7Yw+~_xճ>qӟ|ƻw&C Gpe'F*„3zW_믿KʈFk"X4ʕ+9;_>]OG7^F|q!Wzg?IoJ:C ,L49XҊ48&얽x^DSTR>Yjש0%ʝ`TVţ$F.u4]ώQif=uJZ'{' 3~:.mȨXPv[`4,}[PHB#ȫNàĨDTt4t)d:罹V㻄^K*]eıaBh<(ɨ.Dr0lyVaNg_d| qoun!{ecċ)^yw8 8WCCB%,"f=F,:]t9 KcladFUٺ0G 9&응e醙 MtB+飮jd](#J@!2QT:X+؈1p,Kc)K 6T9ެa {(y]iٲFUI}A\؂Jo[5!*@Ql ֠0D 0 TޠpTyfev-11Ah֪5όu2j0ldQE :MJ b5qkfUQ{w?JLLY8頸3;U͇!68Yg>t?rN]T'jk޿r'޹u:r~}yqw ȽҹШDȥuKϺ%Y߳VfxsխJՓZRkB! "@8l9M0`cP-0mfJ4ђ]Cupn{wCf-؟̽y~7nuʥgXwOzW!8*ye>QuzxUn&ɭXԓ0l#5i3ݚAё\3h-Vx%-V| 7޻Ή+v|m)=dOCYo?t{TQdHj\ٌYV-ly,P Xrf;TUQDԜCUN%%: NU(=TLX< 1׌IS>~_Ε)@!%115[Qo'|} كmTh Dc N ܽuXB;u HRU_uy;w}otU3UfB&Yu6_ EY|(*GdLU%kΪ޻"P ~? ?ݟ_}nkיG)ͱn"N+/>xUD#E?{oӟ_{@D,x".JY'%x?_/7plYԲ1QQ+tk @ O=urB4Y`D FQM0$;Y@c:YZg2. dKz.tkɺMpڭS O48WafMSWz|7\0vk>qeㆽ-ĢR`2*Ga{+uh$ L(0zA l -Ll!M0};L/?hyZdhCs;grZA1.ݠAu@7=2r\V^O~._(PL˗]ݖ|^wQlt4LЧt HL\xJMؘMAÐwfݸ.N޹ I7ܼwx{7$<(}ۀyp{b~&u7L'U;]y_dIr2hNw2L٤>:]4A`vTnHYk%1Qy90ɪcl&a 9"ƪF@]Oœߟ}K/o޾mxx {?ܗW?}G[󳳝ƐNWZ1d"aZ- s 8>]Ak JLʑ>O}/W{S;uWp: D k2e<]dUɆ(x5_#uj-V9>M߸}a7nܯI2vF>u>ݯ?|ݟ/m\›JFc=>;>|^?׺lNJ6GƊ1×旿q=⇟wo$ fl}Orlv*7d^uy=3M>8m[pu&s }P:WԬtvu15uˎlHG ĴQd;k-d?Hzvwjxz:;Cٱg} ؘn׺X(X=P{]֜K PR<;!v=5P``}gdr&)/6ԒlQh)SQZ˃Ls`:3ӗw#T2tNԎlqV^?nױ` ƌP-u%s4LrF5J" 5UbIU h(wbj 8R=[HUJMMp+E9+eʚN*w|LɃ CTLsfeOAC!ά4mOD̓iV+\Z$R 2N{:`xu^Z9Bb6FUP#+yJf*<'D33 x*,eYw"NҳJ;(U#,j'<ҕN_I qz1&W@5Qw.T"}N>2ܥ#kߺ+uz`~w֎ʒB:WN۳-9~9?kA%G#b!A|`ĵN3"TrVpr>CV۳ƙ亮vf;[ʛsc^mSURĕ@%Ifa)dڙ(IiL;3G#oͿtn4m`WNfPhTbFIJMN'|iΈb|aSSrG#lu(<2Da&8sySMW~hOZvĎѿxK{h7=9o~xqI!y,:HuA,*@2UlT)4D۸JGh)Œe s;^cP16ޮ@.l:#0&L`CsCC׫H6x'#c|xbw<>u&=Jk(IjNkHTj n|A(أVӆ;ubZ2{eG4XZ$S&* j> GY J4[St#6$ށ< jY -ePpG[ Ѹv ӘC͐$dSS4JET fJ9k"G0ENMT @6ihѿWv؜t(0 ( y۶ڊh39lhNhPeI@,\j]l5MT4|ʙKӒ9pQIh:ϝۺvywxοj_,SnwC3Ot}?+mۛee:먋hJ LEs6S5ƉIP)@yͿ._;Sg?kg?j$Elrs_~//]l{;[MpD=%pUٸPUbo189>K)M1lHyPpdg۵#:YOFIC/#u< Ѵ3yf%{Y 8( 52RQl]:z/ pʥNQUܷ&}}l ҡwM+>Ζ;*J+J߽1pegfWNF/~Τ흢8IzfO}Rwjh{?_t>b}Btuzf҇MѬ9RFQʩb4TEwC3QN9f&dӚ5>Pd"hJ@fj1sPQãJkRY09F&d{%eY1J;_Wz>T /\nc86&}=x4i6eMe2|s /"9IRr0U3uoAhiڰ:!I ި(8KQgGh7*B KJ,e81Ro7yʍFl[NCN2jPF= \u$Ƕ %Y5Ѹ`ta'S!1v* pYŌ!hߗB0]'`L f`hujlG{ϜEaoF VSPvܮjdNb"Kג#SrtXS4 0$[rd Q B%J79.Ƥ4*H` h`n8Zpuo.׾C'UY`<y /om5Ź:w^4Iz xѐWU=|Ԓ>/JgW+ Aj;?lumUUsѓ_g.os: IDAT-+Goӭx*HjoT:R܉n׫Ճ= 6L2`/ pԇ7Tڭc\boCq{ byQtZ$#&l죪RU8sPVzxR RJ lQpKʪ4"%ZgR*aB&- oO GäJKl7vu+>\_ݸmGk_|ڔ{?'?O<6F f]!D$g8_;ms=xC2w#Sʝo;߸ul&^fX| L6aHɏ(1WLq Md5&0gJNSXJLK쏊4{9xg0q p0RJ:jʎPد>_ ?_~\)(9-l*1EK,#5L2y6bbuIhZO`:5IMg)x*p`x;oJs9@"x] kC=s(;һH8g`['霩nZbG5ڸ}Lds̘@@p6VpF4S`>k,NF %N #饒?K6 &Zuj@ \+A$֬P,zAq{jwɶ"-x4dRW!:5?mW5#ip 4%1#UPiF&t9{$_icHXydT\pX"lJ90rEF&bNLPV >eҜ)!%qhKhDZΏ*[69oI*=MŌ 3釣VzԿ𹛩>K~+m>ظ%ӓW&փ;oܽ}wWPw|u ï旿S~#+rx3}owk(џ?orUir&^K3-'?3/NV0.xEۼc8H]\6%HmQeΑ7o*YU%#Pὃf2%6l9I5Q1Jrͳid @'"p5X7iW_J}scQ-)V޽mvHLU5e]6Pr6[Yһ9˨*UDC4fùxy6.*hŲhN;6j26=j:{)q@ΘԆĹ9Oа'y]3h C? v+k]&vy,Q,ܾ}_]y ׯ_ EâpbtyuC_z/XgjcX`Ч.aRRYy^n`A*]aT*g1m3̒RHcyM30#)~ 8J퓦L%o/;?]mm&zqt;jv939DQ!ak0YSڐ0N`B`Tي˜3R־5bkCL> _)C fItU +#̲svW匜U-6 ;B)**  DG3j j*k*|V[PCQ(t< .~H*0"ljs(#T.B@Y mgvd Uupd1%dNWWvF_moy,\qb}8r]yo]3fBM흞̙hLd{\2p.W^1vv˻{[N8]ݝ.ޞ5Tw~k^9oqcn<~tGj|-h,s"""|6%!Ę Nj 3J6" >G.+/VLly&&7srlص/Wv @G &Ta<ҺқB}觞y?pO\HSoɐ:Ni%p!F \v~Ƴji (Qya5dJQz4jPD.'GLf9o3w1-ɍxhnFΝnC)ϼt4$uMi"(P1K+z;?BU ,ҍSLhj.Pc64HQNb$p !63qɽNWHvBBNT=H#R~0nvaP{)jx.-)!$͕*ĂrwOP"xȈ-eu`<*8IxfJB`f39&M8C|Qjyg,dLĞ 5QQ)xiPG`ʲL'ZMIs .nx-kC<)BU/ % ?۷N SbT &S̆;8^îK%'n6';?:9zpa<[)Fl콛_JGsF?89{ʀ`B[C$Lg],{GFErs`l<w޻̅0c4/}מ}we׺f2gG[[r~=veth4ωhζfC7$R9fw9YbW&8#*C$}4djYEMB$gs>` 12k,LSJFv:l,O>kf?X7nU bK1ƘBffs{C?S~?]c3iR :ieF:"r[kYic9>8'"2"3##3U̲UvnݘFne~j$EZ⅋@j5 nm5m@cclv\*Wq\m]c!Ғsx64KUɒjg/Ňds0DAU HwД#OszZ [wr>ϗR‰1Mu)w5ƉϤ@(| A{39x1s"Md^ZaBR(< C94B!_ 0Ƒ,K{c.uvʸ;\?^)R) rv&ALU\'@ .6 $`VEa^'"5MFut7C7;NTETq+Ә(D&ba""M$y!Gm6 *ZIyQr"(;A}r0vwEQG)T;d笝v hs " @Db1?-89 ܌MOD\nEfP&T$Qawh.zv [ q\0)H6 5'G .dJMH*!e4v+#ap@_9)\]GO2dkݽs1[zɞMcja'!4ݛWWg卛/={2t}Q~wnUxc_<]=zNOTYǺזw>zhQQGX.V/=~q=VONotm2kϖb߻xY W˪ˋ~7Z0qU}{85/鰷3۝S/c}~$̼Yofle`Uw`;DL0+WQVn#dE5o} 6BH7ce$0MۤᡭC?w_c^/ as؜JDeIQ4{TM '>K/Ci:Փ NRHc#LJ 1ox*n0-`1J!w7&5V4QJeΊWf ds36R1>))P X/ѭ<bjlAY }Gb|ˆiOНuJ6Xzl =ܶa a^8njm˘'~<ژ@בV`RmS*WMB ʻC鶕_eod:=\" 0(8(Ek9Y2 ˼X'۶ݙaq4B6Vv';;ӫ.[׎vnhGGwyuXNW4 =;] "S_ڤ=;_^.wf{x~Q"g5y7^ԭvZ{rf>j8Y-}rLYo℔2Xz?zq9j3I7=l.|irѵm;9L,"hqWsw6 w]W4M  !$0qR%ߙIBfabUHt3nw UN^"Phks@CFɏ|.t~?3_x?|vi/$AFF/?gcxNO36\o/4+pNEշz+_c!;'vM9i4Mxz~ uFωL0$hv9_smy}tsH B1Wo cp\yT+Kqwg/@E7?s3Uog/|_/~G?Н]a7 ׾pm7]_8"d#_ ioA !n"R`9ƘȆnΛGj!cF7P-\`>iM]pSh0J #.ˆ xæ8[傴Ўƣv|n&-ür),8B*V` 4ul}`jajBAX"Pvfgl!m@,큳[HƸTf /6>% IDAT.N }-6< cQ ;NaR\t̾{SQl%3œD.nLSjS.,bֵXUE\^1{ &O!Xc"3XNE1 0ǘu )nH BfmS Y. f O$v8om=k6C ,ABdS71EBˠmn=s`"bQ <\(~Ę=k a'Ƒوd@@gl v"k[N8=? }|!uo>EW͝僓ýnHhƊ\վ\t{+ Ɠ'}g=x7άꨮ 8+xmxִ,FQlfd985IT鬭'q|+acNC69h)j[vD1Q$ JSȝəahրb#}/6  G{<[!dBtϾ|z#6=n?{ɽUޏ|7| ?̓/[{カWdWo==|Γ\t:k|&GV70=θnEm:}j1";:^ݦuU :2^V(qHCm:D4R|*d:Y<]! Gw}3P&.Qڟ3ڈfO2RGWe3b*t㐳hVnd D$ q8b+#F4TB8lj(g5W|*Se"1k[xLOҾFX`TYU‘{U "cTU]UUR0~~bBU lyD.aaDn\Pԣ3me.F!L]3'q!0[ (1H`Li+؃6ns`!xYD`jBnACQ >UC`$*NH jqt3`ȠaAk^̑`x bdJz%i*Q`x""àCA`LU Bb(9)W" !ZlBEi>dc0%wf+<9{y6MF>[>~r8Gi^//Uns|p;ɵgwާ?<}:{s?M|hx3t$!l6,_寽ï~ŤHHM|~qfnmqgO9Zw>>$}F8m /M'up[nlMl8lXT'yJm\>;~tp8KbG =d-Kbbt"JF F.ʽ6E6Y1Q p1Rh>-ZkRȁ ˈ* gexd9t/c?׏y|㟽ޭ^o&s޺w?n7U}p)@P,pub_?sov_77 xa[}v7֗oOzbZ 6 QHD"VoV; mɇUKfa^Lps s0Bsa-_no)zFI1V$<;e80p{53qUVKld:d[w[[ <ӐK k)#ԍ7_#\,Vӊi,jю_A[ I`^\H}oEc1;-[dגf ΓKG~>;*ę.d盅OΛtu,D@0HPKh+ 5ftLJ x|&P;1?X|`?*PI`O'){rn) *lO.Oqt4҄[z3dxhMO7ò@qz`v~z' %2QF &ͣAcrٚzqz|}v?;1- }ɫa7\7}QSO&fLff,c?r0Kd w#C+%ݳ78A.J԰v+'8ѬjqS3ϚP+@_(` E/0읏/=>+/k6g4I?b)@󵽺y@$oEw* c!Hu,&7go|'?wIAfH ^vǿ'z7S?8mm]O~0gM- sCD*;Q P.#%<^QTnFUۯ~3Tɫ SY?R"ۉIכwAb^vn[lRǏpueI]NyVGNZcmc>Ԩ R7``'Ϯ֗i2պX+O:jQU2E~$@W>n8)ATr(@KQYB{PQ]l楙]]|)vůu2Yvw+uZ_:77 }(nmB!rhQFgb),;[ֶR$qx10r4>fp#H n^ă[KN5;!"f M*fe7O2cd8'b!i(jDJ\ b\:y:cCAy0%PSq .0 %Xèl$B٭4ug ,FGŘrq5! 蕗B9"' }V5nl–h.ݷF 6U{u<6Y@>\EJ^=mjd`'OW!6JQVcrFw~~?=zS/Rq 7j8xѲ>5)ByY0޿צG ?e_.q Mkd~^ VONV%>Y"D ڂ?ܑCM 4ï,EVUa:)Ϧu.' VĠ+crYH `f+//ulḯ_ 5MuuL1"m_t;0^}KvwO{@`vFjᨒlWk{+͍'>|v|PJaʶ_>D/˟}ۭO~rvo}pSk{xtοst|hWn=9/qNbݏGo~{\.|vyΎ0dfSա ^Wդ!RTv`4?{$v;o?~r Udr+w^;~sϞCZ\^M;LƬM0 P"" @D]oo6P0B 0(TjɪL̃՜udH( Wjv3͛B˿G_N:lA3JVsB HBEmN1M"ʀZ|2`FK@P {?\T:kljU vys.:}|Lv8 jBF]ؙ~"FR5 q9(A bDD>ĤfVjq7E܄ \G0f7Wjq ۉ RTw Ȱ(m~'h1#t1b8LmTqp^`j"<6+a,er%2Nt@MKɃ㠫6y]rXqXy -WscEѐtM Vݞwz$k]iR#|:I>/vZ|=OfE];ÌݴhWP щ5D$/Eʰ6* `&̚ow˓Y_=yu5o^n~wo{QsM7 ᒛu=j/&I=1.\prj_>L]n/+0)x8^7]p^f'Փ!禝`1*[Vb :1#Ʌj>f"&5РB^T"@zraQXI%L4 cytt4N+_nȼ`PASrج:ߞ. ze0IN56!m s Dȳ>wr1he4m$QM*dO}h4 PƮrWfj&h%72UWLղz6 qsAe*I:md/Y%h"U!bbTjjCy!HPLТoIחzܙ)ms饖`U7$֑zzq֡Ls5xz_Wu@`CՀYXj|nd[MթOG}cSY i..-Aj$X} ~0m>mz?L[ḦbVdH$q?fȍ+05H( ö@r*fu0e &Oȹ,,`PEX7 nz_ogooŗҎ ĉո,5԰TyTOid[RW\ "0Gqduk HYbs۩h4#Z1Yؤj\+H.6G)\`0J\bݍaUX0s1s&'P}tN Z`zf9{#&R\!arwvwh`ꊂ33I%{F,Į0R({ r8Y1GNj[6UEG!ES'DT5 '{8.}:u ԗ f.kHQ/ HRmaMyɘ[nhdm<SfR%)n=(anEg )qɝn癩PM|x|i^;~Uڟ.r]omnO޼s|^-\ms\Cu/aeeLڝW,0x5vyw>q{uA\|n~ufӪpskXW&icL\ӛȖ%g~s1)ߐSe"n6] )@l5z#h zVF"! "nd7U]UbV7>{9f? @ <qq|~z#  LAŲ]aI6&|)^IܭntF݃zniDMkAcCvԕn~fO1BH:9~7Q` vboXI(t(az@bcT݁$A($ %'$t#N1P8*VMO3؏HuTԂ:Cba`AH6 YĆr䥖Z՜7Q Ya8C>+TX1ޖZ\xZUu#&-8_o$QJx9_pH& @hqR4au` 9<FbEFY1>?Q2G? 9bO M{gXs8k^POUOٽJG(ԙ(!Rߐj%u$F$mҴīQ1PJ ҙl&W3RHEƙTEm\UY8w9c0&Gp4R(D-Qm4>?HH3KRX+Jqs =cV-R Q)ZaMeUݛᾷ@vҺ:qnᐭi(0]m#W>*Gs=: qб G4 7ȅkU[44g;}cŐ?|z]ˣE;_J'VJd/ROΣη.>;27v<[$g7[z˗?o6g? M|z?JW땤'>Jnj B%q Z}?t{NM3UF˿~9ms'3ʦgyX|'/.=\cڳ'`J;O]׵]4];oڶu$!JI1ra<sU .Dz&(*F+j7>~]yٳN?]~[>(7NCd/\UD䢎.X 17s8@UƱu<'=#Ս 4\ {l8H s%].Sr窾-{Mgbmt`Rhn]kg$yŊ&e!/{=;( =y4z9`aTp=[&2uf&0fG+!!&t`Ih{V_&A\+bkQ|?YuS6?hՇeCDR.f\sɳ˜c+BY.,溿–MQ'/ӓSuНOΞfd[bC}~3ٽ%?0: a6ͶfxkȆ b@dry/1@LH~Mji7fS 3teLh)c(Pq4 gj7; .'Oٷi/eݡ.z4B17'7Y%@ Bm; H`p@Jsg~U0#FibxKLd mdX"`(Ek5k^]<0V;ثs58(Fx#IJS ̄b:;݆DW3B A 0 0D!bGY5 "(DZnuD !Q,l)xM:kF@?g_\|>]ی gPxv=<Տ??|\W.fJT8\m/V#=ټ 6z}wmy]bu2g(}N\U]:^$Mhܕ܄yZ1Y#$ XɉbG4}ʺ=% "c.p1?z͑4IFi珯x{y9ߗ1cFNExpB"ciI#0<3U' ( dV:Ш.DSBDMejf l #s=;zsRܣMBARء!R샳lޞ|mo͢nO̟,M_n ~ٻkk_~6_ޓrwX}qۇCj,VRbb5ӄZDID1äER1\a@Vh= ƒw.NF %S\E;CU@CEb* H°˵G)< .RV ;ef_0ߍsjKn|>o~k>>lTI/.n8dώ'F`ز9ܩmP)c̞U)1ez3.nuQ<]Ք09XONp=CָI$lDHYRCetoV'%۞,.Օ>=Yna!Z:UPEBy ձC$bN!D |aUcņWV=7ze EcݵVWRq2UHQEPՉ40z5i ZK)N!bMC! MPn͋;b ,R1"mP̓Yzx?ɫoԶ<ϟܟe ^^_ywfo6]ɜi`ެ8NË$OKkZrb$(vSg/..P_ۿuzuM/p1K $`]뻲>xbѴ]gq 6Nƛh]c^4R31 #U$M] @fh0? ۜf:Jr~ wC5Z ˵Ffx &5jC V{C jAʲWJh[t4z{hA Bz/f+̐bIm׆&iJt۝ЖYP£S5 :BGw>H3JvZ׆aޤB㘓J#.א8%ƹ趸j|S1 DݠA7M೶6͗tz_n_Y]+vUBiZZF`ȵLE4,y.:ci-M} |Θ=R25ɏ ZfK$!0kj\0L &&&[-VF "X @NRd $B{t-ݍNMcxtKhRӄr'ǧWc{.TMe1 u7kZ7eGYW2}ŏ?{]=n".gMR޽~ӆmQ,&>VTU#3r55*&?{Քb,t*?b}Hw/t'϶}:9LW|FTZ~~ϟz5Gc|yчƺ]3ԬUm'?{^TRBD6[gFKBrv3Y<BIǙKt(X[GeFl$k;@s8VSfrl[ ΦZD%+Ym Oڱ.&=eȬcH`qThd*\"KfPR`As\ "J&Yo8 b:\K. ac2Nqfɯ`{"\rz ?jmOfm9ߜ.iov. _O_=> S;Ǧyןw-5lm]/donv2jAU9FSUc'IӰWIŜə c.io.~˫7}30q$0|;Oݝjs;W{ǗLRDZ*ȉe<8#t>~ͥc@o{njyf2o^7|_60n7p퇛mn<ۓM^\#Jco#Cu|Ҡt2 :NA& M Ywh "4yUDA h5IϜV_h1'Kta @. bww# '[o2wsAd,ud70dJ{1 %0 t--"hjutISbYK'KYl&Q|Ң61SH2<Wmv"FIJ<գm`6{|?~7r5>bf\ &A$}H [5o!7N6m!ݎKa'[U{{P{tPڢnD?t9C=:ש_jZ 7YP|w7lp= Vj`!Ă.б:@!qFG< Z:oh>qv=i }ԋ+/ }ǶۓDPȸQa~J5"#Ei)]Flp׀E1l`esb6%yּ[O4;$8 yyާIZ.Cj{PLl%{-G*)AGfAGUv> R@IWptѻ$D̡Zm֫ty1 !Ʊ" &'vN`ۃC"f8o.+ B1kC GQyΘF##fU٦SZ[b l&L”D__wx~a͇aCM:ykz'7?573uMB)w0>cѩa3x}4_tMzW7 LBUfMg>}vMo i:&C컧.7Gvxc8HG?yxk?o=j嗷TGIl=mMky.}sfMmWZz ҳgHu!_/_^O_Wݣn(4A~>e Q$"pw95GوU?UT3Q ٦B`Iqa23I!`Z5" 0a=\es p. uGdvCՙ(S=N@SBR~o]bi1(Mv&epCj&IbVj 䍄$<2Vw+E0d% 5B"-DN؈a,^\q$.][u)*(Di=csiEmӥ ]FpG".n˹h2 {J8S1J$$Q69Gj"&;b@ԞդԱ7SFDXqR1n,@:y8ի`~H1q`FT%*$I͏B4Ub:$uR4T+B@PyF O|F%la!REa~s]r!e@$p{ao!Ar Q{Bsk-? njɑ:_'jiXo3Ss}ӟ7ޣ^7o^.ʩ=~<؞''@/?O._Ge?zwK WW4w|.w[K;aOmg/WwcE7/, !AoI;q"j KÁhdEfeD$pCI";qIRVwS8@"gJ14!x҉S5F{9h6V"}E׸MB|$i$>KuMeL*51f \kHn#]BT ɩoE8qM|T>4գG*Dg'DSqt)4l?pS)Enx2qoɝbd%kjjRb;M !0y̛xm],wWxj_4d-&^;LIki&&p@wN\T,B=E= a3گT&L6yIfi'43vUxeo^x;}>yvAh*v42e6UQB afGyK eO eK]@#h=o  4DP8CP!q*I\2YBj:*ǪAxam-JΞ/o{%tU#iAU ^~l5V{mXK԰xuNbȷ1: ѢN쎬@m4R&s%%jC$fqt|'fsQ%S(ؑ@}ۀkɵf tThVs䴧yϻȉ(d)OV!mv7hUHchWyj`:`F`,܈ ("RUCOY%Y \lzP""Z.B$%ۛm(a7]ϻizoq*}"CZn7Mf6,OgW×7oo^$=^^,b$sA!2`$ yj|xMzVSd wE٫lݿ^^]o_^M?~YNq᳋_,}Z'ޯ?=;Bv?Og'ўA=Ԅc1,c8f4e>Vl:ǏWM5<%90Lh#32@1(V!]աdAll애 Dtcd\$gDM&ZH1re!rK)y'7l&"[ްi0GsԘ򢥋‚1-GjqTp.r3OFҺYlSJPj & ڎfoSw{XA@ۑjuD9%!)JŹ2Bw%2o^esk'٫zH|=5HxZ0EuPs!: ^~q"9"(뽫m2*h2׫,0:$R5U͗(hff}IGmA읪ymٱ*b%^&Ͼ݀^b#}!I-,BSM݉R욳EjN5of-bb!xpuO[B g$҄\c$Tŋy1TjPFӤ90՝"m"DsH MK`P]` fsoR^!!h{4-RQ'd*U2}̀X<դvlNz>6gZk+$`T4jhmDj%m~Ճ!vod Hpft8kD7\<(I+?zV`(0nBJqyyиJ~3=EmzDZ6G_`'G ǽ|"- >b-dN6h tpC83RU H)¾*/=k(WxlsEۀ6J?].j?lQԼ"h8 G]OA&]C QA j1z̺N.;6w% 2ى`0al2 5ɏ|αMt=;sNmn]]J"<=6`? a|ȧ $03vVL<6[dDkH]^w>k-ISTh0Y qGet ƈ51WB,2N8PN0ә&Hz) Ȍ,8-F@b#\עѐ 2 @0h!I1B0 4$9i)e(Md "$VN€Dj$LxFt%  Ѓ!IL{^OmonDօqiu1q̬Jqɠn )M]oyx]/> fV&$QA~s'y} I ޛ'uT4_61-OEϾK*dE@) wOgN1)Z  iO0Ԇ Αb"eFVmC/MqCbb |Ƀ@G5'L C8T+H$AzMMUEbGE amo)R Os:-}P**Z:mnB!YAДPˆ⭈NlStO !bA#*ҊLz }'}e%ࠇhF`  %]MM딐E1#Qf%EHNA#HT 5KiLXfJ295+ۉNFC.V=|-$`@o Y1Y*$ $B="+O]lKR@$yt[vU%TnfHŒXԮ0MRB$Ԗ:92t=6A P&Ddku}ѢDt-Jl8Kv+2.ӥxv^8z.Ya };)0UJ[Q4y{ bk" )bJ *9%S:yϱh[M1*|eIj=*4lߊxn[-槧<yH[ ݔdgɔE(CK'^@sIK2nAL[R - ʂHLM^ vnqw~xrg]ؒ]AQQXG'٬ǟ}lol&a-u҅Ζ}>ŲڽSЄ' &G/}/?q|mx*J @V (2>04VEQ{nV!pP8k$LBL}BfZ pyWָ9WgP6P 2΋T4Ā حY49-VO@igUhlldv@e4O `ѓt}bѐAA"), !:˹I0x!$3\lPV/mP]8 f#T""I(<;{ay&((hPa/gpYG.9ibfu-Q$+m7Z*$t"( t}P̲R4 HbfNk&F2 &ȍE7'i;ABBM92(  |D͚@k!7f1_6>\%1& >*B Ȝ3!Hn4 bbn5K$k٬20 1՘O8-«:h`@@lV!(c]LX8!s$cR}bֆD}&ה?4ZW U'v294U%XuE3Z8jUM`TZ'fJlgѤaFՋr0̬U>5bfla$RԆ4KH"Ł# ~# 41U*/V SCG sG]ۄ]eRhXԢ U/(4BbF6uL")dd EҊY aa 7&ae+.A4eZN>ț'ar֨ZpT.ZM\I5)l U60 c1抔¬ ȋNVE)SH`IL ,*:g&MM)Fayh2m\b4,Q*Dgu aFR:,a::ƍ͐3!UM\t5384 Ym:к/NqF@JѪM4b*70td5 Y;:' [ , &Ȣ6G- ZP X , 3;Y)6¢>E9T@d sXui X@H*@ 8h"%[E}iVHQ VD k]Zu  ͹&D U0B)F69PB jFUa1Sb&TuCAKd.r)U{1gUf8S)du m@aВ ]U/6Zs\k粪\qV60gG(EAtY5F,(Bhd I>cE)dR;.3aYHAD6aa4kdE ME}"Ei9 P$t XXaR^( 38&B3IXRK,AĮR_R !}mX0X5w̚.C[KUDgtyv -;҅1a36;+ZMO>}ړ}mp1_6]=kUN5Wb9ZC.~2I`fj:nehRW&iC,43+ow;]4e&/>K/=gӷ={#_WFAd9^֤MLkNIX'O]ĕlV?yw>nwg c Fc`lUJҲml1ibkqP0dZsD):eȌ@62mKBCJ*D/:"@aq`j݅TZ\rR#"F&9DtNk5\{AāÑad-թ@ G#!X'U:kO@ CG YI-‚! 2^NkzA(Q[\pZU/P4tF)rNk6\BK $$ Z6!QNH഑Z(`hqQL{ty> ' ALP\rR" e(, V@ KY@*:M[#(q(` ᴑiig8ʈ@i`I͕(-2$:: bgpYÍBfRf i0t8i`1&p YÙƁ€QR(Q O\ZrR:!rbB5W0DrE'VCihFW\ठ0<*q72O*xAIED I2NxPL rzt tQ8qų6/ (ϲ(WJ)r`_snβ_|ׂW'ǧɀ`O}ǫ*lO<{MR<.kHgYwm0ZA{s{@i UՋ/fcd|ցg;G#dQudzN1|1}]X4jw}݋[N@z;]m]H{_|$Ŵ9l~xo`@}<]=H"IHd8(rIa<0zzkz)˲߼yV'N{wFV`At17oBJntZ!Mڤ5NtX'ar M(p\iͅѴ+;&Ҳy_ٰu[9hq]q-%nXy!k휎ta-;ePˀ[%4riFmt X NtiFsC5,J9mTAJG6_r@u b䁥YGހW3꞉08mJ aT5_Q*P Av_ܪ'(|\Ka4>q_yKB =HB`4Z KYaN"|uwd{Rn9m  ov5uY9Vu]dŢ^AaM̭{efLL2BB4L)EAJJ E2%FD" X# TY{rrB",njEV3c @L,Hkk۟ϧ!D0FaRa4V$ Q1R1FZ֭QBsEBBfF!Zs ieT5l,S]#R!$AN"J$9wPsF#Vj J( 3Q Ę"k1F6F#&9gzw!eUA8xk0@d1 }>Ƅ!'hh_|ڤ'_8CBo{LyުBz=syӻ6+۫*dY۾?nܝ1v} /=3g~EI \1*ˈdUA|lf14FUOO 2~{d]dPns A}/!u_N{0bH'45βdP0!!"'YhAn!HdTvⰼu*w>y[sϋ2321EUGzc]> )Ī^%Z "y!QJ@ܤD֘MHV@Μ 1AB+Ƥ D JQJdMt[,kb9unyM15M:VD$|ןH_te\Bi:> T^Z!PQfIԝ{bEneϑb9)džǓtX`GDZ)Y77/|YkYf_sڠcn\+ G}/]/k?)]ds=??g?T$r r$<Ϳ k K'oۼ$ij P{;r>.b*tٺ/Gg?7ӫ?]-q^Onl_vq|ykg;_?/uW~IJ#??xڵ??~ɧvw/<{W;(s=mxea.RiԫAQ1شeeQv]31 {ͺv 0)551Ykn4@9..] ԝ7|yKmͪZ-/~Kw &4M5͕Ëg{_W7Ęz"Xks'_|#kLww~w_/S266sz=Urq ke-H)-t 4HD1"X Ac0&I$E4(!6F,AkR{kk&RJ`#"Y8%@k GBRbJpGTV< ڤ{lM{h&׵ OZk9"Ĭ 9>8Ni<*|$}LᬏGG0FQ8V$F͓gm}3Oêw;\Ȭڿq;g-7g.Ot].yPM2! m\(G_|~qhr4c@nߟOBqEa5UWrO}@BKՠЗ7P!o+uhU}wmsptEF 3{'Me¥\&4"4] "=TTdV55"$L[{;&ۿ秗vхSރhҚvU}eeaY+k`բkC1rtvݹss+1t]P@,1rbY]EdiQefuH!8cn">""Z<:u?m5b* +.,GEQGX5j#%>>2`:ߠ@a !y`|PYS5 8Nf?OW_}Z1Z}XV?{!acc~+}?{jX5!Ʒ΍W6@dpQ' ֭[o/'?W_[N tt`X㍭5p_ 7ɭ>E {߻s>4_6w/}L.J^&?oZ{O͟d25Kʖ7;:ln_퟼yhN޻uK3cru z/`궱$ҙ+F[ӳi`4"nmYM!<σio.4EѝY-~f.{gZu-@4L !<D|(ASzD9ֱgcR9)(`{fBp)C+>mDXch6,@Eضk?dmG1 (HJ`_5RRTHƍq1#f 1E9^,j5|o6U\wxko./ 'Y&sy7w6)+jڔts2gwoݽs^/jZsٍѢII\hV.ƾڶj{HVu]7ݣaisw:-^0L Z{Z-:X( ')"!@(v'Krzlx3XՠɘM:0Vs 2c,!&ќ"Z4"Z"zhFyX"0+:%DȲSHhai҃heDJYk LDD( F&?Y)X/J5G<Ϝ3'T]F!xWtG@y߰~:/3kٴeXz?/NVJ+ŜOZ 3|?/1,rwVV׮=ٵUUH)GG3ϾƏp޻nnl|HbXg][3|1 j1g/Ju&ŕOnoލwJ/_'٬NR)_Z>ߺwO7fccBȲ Q>ʯ7vj1!½ G.lgwwU5ӓc)LDRy6@Ѱ$nDN!kYf-I6{_7Gŭ[7!֘cY8FmkO>sz|SySUmSYk~W~x/}̓{Z2O)<^{ɧF \,^ǜwFK[;mۦژgYJI3>,WDZuBׁ֘C4?B #͙|gYJ`ͭ}^_A/a[~/@HP-x.@/gJ@?A &{zUO"}Sdސb *[eny[kߺsl4lmֳ4ܹy2Uv>{Q-]x{w}͵k{?=9lnOiAV5-$U̪g cͰ<} '#e P<0>hmϖuf?= KpݳY0"$;kFQT}LA\(d:]wv&ǧ+"CH) FHb9;o"cL,58ޗ@$Zs~⣋?\|rvD ǣ[yݴ޿oλ!{ ?^OD'0Y7MÜ|!0R: =PْG4ˍ1xn>‡0>c~hHbWmooǿ/LOeGmX3@)Ʋ,oߺ=9=;~o\hcz͍K.7u}||Wdֺlc¼Z-͝_ڿ]O泓W|WUdyvk{{_◯]{r:;7nܽu/2sC gYB]7_?C|o6{&|1G?\V_{˲CNi=F)E˗.rbzD1G]666֫ehh<([z,^Щr0>9:G*Y^CthGĮmn޼sϷm֛?탃ZfS"8&Ȝg;~_(upxMgד61y#d1=X5땇f6|@2OJ)G{^<|rҶKU!,B*mPAT,l=kCG.tsBK]h+s['N.,Zd'rW vc<||`~߿q:jWU ?uIHidOwfU#`2f0%N1Bf*P!s2?8yutAQ)5iVu)q('enSsygkk\6u `Hf1I)I6G'ĩn[- .2g4qO ")c:Z ZsUBb@(p |$þ>.µvB)%ˆhFFkksRH!)UdE1]5GR3ZٺDCxO3Zw ăY7cJ_'>o_79 _ odcW;o{GUlnmua=,K3W޴nfz 0 H\jERKʄ]b Q )b% i%)rwK, Cxƴv]e߫iVռiP1]/'+|<;'# !eXS?jnX]Ipohl}CTƮs-Mlw|яU'.Q\Ô-tx~eBh.Wt_Nff&RkBmX#i6;_|_swq||rsc qV5~wպNBȲB( )1Z[m;c_aA<QV; "on "B,A"  .ZT2h4ȀF% /c :d+YRp Џ}}7t-ΝZiŜ Z"L\TIȢa}ZGЬ@ NZO?ת?id3ƹX8pxʛ/{Υ7_gN>S)W;Xyfv&''balEq_yf[ksV-oB 9Ϲx,Jx,޾ګߝ_X_JɅY\ӤZkO?#G_W߾V2ZmL~a(H7~㷪75SO]zXtG ^+g{7~:?7o7@.1AJʃ$е-8` %dc}#9xFΈJH5(-ξGfc O8jlB-O*JcD'\^I0R&bOFJt;mױ56p?GapO^[BSSO>Ο(2|1Bql-z#FE0#Dl6FYlJ#JIv7c eWUb|ki (`[6P رBfJ-%eCiJN1B`v &QFFQ 5v d\֙y{03]=@2[ϔ61dg6Y XH R5"rAd5lCGyz$hD* y ]בi4ȗNr;0.*mYaAHp,V,6O0#Le1f`?xaLSWW 8" D}6F@ &- *B.7[z+XH h\1;扮yd@<3YkapD(`?2Rj1V1(NR)%㸮-ABֺaJ0LጥCvXv CayքQewDULuR* hmeقH81,IӌxjvZ )(&BJ+m d Jz H)31QwpJf Sv.d2,~:pپY02;$;ׯgyNlln6X8xӟѣۭfq0Nf0^FN/JKnlm§~?3kwB(J0A? `&ɭ]QRA\[(*RzY'j+7_{B*}y?._<= >}Kv`@IF1VB)Q:zKյ(J,nlnnp8tG?r.Bccò-m˲lǥr8I! cPB7 c0H)-RK) BT1͉WWWB!d0pZ딫 oYM-;3VyB^[]=teq]ǎ"LBqbJgf*cc׮]{Qq.(pTj$5Dq hclBJ%]8#GaMNm`t{0$INZmh!k sJyWB)AP61b!L"S&Pʔ`@&IjmRFk_4!DH9*p&|wжOB*` fл$Z<a/,B eaF0#2B}ꆑ8 x$îTo^lV y PW^譾XG˅^l~ڸhQѶ+B;4 t=}O8rBk>4 2Juhzp=T(Iĉ E:nu*g'q10\GA_Y-oRxyTpfGfkkOՊ(wNyJaFX ;:WJyR/x=2)Ҕb;n,-bFZ72 B3 A caBg2!8.qRu{*a1ƴ12B(!@BB296H"GME:0i1aq(N?!D#ow؛`/{ұ'3eFlYJnlܼw޹s91#X(.Ka0\K(u49s>|M?1mIn''>/~/aQ81PJ00,TJe.Bh\! з\P/$p.ATʾ,)wFship8 (%bj:sW/sK1 纹b1^X84?h4RJv Yށ9q+)oY|_xVKJշ>{ܰ߯ML$I0cJY"2PCJde,KkCmPJ)ۃfBC.gnk]A2IB32hgZ"ٿcee>6"0xǁm#q{mgxo4Ah;jr22eQD0 D&mL1u]aЛ1"-+`Wzim 6S ^s?̡s3Noէb>fח4bDF#3S/yFFIHU)=0Š;|^*칅 tZv 2Odgh^2ts$ $B[D\%Fyd֔0 o\^_'[vo77]u7b\(e1jq0,,gr&f8O!q)w]J+cTsM^}bfr0L0FW(:SAx)m`Օ{3sSkcժco[7C_w{;oZ ,ṉm(~G?l4\\~1norzsC0wPJ'@`ۖljֆPBP9I"n|~JRJwl%R*۲ RD5JyB\DXj1v:|؈H ,'( n>}?1v{l/n߾:&RbٶRhHj[tΕs9>O_V5dTI!LvVڏlrSi#i&xTKdٯd<#n+v% ?P)J) 8"!kaYSWJ-3 f>-0 eEj6fDL1 d*(!BQGEۮVn&؉Nc-6YŹBq&N`0}Lo|`R+Wn^w E8H!D(3 ғWi蓼c\ $ղg@^lvoŢz{c?]P cA1,:S?E!sraJՖ//6d0;%&H$I\ํfËw4#JkF0`(!B>Neq,k ^@C8*]itggݙ^whf&n.k}[vsp]۲bhLkCv!v&3i:[/׵ۭ1FCa2[0F-Dh0֨|Oɞpv2g## NB[^~)Ǧk+wsR*5/J[[lQʥ5}ĩ|apT$ qbLsg`=̯--0Aq̃0?pP+ Pl㢍O>Lcf1n Vkuai!)SqK A4#@yB'=&쬮4JÿOiЊ!4d mCX&+աg~, qn}Po y;L "< xB\뛾t*r!؁:3wxYY AHIRJ^7 ͵ _/hrnVR 'N8]-wR(D !¨`@#P"В` $fr Hܯ +Gy#6RRR )a6iT*q$IMqFyR`HR-AУɐI)Z_b_KBСc㵱$776RmX;k(VJvP, !PXt;\$~ȅBx qx]K(I(`jrBvllmnNkcʽF3|Q[3ӳ-J{^R~? 2K[>DZB>O1xh$7\T\.xV0:ώr ! Z*|?|90MyF Bh+FCJc乮eYPq?OL/P[}cmp` Ơ`3(%8ˆv^Buq.gL!޺[]2E?L0#w4"Pba܉;k]?m)6ڤhw6fQ bLm|~܋9upʟ^]\oui0"2D:=akX8>{X*0q{]ɰ7 |j y@Pxc+ X\QBa $ V6) x3-n#w.,;gʥ$`053Z 0s S$ 2;ffvFR*JP?4ХGYB⁅{E፿{/p,9²mױAh?fP͘Ji0#lۮB%FGQP4!dħ>I6)o-+%?^*MO~꽕 7nHQ2Y/pS|.$` ((v޶[NSq0HZܬAs޹}m0Kz>/B)DZ]׉;?oܥK#0n<6F=ڎA`-0=?T8r&mv3s/<ܵkג$=|O?;׾~9?V 0c #,+/RǶVJ!DsQJk10ҙY]sq(ձj:nMXB#yޖZ 0VfB0ʶ9Eq{kPp$) kkw.yct^.77=yⓧNl[33|۷n8v3gFKΥpwЉӠVת|NH9 ''&&&1ƌe%mt`um˶\<۶-v9;pi9[ ˲8?H֜oV Xkz%IX8sluá 8x_ҋOWOM<4W~Z$p#(1 `0[r Ƹ停=1`hƀ6 Xkq43;nw\׉"ҥ7Riq8RƔ685$i5rs)TZJPvFԹ = K/~7n勇x[z=덍cgFQ*t\Dz(VeD !X(`r9/76c#ƶ-Ƕ<T<#Xq9Jj4F8ëu33 bY.raPf1qJiheuZmےo57{.cܜ{oynk?O8yK_qQ W/_iڔ(jY( 9a}3jMݟgڼ(FgI>~?ʣ nTؓ]%hGܯ?rh˛$ >{h2t)h@ejr9JRAǏ6rX\26PD'N:03БFɵVwt\#"2K2MTZǎN?~j!HߺZ;a8f6XJR X!"I#-=rRFI£wfs&Nf+ГNkw7o\[ $r.) V:c  jRmX/Srtc'*p%ڬnvB?,U\I)o1!V{8JQrDB2(Atd$P!-4]VLy FwwZ5rǑlEaacm4OSvc;EȮ=@J MOݟ޼q!lRmr1`Ee1hr^Gjgm rlrSF^o0> ҎmGQ$i\GQl#"ԶG },8?0J=taQKaŒRʥ,h(33WVVʥ0Ƅv'aQ%tV׿qٹ{;߮*kk3<>sO^H Bb!\JmG(Ji9$i?HeYs3Œ4J,KT29 ˶bm~`. yFQ ,P )VRٱs㍍)EDcvROk++͛pT.+e>҇:DZ-JrY>B!t!_;n6֞{C`,nNvQ-v_@9KQ~g #{ C1k(qzaX:]\a ջBP֖ bn5\m~7ɳG[M60rvݺ,罅0Z\vc07q'TD hdQwcYF~sKhǞu^|򕥭^!R!^-uM϶mD\ZtyNP# %xK'L$^c˱( hPb pFrL,Ųve9Uskh޹g[QG[5א얇v"r 5@_{dR E]m7̶Tp)xʣ0"@,!LCܿe |jz3'޽EYR>>`8y^^3Z[YRJEU%L]N1GҀ(`,.H۫'j!r9O)%0 eD) ,LŒewWr\,iss>inݝNa _{;O\|S^GjXZ[]#PZa’8<~\ZO"L ~33SSzW_n WWr3kVc%LprRa| )(1fK].le5-c4JJVr; 1fZb}Oٰl bYJhbr21Fn9/WL8תZW]bl Zmd:?v'^TVzW^??v7c[Rlvs]7IBiP !8wrJΥ  wC$weFJvɣpFG7ZbOFz<#BgZb.}<[~2M5Xn@wBaWzf䷫G_ ˨ۯi f~+ˑЎFqA `Zc%Z+nh1h&Wp[mis7^sB#!ۡ<|Dh1W7@3I}6: (Ra\ɠnjzxR!XDÔL6(I #K EPżվnl}'Q`ZB(A-TE%Q<脁ԑ0Q7C #@80Ydq.7@8 q,L(Mu:+2_(`))4@ B f J1HH5g+VvE(wٲpԋ}y;w6%{Bq$MD;/8zQonQ<`GffXwZP, rlB\b%$*1 +C?IQ0ތo=J{`Y##V'^7/o]gVՉə4!ԶQ|\*E tfv]/gS~Љ;nQBxekclb6PbJT*eB6rKo[ ^Gu7|… 'NĄѿ>sni{/sϽ S.:xp'>oo6}\wbIJ(.ޒ\((%R9XJ$IS 7`$I(`@ڶh4.-?V-i#1ƀ@H8qccE0 F}a0h l;YOr~,w_{ⓖ;W߼rXU)j2hTbVHT0\vS,/>ԏ~׵0URR],(m](xIB(r1BDZp˲ 6^[[\;:vOoWR ?o~_rNxc:1jJյOksr*ZX8vʵ׾W瞼qoO>_^8sR')a񓧮]!( !1ƚ0A Q2%pA W0`HiDAUvgZml:;crTt:jcAU4Cן|y? \᰷p` …x?Ï}4;nu^{r|̹_ u !8˅Q@¨2V][] p~VC\N yD-(-졑ͼ&ٯF6FOx`{~խхn7?urh!D.liNZR-BkhnRc0 ǚ $2Aɟ,x*VZ]DJF3ǎꥡB #ZSp衣G!6(!lۆZ@ڒm05WQZQ8 'nŃǰ-MԚʖsF?jv}ײ>ї61?ÄNJ@LS LQ- ~{{w?qgW(fR/slFR228\"oʨPhˈAդWG>҇-/ }L˗G>acÇDkڽ/|^O>Go߾259u_,T΀`wvśaRJH+#!B8"J{C11b4Ŕ͗*;7kqO3Z]]śt$B\v:CN,x3ӳ׮^ [[-rjcBx{s8z|>Q"P(q&eaIjY֠?PZ2cA@ _WJ!f`1Ƅ`11dWgn0V0&|Ԗy@rw.PR2+wĉ_Oo!0TI*McTJi%LPK eq.4Mr~oRj۲r;'f)AVr!u]s1rd;ŕgeeU$dl}h4׎ a]/a lÖ`–`Cj4RkdwYwU;!9YŞɀA"^dw{?cߙ6v,&jVeZ\>~Wmq{Z;oT.g.JJp0/2Jp 0Mi5_:+<,p?5mzXBuCk8j:4Gg&\f ygt1aAiٳz֝{[D#M"cDx!4Px;YZ[;B~̋.{Q7{q 0R7w DTA@CmLdb^z|Q?n3ƀ{20k"ł[ȵ")%fNJD&U"dDe0֫;A$h @(0  0E@qs|V~~Tٍ㥚[f MLrXt;G˕ B[U*GXbf\Qv3f r;ޱ,KC R{+7}|.GFTT2ǯCȣ}}J&V?`/G^a퍝/|≯ݥTAJ @dDJ10&#Dv5˳MNͭ^;=e\4!SiH2!3!%DR+ ږ:DSw68vGV齛kF Vb`Q-Zf6H bf`ڤdS+Jn0uժ3W] (65QV7n7Arj3VO-[i\ӽYq*-F nnҁ9XOlu3h0ľ{9{+!#&?Tɧ%BئcI(KTJf1RgRi N>0}Pc.~Zk'뵧z?KN :J!$^[v{7o\_}? ׮^uv(I@ |mߙ{ϼw Ytkc\gۖ11vl@pr][XX\qlWH}u]*)Jc> 8r47`8[(;[8Yq.͍?~7"> IDAT;.lmm;wnnooVM‡)"!jЏTPF|_!}T GdOVV<3Pv;-md?wf;NQqN0F%0BmR: ^.)2)67ϝ99v+)PVg# |De4Ā"TZgYJlo,Hkɝd@-Uf?|Xl4 " 2&3` sE߾6v, DC9@ dű:Z4HA̅ҞW v?^ڸbA[2)6Q /O:xkܝW;ݠdouZQUL$# *d;}!98X+JftS"H!16Fw*{Cmc|jQ5&LQX${r_X{qbGs@hv+mAč1A2fq, !$t=ARj,9ptm~X~H%EFǞ.gLT+F'|wtչٹF__5J_Y¹, %_/ZVRI*E6u\-DAf$_/YB,ŒPcLiL+UhImB 0Z|Wͥ~فʤ^ZZ~⛄, ߽pic Ρӵ6gRFk0?oBrqu I/}k_Zk|ivi'~?vG*ŅLKiV|ef c4ÌsKB8k>e"!ZK!ׅK% @*Fq'i'L'kk? Kc, !xsqNJEj^>:Qoo>tݶjcsɧ˿Ad;VcfBEJLU*p`3bps$>S w{Ne#Bl2"t1-ƛd y;1Q~p/l,K:{{:}H8\5 wyRcaq+/{έզ*!$R!]ۡ`h4ȤRKJ$IљǏOLNvQdMMwovRgQHe) inmIn ("$R&YVvcJ\i "γ(0.`vxeIp,1XhheZ`"U*8(DL&u˾C0hV7weՂ[Q#ǖ(6Q7TuSMu-BIO>q뷵S]ngI29Q\w 0d̫NL$}!T3"d0NbhڴS@x8F2#"Â{^ 72j2j$,ʅ S+U aMF|W ўת i `6J).sf)FK Y[qcC\mʱSCH?_'/n0jmƀ~ 6@ c a~M۱O?Тq D lA{?y̡CG6VkrjBgB (V4=|Xݿu岋 q7Ibbm@E@+u;wn cO޼~-x:177[?/+WJBٲ,Z(':w_qL}7>շg}ǯMVcg`nf'<'mw8JggQ2f+)(F )6@ZhĈ|۶1eRC)C-^?0[b1RJױ1BA",˪V*qbȳlJ)%YRX,HK_(`&F͍Ϯ;18+ yb,MK^p^@)3F)8BIMXd}sl4} ^? !)gj/B{m"aFc1>f֖eeuVf qǹGkDu#t8Xev{1ߨO7[;AHq:݀Arf#Ѥ\E L3raZt=<]̊ɂZoF,҇g<)4)3ae&ҍ@w"<2DJ T0 ׵K򡹩SKs''$[[;iT lHA e p-|ta!ef#iAFeP!榦(ʏ>\/{ՂS)z "N+ J4?8=k~& hQL&'Z~?TA{8j!I!ɩTRUfہ2<J: c~%R)0rgaq.w 0B6TŒoi Q"eDb$ y?)Di*)E/F@-sP 6LC=CG|ꉢ}+_:TJP*{ݮR\&X.@He\ 53;eɽ۷鳏"D'Ql&Tk!uN^X8pfP\n[J,VZj=r뛞ԧPf;NG+: Snʹ1IB3ӳFc6Y_>|?ދ/X*mnnQn?i(|Wknj]t{o%JW/k|\r \W>ijϔJJ|jmo}\/MԦ-RJ`,JAh\qlv\!MՔVq4x67֌+Ǐߺ~P,kkk8\IҤm{OnlCAD_Wo߽{ʉ'\$aT)1htry^k7wwAlkkc#KD :8IY9|dP&!F KR4 p!ļ%YƵVêJt860! Rp=X,!,S")%:tJpQt6c̢iq[9ؽnߺ\Xxnnvv~ƍ ǶܹeYL,*0r 󫫫`kۖ:m ѽ:/{AcN0ER?v<텹|d*ȇFq x b:|^/{cǧ&KTҏa76>2sNzd3.&(-%+`N*k@h` # ,plRn@ H)U.3>:Vdk ^YxrV*ԧjjmkjg.wzQfFȳd *NT$^ \v*5xZyq!2Hcb.iu@#R'_|fI 2X‡g*eqku{ޝb43; pA#8VRzG~a

Eas}!<i/|'N,}套~oxY9);iX_XX@lQ͘%+(aXd4bs̊ QTX6(J3mQT*xοrsϿRqNOOum%4Fp8FJi(͘ &¢Z)SJI:?~g^8sBqwg/]zK.W^ǎOdY7]J^%ݸw_ǫGm-T7/\^>'$\ϲRKԲV,9FsqܠIP !H)ғ !dv̲JE(e`L4!Ȕ%e>ʥYB!$sSys(!iD˗&mxt/I?ܹǮ]O~ ` Qbia;.*#u,JKalVR ~FБ 2jhϟ\c0߀ce_>qj !c٘q"elgbܺwnnΒͭXRac%0*ft^@j7^yÒgO ;A^^OsPv-XhʈE,9Pr5]gr44Al7jyV9wvfk֋~)ٖ&$3ٕVD,Lj*i)NEq"Aı)!qXO2 hCȁX/=mJ&#-8XDj^-vDDeĤs3wnLBL3jG\cZm8z#_q퇿 !*"؅\R)"@sH8cf"ci&[)S2` n|B %+/}ꭷg]rСeqϲA R)(Mbp5.$τŬ` _Cҁ:/* O?tiicV{cFnP !@tQ ma{[?ڌ6F]Z`4G(zcE7ڢE/(utqfX4 h$ M[vBױgL] jEǥZ*044/QjBѵ, T[ۍͭn?LH$@a*zQB&ksf@)ʧ.˻>@ C02 %HK3ؖOchZD|`,0FQ'-zUQ?&-8Jt>ɋr<\w'=a9~/U6 c/XtlESbQmJEcL)e H(I(Hcv]XG[GWaT?yjaa_wקy/ Z8M0FƘn0JqoJs؃S.,BRI*IAaPۛ;JfA_OSjB@`6=tTzƶ(}1 w\smRƜf!m !A-+ʥBw߽sϽpA?Ȳĩ3;;jݖēO?|cumXw 6%ӧ!!I 4%SJBu^;9Uϸqж]J \u-bJR*!E)!1$Y&S3f#7qm1BRSV DqفF(EQ$2 /̖ʥ͍_$44M8K_J2bB0R;nՉ )~{-B< <@ 4m(c2rz) ~w~}{}^!A,JўUݝ/埴[o۷{Q bƘ!bjtG)G'\'F- @ktzJM2Z*c1RiYjF1˜1 Okq޸qsaqammr(ˬFQ.a\OS}?9h6C+ťťf1wl@F+$ˁ:@=q d VZBRPB@M1D)З`S* bq@}Odv` %T$ΤFMΤ@ UpZ4J<{sLL<""fh$vT*c$B 2fg @c@3]Eh_yÓop˩ S&(2H4BBNkcJ-xaS)5 Vy#†lacc4+1J<<1&:FyP~g h&+Ǯ^>_T;Vf))Em 0y?1Q%As1&NRulƢ(J!#fQ"TD8H[ BHWYb)^@h6˕J$vwvjluO~Ƙ۷no .]4??l&oߤdc%@Hrlsc#EE%Z8d ǂnȖ{"{ɻ3*F3&* u t G- U~1 EP5D AeYR)6(w;(R5\>\( b]I}zq3<3;3l;pDe'vb/ S3NhiYҪzW._o~z}>5lQ,KJiY,$b-LcV7_} DaV;7E!T}zbH2SB{i' ) ~9csz}RH[7$^X<v:wn{ʗG=f!;vDI:1w]znRq{*)łg`]]۶ QKIR)g[E lT,1F)\(lٌ%q,v?ieYZJφ/xQc 6^ѽT*Iƹxc_/p.m6sQ86Dk\3m+^П_Z2EqC˶,f{Ն~? mO 1{Z[|oـA!A(P:"Nz aJ _pp{ Řu"t.~V!Fe9G0@"}4')SS+1*54(SilIk R4- ! RJ1{-˲VVC6 $p~aq8|/lJr^iy{_*GV2!۾wNne9Ʈ퍢g{l5/X}q\[SV %J(вmL0ƢT*!$o''kn빎Lp)nESӄ陻wo>|X*}5ߛ_Xy&1qa,"+\<sn<˴~ȹTZ9rP:Q3gnoܸqKϟ}>:?dМP=[{OP.\8L]d5g~Ͽ$8g !Z)`JI* &SDZ-din) $V+%˲zYJra8R B`&KB!@vx8FA)cB!NR/\r۶cߺykw][[{ƓO=lZ-c$J ԝ۷|OP(BD.Ba:q/y7% wAB~pԷayfh%>"ƣq3,<,}DecP*NEY4k%(AET EV(,(zj͏o53 0Z BX GkQsQm{*.9{dB!R*4%M.%D!2 @9 h@0楢.!4@F kqAqbԲ@ FkBcYe}V{zf`@,ĝ(R"%N#Kr9_'G%YeE6!@,$,uF`auVշ>K(A(:-"B!*Lҋkdǹb p#D(%2pʰu^QiIEB9p: S{w>:i`('g kΝ mqj5xAH qkρ5!L$r0Jil^%ϰss>woe$`]f%gl.]xrToVZeQ%nԋڪN7NIcFI^vUyqcT AuFANy!)(4Fk pPwn$IIdIbYzh0l[icycW\~ƿrE> .ٸi`}an;{hc5&,I16=22J@ F9 1ں4͍RI2J}Νu Mbc;%c¨v|ݥ2JإS_k j< ʲ$VU/ڏp?jZԧ{{'.\0=@1FqZxwg<ML0{>T#>Ԝ@g>:Wgj'6x4:啵T(es'0r XUeaFJ]!}_PF`穕'm _kw C08y4K jFYwxfgXyH.| XuocaZJ Bg!r9T%S\]@SX%3`-*Y#+(tqթEZcVq 10AaH̟9?_ݾrUMrev QԦTAe@d~ YTk(RV?<0X : dk{/dQXIv~ii9|vU~~bscUfCOyYF8R1ny;[RI#RRIekaYU(w66<ƹn;x^`qQ&Y~Uc!AՄ޿wZ扵 HLP($kVZFQ&ISvso#MaQd2ZT߸iE 8omm;wv~wol()߿KZywe䩧[4U2%uza~e09L&ց$BYsy^,g ceY k4= kCTzyQYFG7߻}x8|A(/,$J m60O,M%&8/NkJI9cf0Bx}W¥Ǟᅃ^/}~x ʥe0A9&qT{h[gS{^`ȉQhGxO{5|t<,?ѳ) NVC,JYTyfEO38DYYlU~qqx¾WeZ*m%k@bb0 1 ͅSCAf`^N6"ggm? !Xe1:8e@`6"1[lR5[PYU%R*!``L:UC8N20#\~哗٧{N"+ &iMOhibsAl57T#usw (),T'בC5BJ(#1~Bvڣ 'N \Nk8iH?s S$>A'XzK"0~m{3-1l[!yDA$<_ҤY[cFa4Ϝz!daY=O~eI읷 oJ/ܟٟ\ F ǿ yjFLhg{۝]\p&qG/?^EeQg67!ec !Z <}Oj5Ch#h|~cF)n:- IDATBC±J)ӚX}qmGqmЧ;Ȕ9> b=Nݝ=|j ʬՌ&iCQFa)QX4ZYVp޼.KbAI 1pHJl|R^xc3KcUFR3StBDa\EҚ߽oa2Qo3M?#<9"zO[;;eQ8C0j畔0D8Nݹ̔}-кJ|?Jxi{?6.\5 ƴ?9__sjlr8#'F3sK{bwdA*m,JJcR: Ck-%x$!CF})s߽s XSqխdQ4;GFy >UUQa (RV?E!ל+ˋֺhY`\Yɼ(ϮuGܻ{9qLxa JZO ڣ6qa&xr8tZ8IB_pJAJM YI eaB}x3kYD:FPꬅք?;}mE 'imm(Ơ80,6} \7`"<bL0\h7ۭF$Nxdj484۞ jbdwoARX#|<#f$ux|s3+kiQ6'H)ΔXAYiDY!~o9ANx' #P6V==1xr㸥чU~l#qTEQ1{ZWFqe9Lv0#ƣpF> R 5AofL&I[Bm !Ph:`5g/M5!l8>_~KAUe(F>8J9wۯ}׮&ͪ|0qwv=OU8l1E&ij|y'7_?v"VϞ7"W\Nh @Q0ɉBrXVZmYD \P6"Z9de,r!ed.zTY4gRE괷8PшJ%9&֑ b`OR[zX˳Q=ӨO3~hx4`4s~8ӥ9ZTʦZF?wƓ JѦ7>}C ;.Fzǫ=!#RDv+uX'Ktʵ:/%`md*5x]h(0K`p{{̌5l\]ls$퀇DB#EXLtN:)ng.-tlCiRRa j$2z0DL8Y9$:k<4W~#|&8 TK !V2 {bRU˝|7Zh-/+^KBk :ňPcD@g#c"˜!^{I cn?>=P?;N}+4@Ύ:~aPw ! lwˆ638"m5òJ>pV"׺}n-%5*OxRJNιGeQB[;U|j_7~u &?~}"tΝhnwΝ? +"(cgQ*rTouowzn\xg~A2`8M[[n@qntΟ}AA֚ JxU7x+Ux<AȋhfX0@Yk5 ֽS@Ijj;0䶕NMQ@$*kK='E1Nr),a൛zKbݧDTi5)5@A'[~i,zTeuqʣpTqǹD8G">[K%y!"t؛:D'2ӍsgA}БT&rFt %!l֜/`4;3sx'qJq)&KS%<˲v͹7tB(e16!|P- ZG΂cn/ܹ|6_~[Z Se R󄔹6~Qj_'~7n\k ֺ28cA[mEYf9"36Dn \X (* eLI%p4,ܿwʯ/⟟=:%57ϼ%yoQ'?80{qcV)*ݗ_+O=?譮ݽ.h5O>!H,6n+iPߺtP1BN[cIL#9Fii#H0EaѪqR81 XV!H)RJEA)Fmyիj5Fzi0 ٙ<Ϣvٹ͍.gvwZ)5:\ʲۙ_ɋ|a~TU1n: 1Ba<vj{{gV}?Z}#,??l; ?gc w>׏%>}1Ř"(-0 *PW`ƒNNJ[gS#QA|ʓv4PG~Np3Nk<zAX甔Vktm QY"0(ZYZ>cv= X+ Dge2&*O=CCiG5BVt| o`rAJWJ+|?gdx{q0퐳"H0F(!ˆ`bCaeUe&iCIo24DБCj>>J)OXp;H6Grk8&c眳cn*um-R27[Ic9ec( ByqJ[ ð;Ltd_ՅEBÀ㼐{YZ@Q6 wOOXǮ5}yssڵg,c>3ɸW^[X4`YE=G _ZZ1vw!Zc-F{.^?lD0;΁ s>~{woݾ1Vigڼ(Ry%)*!%xffmw_K]ǞR~'UUߺxŸɟ{ s Dgnyeڻ׮߸'>QO<;YB4F---;k8&IJ)%Uβrv#8Ͳ a:;;{xгMa11#6a^YJ|ffQ <_VRS(aCB,QT',+4^jO~OK|޽{֟ؕv#z{~UU8 ΁(9~RZ)İ=1:i(>rچU8s"(}޷a2-Pz"hqӆ6q80Mp%, CjÄ @:Hs_%m 󔱲Q1Sqj``J]TlƷn^8{vǽNSd 7ƤbL)q^j,@PJh>ZR`u` ͖*'D*Oškn>amnnqs0[+˷/a 2W4 B,t-~gs{x!d"D8p9L4Z>3OLgQFb HQ)hmf9Fa?veL)c#B7>7'Dqk !B)s~bD)=gNGl.|z;FG OwWBnUǴCF8(b2Zqm6"XUQ)4tj##eaFS VP~)KP`DSDZkZz=/#AMY3JeIRdQ(?[5et Mht$o'i" htw.@(`{7WkpLv4]|7}:tĵҶ9S {<ױgc6BDz5=:038KӬ PkJIZջL sL3@J=3;1:G)m|kaSx\&<ώ:4*[?~O<_7ǵA@i=x&j31,Ns"OܾgΕ83z?7~124MwZ]O'˫竢ご, %T'{&M~?LLh{8^=UUYc( E8?T*bu y^ଝc*k!@(e!;FIX'ژv!d 劔xǁ頻dy2ZQ)e{I, _`MwxGOl}OKqgFWNg4tD Cr͐94JmV"z_r8x(i5Bzh8BFqP)M&UU! AW~8g^|hpcǃQ^aPTLST;$Z L7 Bk}{{R=uǜ)K($YU\x dHjk7!xN~ؙmѿGր'j;B:p\8踒j~zG1 I^TB_DX})QOYXYy,3gl/ʩ }Bj dy:˘0*m8cҘ( iύp0 Q1de`(YDaT7{AFOx04Ms?{y/^~div^vݥՙhVN9a g>F GA| |<|8՟ }(Ô?~RB8EyR(2uBZ 0h!xoarmeҭC5WnoT1sh=ltX]sY hQ8,s4h_,ڟ<~>gڭVc ʼn0ZcL(uhy/}k/~m\q%w߻6hؤ)BD+E l&Gٓ]y'{W [TMJ5 m=Y?z_2v8b,E84#41&5L썽b g=pUhu2|5_<8x cBsx2n=A^%ɛFo\"<~`90k:dpJ8Ndwc+RJ+@"667FjQQ \΂/V~"|~RxƏI@gni/W] g8Z/w^YJm0Vc"9x Sk,к$Ño(3'tBx%y-"14Y B!YYc>w}_wgPېS:T3`:< 8֜ѪaPJfqE?g?__lnm0~o"Lzwow88[i40 !Zk9Z^Z(:/gO烯ï1 7U s?g[/ q[ks$,+4ec"Jy3Id pӘV+M[~I$Q輳"k,ex siFYR %4M0ZaQPe3mTQdQ]QLFCNpރ1c!@qH x uSn{gxT5/>{-@A;o=.IBA!\ܞok@ky.B .ә*sşېalEY^e9R+([MgyN)2/sQ]7~"y FIFXFȲ!r1^y՗F~nwk++rMfY!K QP\a'w(;1juLgs>K^u֯>zxn~F7Bn,l!"s+_QUB{tJ0wzUt:(, Q_|:w-ogq!jgY&iz]cQcZkd41jN IYW_}=Ϧh !x8t{6;9:t~_`˺.jl_Y]Ν\FhxGx ܸpt2PF^~w~GW_o|h/"gm-.]ܽ~)Ř!/ztiI+e6qu=!@_%D qdkw1J( H OxcUY)e n'ZYYC?9]TJ<~|08Q'+\ڻX~{|*GdfYB enK)6Z9h3a FΑu/$ sLRBH'>3Ozogs=;'<΋ԙ\ՄDm"Fh)EYC$2F;JI6nMۆZڬi;Dz,\v \j ZaJ΁HpYRʙ"hR1k {gXn&Y #VYqD258 #E(kR L#qv!;cs#G5c,uδ,XH;fBj'@b^YXmGg}ltpӠ"7#7z2’x¤iK/j^_gS8օaZBq$6*(´mqZG2e(bcl$uJE^z!٬r#E18v%üB4Zk.t}? on17jFiJpwl#"cnY%lsskM~?|o}??x.]d,&MS7@}q,INelcM֫o|c|6fBhueU[0M5ʛ^;i3JPMڝ~>*Zk{n$Bk/߹}k}ctag][Ƒ:9i?=9$yVYN_֢* c_DQj^Ãò(ڝ~Fi\Y=|FS΃wꇏʪLh+vh0x|yvw6ܼW-oQQUY4Mcm (DU%)gNM>rN˪fUUup8LfJ B UIq֔LjYuRJ)% #DsQH22kgN F0Nl6/򵫗}nVVYݸ?www{wn2Z3y0北4M[]]Rx8X+h1,lі{s>ܙb|;_g( g!tGp\:# Xgztng5;BˤB3( /ύ1v8BDu(a1*pn 2B(Ax)͜7 :P Nu֎t" adF3F2S㬵Z12(%5py(9ZQ֜.zJvNILm\4nDPc)SB{0qeT*MF(!1zH^y;ryYt0)`V+i34CeK0ş tx@[{`Bh7K7Cι:,FXƜA9Rc#/#,/!M=0qy1(8 `aHPJe^DQ5+ 9 xG9oчx_ݫ|vS"[[[+މpvVD]Esw?(Q p$it/O۩sB9 ׯ_NG 1i!`QA7W0;/Ą$IJSBH#kPDv-Q7ӕVctr:JkvjFoƻL)Ń0MmlgJ{u?.\-gnܼ^\jA1\>IL^7M4L&V+ڳY)JEB#.^UVe߃Td"rcw9д1) Aev?O>89~XdBLkתf(- F(ʓ}Vfumc{s}Z''/]}EfeRJ4v !> ~U{kѤnAgE#x_΋b3 1F'a;U^I v+WW׌5,<⍭ /jJD랆''  gmfR( =Bss0@4W?wG^zhp[Ub@?7B ;>9LJJ`v#r΄ h{iyߥa|p|w0.X C>íu„k<c(a1 °Gkko}uWJE1g2F[i쭽$#p#$B!c᜵y~v޹˜  V:x+"(I'j8Y]OEUcmmnU5ݟkg)A@Jj89e1"(zNU_4'-ަ<_G_utw:#=o'8!!ڦuS{<]wBHFG𺮜u`e,aff=m.n$@'GD(QbAh7Y0$aLjyub sZ!5Pij: C #B <99gaJ(h zFFٓq=@kb 0a8FS>+ 燶4|xj7w8\m^/gνdO[f_,Zy6%M e^qWeM]-’7\ tOyR>8L$]>>[aݽ9րͦs sDa1Fm]f'M]\t 6߻k[ǧǔP}zqR,oi"Υ,?Bj}txD9J N˺T3gIc✫ "DWN !EvCh(%a(bY1"M(cA N&3LYJUhZSF8yqm mkWfX5a|rrkr{wv㓃ݽi+5 wvv?c B!H)u@/;1&9pz !oXmt@hXN8Bic8 Z7,X IeΤVSՋV7'Q,b<4=!:`Oj,&8K@#@P)EU4z&qR!F0`L":`M` aa཯(1BrKvC8_NgJs"bJpy9B]ʲbnsF4clYTAZ<+8&§O0b< ' Q|||x{qY蜯qa- E.֭V6$a<Ĕy,30x/]Ż?wy>|?NAt:V;M[ O֋FSMvhiBRʴ'Zwg‡;e8 XuUUNȧ"k{v;Gs_[e97M!ZF @鼃 0ƌ %KgFk3G9g6fl8ww=7M&Y7xϿ8<||'֏ݻ˯t_ꓭ瀔:Ϧ/>|9ıPB8뭳{z#Eԓ,ng6?W(Q}}Hd[Pjy98ۙڹ ka9i}1Ǒ3ʦ ;˖ Oo~j7a2I8=)g )18clcƞIo0PH H4@#C-b =+ ~LҠр(JI쌖`1b ; QB;"8Q(B[9( -ޖi`mz Bah=D!8 倖fC1Ŕk)GR*2&!fa][ XHe`gc ?Ju.J2!4,Тx!FR %aYi]<ٴ|B,5EW16g/ʡ+PN!4/04!4)e˼&x0 ֮_{iVԏ>en֘Y˲hwvUL0Jlf#HX@)J7z~4v{hJiuU̦vkkx,˪Jj=i_VWWkJ(+"5aVT٬(1Bt0M#a!tao+<@β4myz:9CizW;}!ꀱ( GimqmBRGجڎU}5ln}{_/cZg 3V{oֳɈQ6_1/~杳R_{i0ܿ'O޽w0 7WCV !u㣋^+㢝p(tU:<#tZ)( ƘT:K*!x4<][_R Bi^wl6Vq[Y9>>JSU0Fsh c<RA՘Pf{0eJ (Gm#%*K]U71I߹s{Epd_2/c[- KAOgJʠR*iaA^O>AUu  .|6Q-W)afٙӀ"|]XY+-0s2oyai |2yN%7k-?? &ˆQ:GT-sNH4Zi̧CpO1?c8.lhjm,2JV{cx*kQ1'Re(y8 ϲjZ |P4axagkߛLf(n2)!9J5+5+ qfzJ,drՍov AQ&()ђuO?`[!eRb;a'섎orN=lu𽪪vΟ}};y[n7M tW~b|x4$qBJ1Fqmo޼o;y4PvpGiQaB\z;?xT5>4&!ey|ES2Oo~{9,w,NvnB`X>C]#])Zၧ-mv:&Kq V&#.8(^:p|,j[<'S#F(Rw(!֦n[wP;x8PT A0R.ڇޤ,+m)dp\@l@jc'H9 M#s6㪹7-Jsws K孇Bhs}Y1F60Fp&5xHBNeU'-O.rޓMfv%bKS^iRV56MPgY&Qt{E)e7mTyg+x fj1;EY[{3 \Mp❛ד43?x|Hbִ 3b7H<-l6+Ҽst2>:|d2)ų>o~ ]{׳T{w[1M'EbmmgnU-67W !}$L%`4uhc1Bs7߿gi HIFa D!x@gܹs80{0x{%h Ą, _f/g`8z+?Z]|`ow"R(im΅iCqP& BB8Y ʻc$cjV `(%J/ rTɐI8Փ=tBh,qb@BHwr>~%ҀsZ?"=tG`s؎/ Q )8 *0Wr@!DNe$KSXk-Հ L .xJBbdV p&j7kNiJYIG(Y0B-nQFB7MDlaQZ+65^aڂQL!!wljc ±@1juU v֔SI$S-'ei)Ɍ9"5c $.?iLy0ܟzQ"BkM0UcC06zh43ᬱΟ;T 4 :gˢ4ZSCIU ֹ`0V (Zɏ?iB`Ɠ$j[4Iڴr2ٿ{r GsW*l}e?j{pY DZAwv7| /,bkscا矹}ܻ{lJ&ZkJ dqx &NG"¼Z8Ν;[VcT@0cQZ5PD"" k\Y]%6."rG|:nteu+c֚q2fEѴKc$nޘN&?|mK/_t2޻{[˙x_>ؽr>{u1?tZ3 nsݻs`J&a#_pu|th% yALgRj4Xj-R>aw|v`LZ)uEԬ}j"\^=6k`\ҙ9Hry OO;u7)D>8C&&1k ^PpK)ZJ)E(Yil-E4Cc`O cDZa (Z_[>xBcAC!?~fQ4OJi#JcUuݭYY:"S~i]W/ c sZH4;iC[?K"Rl~][[k"cãIYxgz<'ܹ30k UA j ֯iU;㺩ć~F8QTp}ݽ;5euﳇ/{C(i/OTZiJtZH!X(Q,ͻ{uf{[[g.\MӶM5_z[7Ο?w+yp…?xp{wxQ @յ[:/ !!g-ć 0B^Vx*嫷os!A(1n <@c6T,#ehKM9޼q-a ay2AMlQY6|ྲ26˲re<]6?$\rm~?h&m;oSZRRF)c~0>:"0BEMۆ_:ӭbw'pr JK܉_%NvfZK*R备r cL){!'{!/gpCOFѓ?r$K=HXpgcbBH FA3`hSB1cжe+Mc _)5.ʦDT;YF[cu\DqXa.x0B<#H!b>8x {o=AX*3׾@z }p@<0$<ǺQB!D C9CB(x$NeU !\WGFO] }I"<1eS'Dc)RΣ1'eXq*v˷S ƓJB"BȌǒޏy*jqmU@k%F)eMg^=k"կ$Ucu#Z__}O*a̢$Jon(ARMSJ;<$"DӪtZV5$tZ2Fu+a6VtZ9#8 ,Ehyf=mS}ZoFc8.e.G#Bi&\DQ[[?)cohŻz(ٷ?v_>8 8!λ(GhDcSSc` (PճI&I3ڇ1|Sqet˲릑*o(`LxB^i&qEGaZbQE3<"5`@Z/1QB U-! F> LkZs:5 `󢴀2ʀgaQ!%z@kCP-B8IcB{.Ţ,˺$4L(|Q !&`V菫>Pq1!3<.8̪YLD(8:uIIϜe gٯvGPVJ sct8S@ݛE1'i?ic#cySd|(gNg󦩣$ ?x`k{t2ٍ9/{4@ayEgPm!񮥐V!ߺw?IՕ yP)(Mx)kkuYlllKN?~O߿pµ7vbeeXի>G\jcᰟgqh<ܪb1_{y(qJ+`0ͧs%9)X_qdc }i_.h8A67۷oo};o|}Z^[YZ?9s^)GQ$ԅ$~KWxƍ39S81Zs.{,ZpPmTgǓŭe)8cƌ6eӴ`TD4KF@ "&q׫!8BPbB$ B"b0B ljx.miBe}|@ljX DcDaB0J!~?!w2 !Q"9]*k-AhJ iҘU^,Jx:yv(n#-vBUO]]б;ǖOC~&~ŌƲ-U]IWn'cvy!`Ef!Q4!ʈM," I?eIgUәs6*a:9y>""X6IҢ,7ץ7m1Hi1?w'+Ѱesa zc6l<14=8lijIb qVQfG{{ͻwn;{o~g@ 8?xx?@l. Zky gT!8/fԇ(%R|~8 ~/l&SfsjEm&*7` n\YE6?:<|Qhm4U3H~?/~%ÇhC$>Zb^gmmoogلaLX ے;O09gimH#2BoCpZ!τR1mU֐lWZK)`4L7{~ooU:68B`Z%q+ge ꚋ8M&4\ /|y}3;oy/9{>k>y嗋gΜi&xt[)Bm,sFk&Xʲx!倀1>%NşS'xYr9J|QHv\cҶm=9lO}䧌ya 8CLpZk+e, -|MoϗEoEI|ʕjϲ֧m9(OG!%e l[42xG0(I|a[,'¿K]?᧟OƓ|r%B! oy)q`<~nRƓ/2aYr!9{!kkՕp (\օu`OOI Q󣹵IƥR`R 5m+kAc<>:MclVk;_13hcR F}}9/18ٝokU+C/J`w~`{7#Ӵ!K\$fc\T#ZM"B`Y?xxxpx49geD$˪BdDO(rMm!blkQ`1β)-j<Ώ&/?K)}`g^ IDAT/^,fDjmhU"E[Ahq&Ql)ƳY9a8SB,Kwygy[+= p)%)UӴbssÏ>[gh8"Fz;>:S7M+m?s.%(Lu.4e! cVgPJ1GYJys)?^}@w1ƾ~dH^7xδYˇUmLb! !I,p]_[] GH$#"HjwLPʮ/x~kV2@8jB:t^$I]3ϜK~ !뜒 #J?/i=wBWϘ( AHČN&Q#M]2Jnjet2u6v)f;gG}ig,!HXYV)x"qkmBG[8KĉW?~ܯa"q́4Ӟv_d9kRM|^Uuub6yC1yB]B'[jYY`D8W$ywp8-sːP%AhU.l|v4$1( Ѭ,dAʖqjJ43mXkɂz2-Z몖9,CRyc7Dc@E u)de*~"Ʉl/ܧPdK B8#JĻ 8$RUm WFڪJ2h뺩JR"do69 * `UkwxH1q! :`fyvW}BNSpF|> s L8?\V3;ms0߿xioƹiƴ޽p+/gW|vc;h>Z> 7n\/Ww="Yw讜5{ Ϟ}ΝC7$huJaTڙ0[뫫AGR6I-ۣQr<>:L&SxXڧI">g`8Z_+WϞPS !hmbPJJt۷?jk`Y7~m K;kblʪΜ9C"D۟769l6$ zJ겪`I{`cN Љ89i3FC< F+Ii- !@ hcfj$Q\V L@NDŽeZ[VUSZݽZk@Bu߻%:zQܹ?K/]qY?&iETZkۙWŢw"(@%8I}c }$SNLȎ G_ok_!Ľ<]w !X:€HKq! $<)iL! XC6|>XE\PJ 2XJI2\/JuqQ-dЋ@c(8$b;NHRh.@ !8!B(+`,Mbcl]Xq-c DD4Z RM@8Pvꗿ<!&:w$QiT-}QYB_ u8FL#)!4K3Vc10WeEiFc,eݶ i2,G{ bns!R!iދdeoehheu-KX,¨(kuhN@E@YOt|><< ?p0N.]ڝ;{nE1oKϽ&Qd{x41Ydi"`BV6GGQQa:?{z㝗X Yb1{)JM)z {*UU={nmmm<'I6Z]qc\)im C|o\(ֲb(Q°zC!A(:Ad%olɦKu)u@뺲B(!F*-4K,.]Dݻw`$)ƴi3欍S1Z^y۷nHgHzAUZ)W4_Dɕ?tuc]whM+ػV֚,͔p8MCbn)!B'_#]XJ*OOROO*l4C}?y' 4 '"2>ytr6#(S҂ w{=@i,@i!  1I ">-@sN4aJ"Xi)q9Dcwv%EJ0"4M1h4X]_[IgFa&!TֶZ P^?8"-j=Z[\ۻYVb1GBXFj,˞yM&ٶ:1S֨i\PLh]No;3c̩wu(AѭΗ{~" F3et1vqS־dlV槄N0j,Bɧ8qNs!tJ)U+e$QA`{l7\/8NU:h4rel_(ҺQΙwy!dYtnY1c(h]VRBĢQhoQJc%3Ngs10O"!&{/ v `xۦmZa|u 8c\pk bܴi {Z^_+PSbK+s~nn}GZI//}NWd^ñ%8YYcp.8G̦BC$XH&[|/K"Ik$a*F>XeQڕW_~wܼq-Mӣm4G퟽Yote‹ɶ,V D\G?׿s'O_x|hY ATI9k kΘ1Fi0{ –e27)@_}jd<^ܚ?Ɠҕ+޸|R{=|xbQ4m㌥I2RhE!dk0FVF7ohv?:x?͒8ٹgwn uΙ3ubBg~/U޵!X(eh1D a!0F]h1dԕqzǟt}%.5SPjWO7#K{r3Kޕ;juW-'F~ysﷀ('AЉ퉫Q4VF) |B RRk 0|*,4!1$&smm9A0 82)Jn,e`!0%aS Ɗq10) i1XRqX 2Bx2AbT B9B Dxxa͏*PJkkXkZ{nZ,:a̝rn~EZWLq58yZ>EeYVR/*]!EVpy>=YtPk]+ҏyUlqWT=ֺZWJYQ(eiڜ8c`7@QibhԤeY:.a(0ш E9J8 PF t$!Α16MSJp2IFCJ t]CQVQB)Fif\uz9I/tm~y`4 '$kLv&ոޖ&naR*0,'绮;Q|B+VGd2k-`L(JǏ,r 𥗯e&TR껎"77M O77?GGF)%IR-1͋`b7J( %AkZ-\=}FgccA)h0c2L ~hu˼@˲Z0/.8`dyT9LƎ㌆frg7mmmٿSFCL0Wp<,ť(./˪V_%˨ۨNDq1I!0BJιRKm VH;!tqqq!&+^ԁ0:k[[A,ʼȭiEyd21JםeG4RV!-Qk ƀ+/]'fcm};'^{O8/fYIPJ%Jk+eTR%4#tFWAg,KZI J1iif~`91KH)+YA񢃍Rz:,Lؤ|~P+E3fZiZkJ#A{Cc&j421Jk>u%;a?p\lsGC\%RB*JKJqnraZ(Q(qfJ3AH^05J@R5,F* $VzvR !A"R} \22)D-lzz5'AuyQPBF & !TiJ-|LkZ%'^_U~},a7M+"H!fzܒ|J(iBV ͹FkP;RtK)a(-`8kB`MD{FYR%`0$|+}!Z9L5inPgg"vw:nwD͍ x;;{{{O- 6 zxɰj՚ͺ PX0?tSX`ރΜY-w* b٨ŵnP%z1F(>{O?nȥ=YZկqZ7/F۽~7 ŕ$MD*h4*K^aLӴ՚o/ޏ@!cˋaaSgtGh8 (,sEG_}-rX:aeY( }BfY q=/+W.ݽs7~]ܼ++6i/^8{…y7ad}d2( E\puQXF$O B (P( l!"#kyX!*rsqhFx2U$qZf,/lc{󓛟8R/fyZKI:{fm`T:έ{w߻˥\]]RuXmcF}:K }P },`Xc! FcN^ 0 È,ϵPk1u46ɋ `V(e pǓ0 E>`!L>Jxc* 5@Z[aPiuc.cAkTJ k17R c40^9BR4LR FP{4#nTr_ 7s;M gm65:'4g O1'3rpyż,ZTۭ@k BaQY&"#=w(Vk1b于a<á@AK)( u<YFKZ. ѓǓDBp>Aѝ=F.^- @U\&~K! O=~{^\Xr\XdΊ" Zy4Kr)Js-)A%,t2 J.!"KgΤYV=t]Z+c,u!mLŵ~X`8--.`ҘLiY^J'Zeb\q&ɕ+/;9oV._"w~{~ޜ{+JQ/1 A@$2BFVImH*eS󶺳5S/y2^a}x*__ &{X#ksUvF<˫nԬC( XPeJJ+]1ʟ!MEZyR!8}ǡA5bdZhɱAC\aTo 'E lԄPLT!H k0cyQJ9-!D`,Zcc/9uX^cr@yjF Ƙ5d\hc9㤔Rj`Oqwc\E*=8a4.!' V^l2jRW2kU>mVr'`?;N3j<"a0y1d<ݒ$IB !dsFQ c DkDZ8W.A cNO$/KeDXGy%˧kxu BZkA?N_h8ezg `r. ^{q"DF0-"<%8"X*s;Vkkъ2vttuW9 Zc sW.\ [5Ç1/^$pWWOa~Gz#>{|^!f"hͺﱹFu]p-yɳv{B`AQq=*RJah|9ۯ5[ aQ C uhfkFJ,KWϬݹ}^jGR BȻc0EYV&_7w=+)RRc$#aFYx?~RׅYH%F&@9dy~P9Di ,i4J(6Ǐ!!Rq8Fi^fBqEY\{+{O4u0\x<_yAo߻.^zKZ= ?دΝѻh B`1FyɭZ.$DzӅj wJn^9H?a5<&})o/ ~.imN`bO*~ճAPiLfP9UTkS+SRR*LA!)!RB {Aܴ)Zq01 ABrn"ť8 i0@ #0Q `J! .$5(O҂ )@*U5;c$ɴў*8B(J0u̮ :J+qe!(X "$IAl<| B?]4 50-/0ƦBS<1́O*T B՚Ϭcq8c Fpuv2j-[ xx`8xAxji~㽣Oo5M _}/vww}?X>u 뺁򕅹Oܾd7(1YG FY^nlnaLWO e\3ÃdVQ5K^GTǁǒe+ xQV{ !Q-.-uzo]tioo'x6@+ivwkh<a|V) j8:h\p5(\b-XZZ?jm ǓN1q#ӸVGY!;*zzM2͋T AK)q(,ĩB8=~A"VpssMhF%Zl,`r.Z8?8<,r4PYVuaѴ41%iU"0kc^ z[(c FXJIQ[ 9d< ak8Iy}@iN&Mn^Q\ /_r s°jz=҆Px&c CsB粲T%ZI5>0 ڦiϗ1kM?Ȃg?Sq"A=NMJS{iBl ﹮  6P!x6FkJR eY cW#Hś,R>%(R*rrT"AF#$JM h:, CH)駅1, p8*21S݆Sr9wqZBzBE9?W'i20R )BJd2O]l 8<'l*p~%ak?gU-5NjONVl͚՚`J=Nyv+M/8ChR$H;-6hb\S Vnkn!"2YEZsAՂr ,,!8`~#xp,p4RDqGOj|[+-BM4 9<:w]Y87l$;+~;}fwq0 F`ob!ؐf-`OJ ^sGk ƭOo֢Pw4M }ZU\կu8I'W_yOwh|woӟ||<|p؏'a[O>MFز8 cdܟoC 537o}Ou:;vR"+8B(Pd<~n_7zץKAg48J8R՜,he|܅F=~paJhf!JAͷ[ͭm (z^Y9W,K0^d0k c4 TZK;=FVvvN>}QTJ4M"%q0 Rd2??]jq{`Ȋ9?uv5Q\s(Fhyu>}Ƨ>ɲ # zr 7:.#=ʊBRp 0(]a,SHb+  !,,ϭd*ih R# \\$I*{O *8xPFs )`}Y]>e%B ®1FJi5c eTRicTRu<¹@,.$I(%s`-B,yIY+BE,BTuGWY;xlJ\AJ1ٶBfv&SB' ^=CգVm@W*DXQg3K'5Jwkq\n@|J\h4#bSJ!`iz\=#!e&RHFY0e()L&ܼEQi `hΘ,.%Al)s1eFǏN&Z}v{qum#BNzFǮOi &B4mz|֧Q`~32wRsZp)EDI2~͵şϬo888W}zp8N'\b^tݽ?7WV>o}˒P-..y;Nwfed2#j56ֻCO>|K'2ߞwgw8]͛o\`m ۭh<Z"dK.J{bw-ƨ 'Z))ED :$y77wk;GN;w'_L'9@ըieeYe/,k }'.,Gv= x7.3gGTrk}$8 V﹖KkEF jsK0!Xuic`ع->{xFei,עJTFƂ B"f9B`HѸl/,X}S4|Gxvk1*:FT7 %c,Ř8w B@<ף`cA-4J R<!A~؞sݸh/-(ea,_XhZ]H#d<~vZ59Q# Dz91Rr<>qkíyBW=wիWrs<8~4u@) TRr!tz{"`" 0IgzѣzW5ʲ _^B 4F٨cY6a Οhoݟ d8ۿZkԾy\~(;jU_ 0¥]Ң(ad<.B+olnyV)={~1ݵͭ,(+9w(ɋWΣG_K|O^{[+毾O?w n.x:ɍ:.`0^JUHBb- sVO=JPYs :8:gwc sJCxZᣝZfpУ`0}EYh4$D$]8:7Fg8^}kk^ŷg/]^+?/\-$ %2Ӧs v,F%EI)#(e|6ln*/5/JUiTk|i9ė(%fvь?uYuUB{oԌ ~&$ Xb\ !=^O0JkB%֒ R !(2˳*ǼCk!P10Fy(sigyiF^ |1͵LV{c55O:/Qo4'Z2Z)qk[kˋ J@H~Fs DzJC)s1BZjeRZ*1e/9?A~?ޙg'ЧbV<r1"gJP΋ȥPu1fe):h4.*AY#3czZʬQ`QpBF:ab\,O-5n7Ks2&˳x e)Jqa,<QZ IDATRGǃp2J:Ex00)rnƵZE6ַ?wI&7~? Gܰ/ %)"3rThucxNG鬜:]Y֘$㵵QwFOzq~{Յbkq\ǣ8NYd>a-Ȋh}/jϷ{G:kA:Fxŵ4)YV#F $ZBX\ )J%Fp8 wXZ鬭WWJW_y5LƓIaQ~Ekc˗.GAݻ}q}Bpja0R"jIv]wyii<aV} +7+ ]Ck]}QS />O3+$W2fvD9ְ_lP.Mf#\ F c0sZ+0,a^aTT‹Mc=iM)}%XSyOWb,Xh%MPj(ʌsska%B6/B/4K<+{U⑕Ƀ9]ROWh`}Al؊G+Y-x\R=Y ^ ~:У幀E.XgEnM K t]"亮0J(c1 !)% 1EcAʈ9nJj48ke{֧^ty0i{'Nwh Hĝ͖d{fʒ-Olg*TRd*L3I%v4ȋ$/,RW bߺサ=?F)Y5yt;l+|Et2Aη^G+K+yIQSFBSQXkej! !")%!<~ᲪB* BdbkZ%`^,4w=j߹}Z9D6DEYԥUџ<7zKZ{Vsȑ9~ǯ\=׿v?xB3]v%Ң,-J)&q %2QRϹaZ1,a#FIavHًMm|(фFJa|ALt?iDwuMHyrMX'[i۝ !v`bݞIѡh<8ۭp$4bMuQA^iiA)"(v7 D#ccQ2APHuu Z!Q B_Qg%*4/ 4,U]LRHMB_)(5a hxBY3I0Ab!i]ƐR"VU`BTZeު:ߵ}0aجy 4B<ݿzDn0`i`p|BxR6< WδbLY-(!ǒ0l`d( EQb=Z! ]QB`:QʘK&y15xee5/Jse[-$c! (ʢ!Ƅs.$OANkΫ$vc3qcORV@#?BgckW.Ay~btxH9;t{<q\'2?xV5՟^ Տ7ۿ>v8Oƚ*0(BuN>~?G]3n|kkkm}N~~q255oiSOgy+__wȃK(㩩G.9tfYݺ8z*R tԙ ^?:^\YLojk}CyswC%I+`T/ qgnSp"LZX~YR)FIhQ* !z-??vu1a9uqD h :F!Xj- FEly]ommiѣs^k?y{ߥizh8RDQ,Don;B4&".$&陙;Q*- TX kwP`;MɁ aدd\+ZcFh%ܾ~0Y]5= ]\h6DׁۯLi}m%]ct9sq%cR`͋`vrP0. PZ]}OF 1cJ6\"XCV*e5FcFiդ7w CGa+1u]{1K j#03aTԥRK`xPJ[U-%4C!$4% [Ņy#ǧiUU5^EͫkBHYUUoVUUUj\EQrnwBo2i뺮Xes/\ƽ eyr5Z˹nܜ2J$2Z~qP] #^X<"\@ 5Vי͊h 5Fkch4=0PR :Xht$R%N e< "cJ$BH/S\x45=778 -1wzV{o:#7 mgHM^ojjfJjƜ=ofzvcTonnVI̳_Tu:aLM&t0bL{Q&QVu=3,.b4 K}8yO|zc}msKAԩ*+JlUw#FB)՜ yY 1fWu5Jk0 (@޽sGOW R!Ydc6=}̙&'QXZUՔ9BqZsm )m #~Nee]]]„yA1wF^?ntaă<eY}~}yiqz^"?V]ʲN^Zcy~($9jwuYU*N DRBp%đ#DzlB(M/|q8>=1EKQon?g;aQe.L8ǣ$ǀs(M&iUJs]aBLhg' } [dfQJB62ͭ׾;W0 %ą M'L%A` 05Bpl- ,OsDMbWp)PuTL&UuǡAVk-B n<]'nV>7Gxku]c& !#8 qֈ@וXրkQPVxg8PTJ!48Zc)%(J\ &#aH*ZekBn}qyQGA6ka\O EU&\^l梒BIi vZʆj ,%D*)mu 8ZHF "{(6b,˜HB qkU]Ygff!Wimm,ݺqc߸y-?-#?ŏ\q0̅jkgk; mIs mOJz!ۛ:~뷆;ӧwcO 70X/aСg?wg1"0qVu}ԩS7_{_ $~cHwks]qV "ƀ۷nUk{hffݛq8,51( ?^8tkyQba)'?BUNu7~*qׯ;+3ϝ;~c[?6,y.}0 IDAT T5LX /iU wZXQ{.2\B4B \UnQIAa mV2 Vu tCkmBHx_]R<`7Ԇ1 D`fE*Z{sBIUIZ:Bxmc;/Ka$(e DsЁM! E ~U$-<.hRKE<,}rɀh Zg)xWyR%r!AIHXK4ƽՍ~kNq]nonm&I˲tskVu=ɤR Qi>,?!m:NVz 1;Xzo•A+hl'夬s8r_].cpZ;vNƍZ H@N=:NO>g{_G0vFׯ][[So?:~[o^U7Fg%]rﷻ0tz> JZ{Nup!wWqLOMI3IϞ~zTuyzK/oPF߻T: >&hgblv]Kڬ(\FQTfe!L꺺lk6CL9%WOrNȃ6LP46Y_FY!wi[8ą NŸ1=a{Zr!Hr`8J"~??k#7~ !<8؈OGC{wZO(q\Ȳ|367оn\5D0 9zs FB9ژI/n%R*Jq$ITHB\P64.uC-he6BH!֪j!R6 FzSrOI7i1J)D BT9&/ yJȲ.4ljnVtf[SxMޯg 09]&C#=1FmJ^`&vL&{L)3JX~FQL0-k Z@Ҽvjjf&LVGJZ#7 i!i8=3er<'N)&$r ۃzgԍHfNpޚrtvzS\$ Dn^=67?/6Y>~7_V =ϿUPwRwx-V^x V4ˋBcVex˗OZ_[X_)" |-,6Zܼya`꽕V=0ks |Ki:Pqnv;|\JʋRk;3s33]Q77\zכsW(hh#oZs{uu% aO'i,/ΧYaY N,fY^ocW.-:Rloe-=5=7;35;;ݟsVddB)!`s„`B10ǭx-bjfn,K F1WI\u]h]Wwv飏c/noonmn8onQB'I<ץO׷{ BwU]ce:g}xvC#Ǯ\ =q_dzX `YZ+D2,jh-a16IQv9ESV 0M>J mSOCB҆0>RE,|E0+b}n_q 1-rM@{(}^X} DB.l %0C ,ʽIea} [6V!Rf|0l3FC;x?ɳg՟-|K[O>ԕ+67טÔT²ʫm(R)=5]{BҜP:RpR[k1BEY60^%D k2Zu㮰n栔jB{J5 1c<9$X&o$?Fs/06fspk$ܨaqJ s\J]a` U02'FJaiRqJ1Rƪ"À21 FF);IS1AQ+`wnOvfyDS,/VW0pUJM&B01JJ'pe [SuSm uGXdCX̉֞yeMYuU'pg]?f~ F*,{NpXV"ٙ鍍4?\[>(Z A(#Fp?KaoZr0ۃ,_kO( &QYFxa.ϳӧ=~33Sw~R=`]w9VϜ~筷77_+[[w0 :l $p܊ۛcՋa.]i'[ۙ&\URIM0:q{.|?8q;/ݏ6i%[[[ssW/\ԩS'Vs _oX {ݎTuwnݸ/_׾hgmuӧY1bʝ,xi'eYQd͋*+aIeِcZEY`"? 7$/N]s`!DhAqy^,3[?ޟ_YVl 6_PU(vѹJcL+pIQ1j~ 02P( #kڽ۟ҼiLϬ )1k_aQTvS$˪%g#x-@YUyUEEFB5O*fvAIS>C{!6yTtߥd& SX`c#.wāgR aʨJJ3FBBEF)&m2lUeUռ橘S1q]PLj_Usw5akmkh3;3`R̹RRBzs4+m-vV)(3XltMR alm6mQTZ)8?R*t+Ȕ*Xò <.x$*+({qKJIP݅@qumi{*B5uac !"!ڈVeJHJcH3RQJ#3& Hy!Jk%@{n&Ef%&0au]'Fu$<<ޚvN&nW"L"2OMt棫+eU278qDl0wޚUuTM0yi!pn~~k{c<zV 1I3՟RonnEs/.#_5 ο|x(y}sᢨn_\\ؓت,=/b\pG/\Zϋ<QN:ʋAeIWu5dJxqpfR \Zp%w]J"Wki /`{.P+3q0N"yE_?ܿ?[` !AI^@ 4!Yh<9rp*k555幎DwYϞ}rfz#EuWn.]FW^_X6VUezZK9@hJ%JUeA˲fV}DXc58a 6f> zR8Gn lVO׈ĵ>Ljt_>Z={BRV5/*UY|C5#,+˪ʳ,k|_ " kJƌs3Sz8nM&uN-z[e^caLe$I Ѯa|nck,X 06& Rkj^v|8 @>ڣ|uwnRU E Rz@%\+eQ(p}m '5Z[ckMqr>ki)K\  Q+sRNǃac52a)ea%.*60NM1Ah-PJ9 ^G!&TCLky꒲ȹe-E,==lH: sSU 'jq[[<7%I;r]Ï>mWuii}8 Rnn`F۝d񓧶67=/d>sq\ 3]\=Ky O?ٟ]__G߻s/'NkcNWb ܸy3>zo# Y $Q{s'J- !lx湗y-z1|BGI)q IDATgbx7%~bֲ~k_ƶkU|ٵhr^v*d$u~SʓϹvZ[qvmon~񕯞.Hffǎ?rjKӻj,.ţwn|9.ɓw~+)x]9VOEU~'UYL!bj<3;`#ˇO>~,X*XJ#!(nNMRpyŵ֢9*5PJ UWZeQY.k$)uUV iS5-R) B0(K)$ ¤A "x˹JQBFGk !0 )}2Ď?aMoT44&Nd[4УּH39Қ5԰6p `W7o^x=DV"+n4""N?8Y`5Z%Y>N3a!BJT_gS'5dQ)ۿ:PɃ'+SOnߞqxT['kmԪJ)N)0Fi繳!495;3=a^ެ$Mc9<^y ))!q%UiQNz)՞|\ 2kӪxqYVӁB/^xa7ۓk+*t SqjW{oٳ? ؝=<'n7޺Fs,KVqn:QZ1Jj>Fd<ʲLkU坌?t(q2TsUiJ鯋3Q{AF(1?*<*'~5WajPUEH) Z++%1B"Z*!BʢU@)1FB*qQX0FFcaYkYrlG0VJ!3 (j5!l+TH&;6y# 3h1FYh( BASJj !9zI˹L'c|P9GrV z'L&ZKZa){MFZӜs)Kf= (09jk:$48MF CЕ+s4DI)xu>%vc78mo7ןɀv{4]Y]]g67;]q$ni+{W_};8|v!&Q'<`+ʴq*FYttKg!aغ/o[8 4ΝjňNL(F0amf6+4I7ΟGgΟCa" ջk>.+.^z9c /ǣ4Kn߾z~}87B:Fs:K3 S?#+t4(n,#b{?oʽVBĺY|\ ,+bt2z8<^ m-K!1ڣk&`Z!1c\k~g?? LOF} Vb  |gNF^H)g1F% ޺v /.[ƇqܘZYyjVy#Lť=!$!<Zf0VKyG}㴔5l}?>(T5O58NTGf;:x,0 VZ*0V'ΟG >68U=VH^N=1(##(A!Y3ƔBhgYgzN#; )U:s#QRtc)c1gRbnZ(|ϳօaB )+p#ưS8uJ#ժAd3ZkAn*4"j=7!B"iY!S(J c cF) c3u\C 1_" scǹbS cT3l5Ο{x֨SSx`5 D1h7h?f')rds8e5v-/MAwn8gzۿQ-f n4&2E>=?9jaL(e'ciԃzע$ǣb z[6A[*(05s{{o|nbiL^ݝ?K_"&MA1KK;Rr?l0H)e_{m`+8`?.^ta}\Kq2fPZMaO>ͲR(;cDYEPØG5Z )~{p(_9Ax_ʯ /|GQ j,IX+;Pb~~L^RH{YZ*!j)AZE6BLCXJ=J8I4_N3qt-PSJ3\$Ν?pVo~/paE. zݸ=YqnQ&''{7+ 8o1iB1t0-ZwO0]Y-u,q*ĩDxяxՏ S᫊#c㓏UH% Lm6{+/Xu{ꂫDx̎x#!&PV)7;0FWb R0EYI)g,BٱR*m jbD*(MDkK(BAD֫bP85RnPJ !=ۭɉ0?U)9 2SB耭)ں4Y3UG0~&lՖLړCc,O}Opsc*UrOq T >~ c|?;`eYPJ=3=c aQVRιYj6!xx |Aнw3+RΔA;Zf:NZEQ #;sv9Yјl5zQ8kꇇ^ cc6A;qrvZEq1{j\ogMEެdUhR^o aY ) ) iƘVy@ZIPe|?!eYҐu JU<䝼rhqQ) pr]@3LemiC>r yQE!ZfYQ`3N*䅌yYTaM^h-DBPdŐW~]RL?UǵNON(IXɡISkǃOQu)gRZ>BXkEL)眕J<5c'aBm0~aBQ*PJ!Ԕ0DYfYy /nܬƺ 3RueR)Q H*V`Cc;)q}!!,T8I֨$(Zťn/3N;I2JAÜѳ.={qqvnw{Y:Fdc9*b޿}ՍZMch-Ϝ=J([ V Qb`Rzܤy9(5|,J)ʨƹK.aL767D9hO,/,-]te1/Ͳ^~յg{}v곳s3oߺ~G|(`sviajYo6>^E9,SJsΕJ)Q JHGs rA1-uQ!Z+aǍ(oޖ9}E5qFM, ^kQqB`EiYLMͮNMMlo Vo!91z٥oٙ,K˲s݃6Bqy)SF8% #R\?W?ٸxb9RRpppىv{8kgwi2{깩̙ǣѷզ=ZmL)plk{7Bˋlsc}=B0[9i-QeB`nZƘhgZG(,˛驙8wvW>.-9_\\vWWW4ϲsxx&0/\d$|F֚G,0}o~Q1Xk=LWWVn}y+Egϝ(ݞ(ޝ;#ν^_k%,2/ 8C! VB=8h6Y@VG:BN OS_Q!v{,GSKXc#czsMyŬObNj>V5eV`B X&X<)jͅPJSZ/20jm0ƞT18,|$,EGE)RV. eځ! J A:K)=[cZkBIvuN%`2=5e:wf]H%{ϓSϷq(:@je)}8wXqݼ}g+KJЉpIJQloo ^x镅dv{{o~rޭg{h$!TdQ_\rVoܿ{K3ƚ}7uy[M- h<{pO?ݫϼ'S._ۮkF[#&Ӳ!5Ƙ1Vn'=Q\׌tah4&&)clL)Zz=_"40B(jOƜ&ca k?+DfopvM).A#(+gS3s~ ngp9eKfƭhif1Y%2 L<ȔRIf!Tȼs:0g)!$dE,.,ڽN>XJP)vYZZ'ۭxh9'v'jg6==pZ]ݜnNJ魭ZoʃvCyrΝ lmom*%..{ !q}7?쳛_~+z_|1:|T^aT\o[a&dJsYҿsV٨7+mۭɴ,//qiZ{шRf:rk0 <3ZQꅡ^8LyRZ2T?JԌ ޠ0[{wo'i0adgϝ^ta(4/шr^y]K^eٿgG7Ssk>>cYm("_^:σp{{^l4FQge=PgϜ{qseY$˵RƁv1%1]t~{gࠃ=a膎볧RNƙNaN?6 'o}B>ƗB"N;ucԓS+(GFAgEQTB֢0 | )5#(=)Z4Fg[ueYY89bCg\8BbTCAt8RV8TVl֢8Ѩ68 J(Jc-h)RHDK * vNF1&#GܱS Y8ÎD5n#cɖkh:6~hk1"ܣU}Y׭A@! x,Ae(r{[ۭf3s%eeeW;kkkq-Aٞ&6pEs,E%XJ k{ CYa-JnI:,1Xu(<}L8L4 Eٚ_K8tkċvX3ۨA _H8cˋݽuO?tVjUg!^7! =o4TNLMqsokv-Kv.=<,+\d$D'Zwnccn߾s(DžUEqIR0,__[2%0E=D:侷i5W.{R޻}|p3~?^G?Aeo}sfs}}gKOwt0,j&/evS._hD?~g\ v5qEd儐O>trrr۟^pvo}tbBdEA Dj0 L,<`Z1#hEq# xD2jzZg!D{T]%8Ftn{p<7>hb2kktA!|B'&n{kﯯ1ƿwk%EFJ{\}1gtX3}FKo~ysZ׊ GJPBH1 ڵZ<2|n ~ի1bӃRծ=9uowI0&S ǜ IWDx'uk;)Kr D)S6cB@t1Α8B(R 5 ,pSJ1B(Kk( kEZE1dYy^Zm6y@y̽ZcQJ`0BXQIHJHaRIJ15<(,\Tcg-0b$BTRxB[QねCD@xܸRI:j>~”Ҳ,OdS"DyRӽN_amp1)˒2iP Cyybchc0Rh7BJGZ-g)kI>8YYyq5F+iP3f5 +Mz1Lz}{gjc L\C0JC$q^"O8Z'i^d5&$IK!JK)sQ}NKvPL7>^Ynl2n ۷oYonwEVܹ{_}7m)VZ~(ʲjnm~0  x,,Fk.hfn?yιǜq/%t** #0FY.vw0VZO4FNe9CW lE^ #D10ZK?PRjm+z_ލvk^q_~IY_t?d0{KgFAggg{iqA޽;&g$q?5ZNM/._ :u0w#lW_^{W?sW~g+^yxޠqvw懃[x;%qfJE/Bh1 NBJAu;BT[Yv{eYLQr0?ŧ~G>sϿxmWaky旁A` f[,e7n'&| Tck paaݛ,vv,Njg eQ`N Q8I!pήZџ|Q']%<)ǁIPRN%B1A'OF3FR XWYH#s1.T"J=EGTx5Z'*קSLQ !Zah-=eƣ:![0Fk$8 G1C@%c m/VZ4#&Q@ sXЅ'Qh'4)!0dRJ(x@1*/4mF 6W,I%ȚJ_[WpZ1B߯Jo*9{d^{ N~hQW1c]68 bVLDJ $ ;@3cBTGn-OFT2x2HA΅М3p0R{g0sH~\|'qcʲc־+,Do8S2ZB&L%tJ1 zHJ~G*E`8rQJu @?:GC bwwknf(H`F9F@lllu7K{J94ښ7bq(F~ E! }t;V{bqf6vn]o0doo/R!(W\yxIdww "J B:47?9?317/߹!.H\qYPE:! Yy( Z- c{|k8!ޘoQ Vɽޱ*s#@1P>) SQ(Eց` @R)AW9eI !#L$fsQ|xD0ŒBB0p.BUY$?xR#(xbr8 ca xgo9y Gm !8J cԬEb8%MDž9rk 4嫫1Fmaa{%ɼGaj$d8Y 3׮wݽ)iV~1]?Ԫצ/\SBDQ88;{,RkBؚCfYx`$fsg`,n^MahZX0PEC1Qp@JIx> g0F #'*,oNkk'/; ܿ?54n&>GAlohm>%Gц4h4͍~51pu׿ƗW6_8GAG~%?K!J!Fä2q `sx"~иrp4t7fƘ{yg1RꬫբF{ϨV2 #)rQP*k{g]SL)d~n"gRF־ qsƒdo4)w}^!y^~ׯ9sv(!E6== z]i33;=<Xc`)`|9LNN~^*_%vt|W aEt4 (%A2)FGQĹeVJ BxXJUY6i0O0Pb !QR #ab<+)s"pR- & )R$Ď?aOoWWwu7 9Cr;hZ, %A/ =HB.+%Ct._Ux=DfVV$Bf@č;|F*\Q(3ΰXCD( Ad!gLH;ue8l!dbD=9,,] B՞9謭agADWO!|qmpRiRfх닗_|G/vBpZ0= @Ybm-g*es8Ƥil6,eU8Irc c1@(LfEi*~R:( J1A;`eFk!kmMliiiiJHt}>6bh[A,*ƨs(@Z(VvT셩§O3/oO'an@X3i)'>sݎ"ZVܐ  F,ʊIǍ(j7J6hBLzs4Il6BK,1 2'Y M/l|Ks'~$=3ֳOp87{!*7~߸uܹ}eiȘqiyzїssY~ GG}c-PXU @hJ,-wzs7o<^lO'/^9nEꔐB9Bs ,Eg"FQD0SJ+#Wb trtxܼ!eVbtVdď…V3uJQ4v^Z("!‹&%a&(02&^m4[/_v{ǏeY^F[kQI:0 <SB%s[ͫ7۝.!8 ,:}4E'IH[]RuiV,--WEgfu['|?N$ISFYfk_E|#vS]z-QMu6iFb{{a^ &( }l5P4in]ul->h<<* `~i_ſ Rȼ Ri2EdnIa_XvG![׮8>98/\8tuee4TB=+E +(n]yG|z?n6z+Y( aPBRZ Q#4-ZK cJI¹( 1JJܥ ֘^,ƌ~PƘk[dLDI`khk &PF"E !e!Z!A9B@h:Rf9CPk9Q1FT"$&_۷c)!Zk)e+FX\  )ky>R-bXP(ky5rTHdz :ۗ O/o^^]m:_3;C~8gOmб5Թ #U)궮J`h2F90 Ӵp1FS8B &(j6<1Γ$yip}۳4"k-g4 CLRi]1~ptxT)eꕵ$IōhT\*% tZk\yM &8 pttxbe,+A4Ji.{$ < O(RJIE͞`{g#pJ4c~dzth*hqjϒlA#7/ܾ> G +iRtJJm&:D.a mmhQJJ6N k27ni5{^V[kfAV19 ^c!UfF7 !-m "DJ0d:2\j2I۷ܾ)8??r`?L)sJV??Ex͍+;ŃO?A!SBt,EQs{{Vƌ9`R{/k;;ҟ Ņ=Ӈ K{{/K˫Ajc0κvS!sr%Bonl<{!e$rkV5.r6~]r۸.]i啂"#lR WIL^cxmU9ߥ1ܸ޼K3D 9(g1B9Xi#!HfN l]J(! 6BiLv"eAg-Z,) 9!R*f{8 eZjyB 4!$ QIs$BR֯TDXZ)DcLs `1& DP_R"[r3>dzL5s;m .ks=HḾLnZkTxqi fk% 5w@0ƌtVa"(,b R 4*BFs6Ykuロ`0 c(,R*IYkQy_Y:& ,|O*ё5Φ3ʽwnA|dp~ju^ g3(@ ᅱNtt^<|iY͉Rf gVMC%8?dY1prCS[1"X=(7nvbWww/<s _;ypqv5藿_|TG_|O>{7׿tt)Y\Z>}zpt͕)ChBj: R~a^B*Gj%K3@B8 9sXkpY3yE }qyYVT2(tBQdcl:!NjC)#Ս+ia7?M~&i{{;(piiłq2tV:m111Rz~@g`g x^?$ o%(A;JƧ3yeB#E%d5N%dnl|{s.+B.YW=HJ2BژBz:%N[YkR &Xk }X9˲2b1!`:q X1FmowNTei3=q>j:BA+jJRdy "3JQRJVkSD!"XpcTǜ ecd)~ 0 eh}dZK(5 D;-!RtgJDJiX Äalu41cFp:F D!@|ν$91Za K ec}t\e=/JJZ&a9qonn6LF( u{J BT#L )" εP,Iӟ|2_9&{t&FJ$=/10 XZ^{RJWҬ7Jn|#Π@*[)uVab3Ɔ\*n&ɠ[7,ngѰN@i(Uvݺ{N:9_Ws, tHi0"XHyi9{<>X];YyN(-Ore%^f~ CɃux5]UшK+/߼qcGhxhfĵkdcs0-=xnG;旆ov_<{ps!Շ?޽$M ׮,>zS 0x LѸ  FQ*lLJFi_k]+ڝesssUY礔EQjwvv⨱ݗJ%IҕH,AuFD7< k뫳BBp*/Wvvyx<;;=hTxGH6C߃rj >>ǞGnمd 4.2Jy~N jE%XWsu,D(c^JȥOF ^DzZ $|qluS}:k9uy=!MwA2yQ Ą`YB"sT'ʊ)Cʒ0`G Dc;[GH B>cR)e !|L03Q2ZkJ*eA $"ݚ<&E4,DJT*ߍB̌Zr(pBHY}msa~d4pj 5XΒ|rN3na=;O=x++sOp)iō>:#k(oLQ\Y~1 O[Lr<03eN-9]BǣzOht2M$ݽ =0IqVKiYRl?{N#4B8OZ ZS *&4p<qv;?O~tt'x·quxl|ڢ4M BSuRʐbt Θ1p0p4=z9T i1Cd[][(BI[]-@Q`d}ոQi`%0RNf|0~ss;RJm~cg\r)@fiaP7o-?M' cfl+ኒ 9ɲ3!hsaќ_b<<67JY2VL @sBH)UM0B ÊJsJi`~~`eus[Xvj:?OOo5xD~p28ۊDeE%WYodTIqۛßB E ,a9Q_Y, 4_^x<FV/_:?y|u8=z!~ݻ[[zrpKm4ã~/~a6AZp3;7oozƍ[èՌ\S|vg˽yqq7J>ivޕ͍gO{*>(ȲtQRZ[0BJVȲIYyah6" %J)q9PVMhmv5:9Tz((Ҳ;Wdi2κfVk۶RB9JI%2! Y̦͸9d @yAd XYY+7\#lGKnwu} _`yykmcTH97ZkȃXk B@FowڝttVBBH0FuƋſB&s?W4_]/qHs9DDMxɤhe0n?AaAB PqbEX`G=8J*pfa1#:vqL)1FU5<PZl6GE`dR*`( cHwJ)9&ӢBc0йSz~9t|݋/;׼DF4gqC}KbV{ȼrru0Fg4Z݀3?0@ !9KYNθq2aIYUX|!*yttc18Ms 0^\\GeU@pR&E0Ԡd6m:nO?/.--o\_$Ipֆa{!(pΔf0F!Adp42J1J@ :K |(:7y{j1ڢy^DR(1(<}<2`B~~{A߼yA: ׮x"n4%jUUaj-=}2N(ee7v~ )eڀ71ӥl &,7F@*iΥ"y8(%qP)U %1r@3@!Zq%rV( ÞyQNu>g p#g '(HBqicIyaFY Dba)$%!uϫ[cQZa)s( 8ہ;8g9ӝW9uًDZZ蜵"^S+ s_?^:9C7K3 zLi #d!Jygo_C :5Z8g1fa%9}[AB1tic:m;04FOo\u, w{nnhymc@)YsZh{>}w^]Y =6ͪ,?y8Ƶ+_ou:h/~4YgƢVWV˲Z #Çbmiyeow~RED$U YY^ <{ާ][[~W1%͈jYJk<V0'1,~%N2^vi0eE ;se׮ݿw޽QEi5ak鳪*zv~wd}}co掠ՌvB;8rZ3FJ0BVsum-ORB ,VsJQ ΋4K8Rzsi22D)JUet8γtɓGi쇜ZfHf dZVBJqťGQXv:rrR`q,ٽ׮~xL @WBs'G'w}xW%" "{,M_' <ܑM}ᛮp}_z7WK%X mDcDZR)%9)FFI)1Ii,@̶be2}Ƙr2aJEiVWU~](VcR BSRC0BƺWvPoaΒN4ђ`|8<::JV 5`9+1Ƨ*pso=1Qa1%qu`0Bje''G'G; f5T*ajx8QI9yDc!䕫W p> yI)dl6~+)dՅ`__$-,3_:GI8[#5=/&/[mB(?k_Ͽyp~a@˝VEO;|y487OCE%|WJh ێBqgn޹}x? / V"]ZZVElAnܼ{tmJss?㿻'|[{qV+ ցFܚ_<9>Zc}6*Ή0Ry`\!psLv_z94b{cg'Z v^9"!zovM/ݥ58ƈ3& !Qrc@/jy sJ@0cc p`dJUB&+ZPY YYH8y^X.6g,A1hU%(\YY,*)%%,OI8|# uH)!,'ǣK/rpwugӳ'OFQ4_d`R q{dj{woyi\CP'I\ί Zqa3 Skjq|co_:]҄`띷dW]ݤpܼ@x597m3 ԡC4!/1XSVbY.3UEY@]]WyQ둶J@P@Bз~=) .@4qƵmS½ƚ,r-b&N88Oa(YcFkJYkZk8s(%j)SeXsάCtnJseO? 4ֶ׳szZy^sC6^`Uz\ᇤN8t@1c [8 ڡ-x,\RJ6BC!VZ͵lpi 1u<u!!M]Aw8wf>v=D0Ѻ8qQ7r<^.gA$uXN9yYu'[C)I~ Vl:)zuΆA 1)qsQJkC(eUCHI(5ZmNwssk2 (:O(/ 0I[ڵMcSBpsu1θ@lsk}o&07׆݇?ݻs(ݴw,mnm޿okg'I)Ⓡ)o}[i_/RZ qz_zo' r Ajtګ=Llr{m4~ob1߻u>9<9[_Oѳ}gO>sg?Nǃ wU20eg//_P|vt-J]%'e!:Jz4N νغn0^. 7M]AȋzEp@[ 2˼m %L)wKM]R;юP륻rhfƨl:Zj:P&$gK/p4Mץd%eb6ol GIZd( b<_^^L'ͭ{ol8k$}c lEQ7{~URk5% R+h㜽N%f:BjYb5+sWWMk#S˥Zuzn8s^_f$_5/ vsBV;FRFH)ZyY1D.}ıp!19 2sW)e ԱJ7x {m Ppn)Fq=&!D+] a,*c鬊VS2VK)VP"+MߠK)k9W Xk7CWYW77F3WFjkA #8cUUjHos<$zO=VAkF:IBB"/ aw>{#0s P!rK޺Ç4>9>Ъ }ѻ@X&X 1;UY\N.|ak1U8IJcH8#nY.shP),ZՊrfF#:! 0(mIoR 낦nsf|$nyYԵ8vJWSCj(Zq$8p`$1 Μs]L0ٻoZ3M&a]Wyγ%p߿{'#K<_+;k|ɢ?t|yq.!m8ѝ>ә3* #9:>tea2)2{l.Sku&Inuޝ>ᣇQݔ/{W_8=1(=:<1ܻe;<:J><++8X_ۛ.qA`YfyaQ!?yRNI@R!EQJ ĝ;w? (Nz'v8hUm=֗{|UMMxՋS}(k~6X甉*=X`O>2&IlJuӍ6a%ăJ֖rg~;߳F>}[@kkeYmFMd֛f-𪮼i'Tͦ?ML?J;q"sIaAHQ7uC>D'M8gM$'M!Bѡs9A$c֘4ak!X#QJ%Z+1(LjmuSk)":V"J, @e J#ƼGqVׁ!P΢0 PZ3k5 8E|}WAi> ^ lԱn0W4+MKrEɸ{zRzUeI\ʦьA[Dʘh{aaRU3Zk WZXd1Yz IDATx籠$g|/_!wEO(ȳq~֭bY;qkk*wtt(e\8IYL,-Oh^4(ΈUU/ :I\7 /v<]_JF{<{DHE겪H.Dp) ˋ%g1;EMg((>ߗR? >|AUE%Iʳ\)g4"z#@ )PJ!V&N(Nvޭ~t<=ѳgUOOvwӣ~; M}>UQu]ik"!gӓRЪiŲۨr+Ε~q)wwoLg|)b|ॽV/ڠWWM0UlVXU ֆq`룍{y׍O{ώ2HxCsyQ޽wO{.OY (y6Uu`0(:Oʼ@˲Y6&<JBۙNN i, G=k]4uFoY7P71FHL@ֶkdT-eLSǑ6& @ʺQ?~4wwo'?~}?;?q>[l4Ju=cL]VnZ;<=;zBQQ o_KG4ZAC!(^ Ey7u~c ۨF96@C)w^-͔Pu6-HⰩyݸ Z$R:CƊR$*!{mSrB8PFAJaЧI͛Zl'C)řO_Uq:vWٔRƅu"D@9O<"Q̋fROo^M#o F vWŀam-t *:n U[(޷2σw( [km_9P!bV&8N"CG BJRRBJy[XaX;;񢪔RQJ) @Iw, qnwm}6򅵪]T]\L~7ә* eEggeZBD*eF+pkkI=b.gyOyERzPBsķh+m)PBc1 B h窪/?~֩-J촮k~6L&^;{>0\QV{h\S֦q0јtJiq,ݝ,gGk4`vyIǟ<̦hm+BՕ=?s @ L(2ON$c8!#ۉ;hm GkEeRPhŝwB 6I0ܞ'!cl6Nԙe4u! elww?ywo~2iҪ c`ܣ8I|?n/}VQ )Àed{4@"@BpBkl̊$^,:D[嬀p!8\Ϋ0h ey'^{_݇,RYQ [M"BjshmNNNOf䤬*Νgdzل ӓi:iNnIcYYUC 4_/GwlYF'M&~Өb$ 0d'EQ'VNr;x1'r1~ xAAnr!/f(!eYS![nGcd  Z(rR2CXu:~O&$LE܍W2 u5!>Ps! $h\Ĝs icsN/("Ơ1= J@ks!' aRV?w+S1mŪǫ%ēwuu=n9q8ЂL[F['pȀsԵ #Yk8\JkWn!xy^MpZkOH EU+csZ-^9[䧧Ĺ8 8ι @(QuӳѰsν>h2͗R ljB2ZEDNXk/Ǔj$mm+E5UƝ_zsʀ*mSO3NI* e6 ##|;I'~x6EQH/ |gK " !'~0PcgUY(ŅRfˬZ,q)Brmgls1Ou?e^)E>ĎY C)PJ{NN&<{#!3G$yQVZꩧU]e= n!p>g3@͍ZaV|V5i6M6<8M;jw-s)c:@ż&g'}bnpq~BIHI)uXNu?vx"oՖYj}ck{U v67&˪Mj E @<IdӻtSno6<;8wdQȫX.$8\e"Rp,/?88xFq-۳:t.8ICY,$I8ըo|w6?_^VJk5?؏;Z0 VXHE(DM+8jQi^ 0ն*Sk.ܝz$WT˾,SAnRn$ތ7/tŃ0 :uXW1B() ZkB!ƶpg,J(BBJ@r)B!D%QRJI))($IPx$":g^G)}BJZ4 q"9 0l; Er΅X y p eU'FҲ#V+S39oտnWj4/,־7pY [vFQx!8cpeB}  ] =m U0ƅģ2Rʫ=+08t2sj9 p8aXmmm[]eH c\0 eUWUU11R*c|wvr8Q᣷o޻jZywQVqONQ5Zc9q,PE^.n(!qztkd:\p.*lnnuSSJw~IVPK]!Rl?rк֟|.[۽J]8\.8ʋ,[r?p!N)m(@]՞[DvϞɾJxDଭYZc-cl:KEH.Y6Կ¤ۻs982:˖[{yI))$d?O)oyY̗InӨeQzЌw_zwvǪ 'W:Z_'g'Y}ɃƓ "Tmo.2_ 8llln?}o[oln*Jx99>6UUoͧ"/;y` ݐsl\8kz`0ul1+RH!ZKSuX7Mtyޯ.<_twrQcte=`{KqotH['bDյxk{2~tx%FޞGk%ħInWq=m :@():{ (ʌ1yqfkk*k);w(hTSk)gs,r[&}hu2-  x8炇A OI+WƝ"2Ƥ_Aow8 XK,bZZQJ){@D .BH_|*`(8NJf 0 7U]j)B[R-%$ B59 ahJoQ\E͕'D mhuJWJY;OPpj8LU18,BΘsH)E1Jh] adiG~ 88k8%Z㺽. kzW4 `g766vv7?CJI; bs,  ]fDs(a1ƢGG8+w۷o}hw1֚(Gkm C c hV 1hC*6Vic \Q-ډPƴ6 X.˱ӁUYDQskdwQ˖VSuO?7DQDVU^]&z)̣7iiǕ>ָ6WE)0.%aLߋ( ʲ 9j8u$Z݀[Xk1c0ύڃq< X'q0Zoz_ټ~G|0N?QHh"HN'%R\8sNHyk1ئQUUik)p l{kN]7Mlxp-[/skt+ {R!-o佧X^OxB8U]tDCRA J7ϣ>wP'O?WMSe(Nnjd\\rh "b7^8s&ko|ӏ?l-sޭO£ltzbRBke1nYQaPA,h R)}N".c,ϲ|J˪:9_R҉SgZQJ`aus~:;r9ow޻( Ǘٳ} ?lGUTw٤.ll.2n8 d:fBӳ۷fNQq`l0gY^׍e9{I 9{6 d&noGlՂ؊^v)ioՀP>ȍ1X5u)\)W55y.U/!36N4ꪖ:ymjm*I]77ꪘ\HȨ3{ !!ZeQy@=X8I?lDX\&(Xk)edAhyȊͷ҅8*4̊wq-߁f׈k`&ueLŨ z_j˰be30X5n:|%-jMK-5^Zc`B^` Z^ !e 1w. hS{'%XK@JrN9'J5Ρc(ͭ㣪c!-SRZguBʲ*!QƣG?y2˲JDυhB8 ÆR!BhmiDF )]:l͟i;X_X`sH2) @Pښ![g4FqP@ d "RƘ8?ﷶvn1: ( $i{֚@Jt*՞vyeygeʲ,(,'G`4vcYgu]-pzJp^l>9g1Ra޽qҩz4X眵( (%F(NO\HJd<omllѫFuww(wp~yi1' iuU'7?MX/ˋӵݪyWA;M]ƚD[d"7msw J]Y}A!PUr'<(Պh+JRRh0 U Ƅh=8_.NqWZiOƗQt:]ySͭO> FN8I0Ti!"hcu=} %<"2"sNN^-A i(is sH$qx[~z>%+쳇JiU]oE3qF!z)lhm9@FQD͖ﭭ۱:k˜#0hжd й2/d 7eF˳ZqU{.NVB$Ip~EMY$;>|rq[[;oyѽwY̝[r|>UM=ӯ&ق#gw w.OF)^h?w\0"{IԈg?ڶI'e٬mٳ7}tHeXʢq爯)w2tǟ1.ƀGnm .|>=Xˇ>z"Dpyy~ˋz:?@$amlnZ =(+pV,8/"N,/1 t8jwX֕e%Bܻ/ ofbWoʏW>J{CX쿒o%D6Ӊk ¨)Ԇ(`'&FW [4F2JEAu(@iRhuacsK&ǺB!'sc@(#xhT5HƴF; Cri'DkB[ (GGO^`njH+\N?w_>>6zBxBSFuVśzZWp g!!QyvR~(CQ`4-(d =㌩FI\ k*N(80N4MZ֡ p׆C" %- %Pg(B0FPDa>,[dZw{o杻8SBpB&ϲ,{!"eνi⫪&8Q!"v]Xc֊Rb̀zOs 뫪2Z;0_!zr𔮭osdLyҫGG4899:::` B>l8@9<|FtbsetÓi(<>: iW\Ҁ"1A(M9J)惘+ocHvo}89zu$1qO qySs^ &S }[[ӧc1zU'H{{W:]FYVLVE߷)r9z w!v[oӵmlq޽}d2ھ6ݍ7yr2ݿ( De/UO&٥x~t맧|5bt2::=:::><>A9s@Cl: Œ<$ˮk &\dƚ|=ǔfJ%ѻ<c?QԴ=\.fө"ژbF|ι~>1^zg,+y'[ۇG;oOU^L'W5${s2YQ!i897Nڦ10ƪB(6 2g0.W8qRiqL֖~vc@sF +Ro>0Fh%M)5&3JCY.)c,J`pMl:sUJTc1y)aj@YBJnRQF1>BH$>3jGU8K(ڜ5Mk덧dJѲ hLJ @6 B ic2Ĵ12:͟nF)I (tHR'YF ߋ]B BcDń+O\0Ni)"% #bT 8:J3ɭNJYe7߼]{BpbLӆcP3.1)%2)%SOfy^7Bɬ6:SS]! pL!ƈ$R-V, JiA3S(]Bףx;cMdvЍk׭Izd͵7^lC__p1"D(+5 /ہt1c͓ϯpN1'RNK%3C!C.PB0)I0dJ8$h=L >|Hh8>"2btEW!URqAA*1A^VZ>k)ȹz H89Y`ĪQF 2.X $]U&x)Bd]t !Ɣbl.8g:Bٔ a]< Z{RJ\cLlCg9Y63nzty/j|"!Dk8yɊ B@!E.01XBs"yI) U #_-WR B(Q}w=bs:3FB۶E)ֻF,%(ʫJ6l9"SBӜ 9黲,FQRkcEYB+''{cwwwǣї,Tӄeyl>_4qJGe`3& ' .\UecDr)2 @t2'J΃Cjդ D;ۅR!NzuoaFѸYYM~[[&%ן@y ~y K!ew{wGB\e7u>հZk{m0M  s1ᅘֱ*l EPHJ{l:vD ym{%6d|Ҷ}QW\MFKm>o՟ɟ,,;:]Ι&ϴqw~=q^_?JybzWu]>mUcwJpRVSgTeR:!F@6ʬ(^# !\ hmZql{{6۬횄qz:;w~a] 0jg^Y8_6AS#FO>nݎW!T{<_{^b(JmtQFV!YBHPSFT#9C!8>:_ {wɳF?XIo}ӧBW1ƋZǟ>~``rg\͢o @0AV__<{x7*|8ۻ~xt]Mv>DDUdj\BhOOwvf[cWc ub|֯;[3aZ^#C)9Xk I{Q۔ޚ>:8⋻LƂj5> %cm5Ȳ|Zb<7kc/6߻!Fw}?ʶ*ѹ'"яcYYV;Jf]1z<͗ FHy|rZ&l%]Ww۞_¿terkc#k4-t-pqUP>A"onNbԧtX7Eg2SPP|@!$)`oBpM[*0*2 X1.m0AL@؜lZ)"qYƲL0M2Bdc%%@)'Py0A9.fMY@/ !DLйþqgy gyi3@k{T۵3?JJ$@uB B5R@$yqMd b&#8>>FdY^sJq$WJ*9ȦSJkjL!ZkѨ{B(:B()Pfy5:! ,"!8c1B餒8&[|Q?|g{{dvPjDB %%TBy 'FUC.T&* [SJP %7ŔN7m 9(%΁,L٤N׭81Qr! g]VŬr:s!ƸX,q:yf[.R_}aJڮM;7zEY:(%n!`$KXцsN)+L)94{{sϲL)irżSRZͣ#՝ŋB^Z5B]j:<=>GP1!x?)k º{WVU<W+ty{_'5w <5>^ ,Ρ1%L)SFc5%4(Vuk~XeEL{BXKJe2%"BA 6r@ C Xky.ӆP*32<^. ӮY. B !vp Rpcږ`"ySʔAe(3K<+ 3sڅd |Jp.0AjEx0onЬr@ot:],We>MR(ABm,!xc ǐ- B eTbkk=b8eb-K ^J|Lr0l4sʚzRҞ̅1*)t}hTkں,'e9.ZX"9ژodX IDAT& c!$u r!@NH%8 ߣe])FA(22BRȪmlk燰 ( ﵉1eJ,Q CJJxZ҇2_vWB{mI翥[Ƹp8% 5mcRd4|yGz\zm!Ҫ')A)j$B⌌G*4 1 o:' {ʚaA]mJpB B L)9c3)dARbP(\]!!1]>({NZjҸvVw栘z>/??>9޼??o7oݺͅEI1ڄB };k>~d6VCB1BY'x2ݻRBp,J*ʘѦzcu9B`C11vywo2BV[OOO3% !xҚis{U0uM 1E !'(EԴtY._ލ &׮V!!W`vmwۧEL7ۻA`]`gf;P2W+&JniXsv㵯~5_dʲ,=glmn\[/]{7Z{Z1xSZԫ@rNr4 )e (mYbcwև`kݰ>Ŕ"):R(bHsZ4Mߵι]=x@[sݺYm6,֭oܼ{J槧֚66xFN;t2޿v/M D&qpp8Fx9Lf 5ZJbϞ> @ lƓMݳgϷ6(Ȋc,roQ/0^x00ۦo{_<gs{o%bʶY}Hpg?{!#x# %2uxp1Nba<#%(gdk<K& FHgmompuϸB_A* sXc!SJkP˰ Ͱ?ߢ1ɴ~8pw WzI)齋!6(nsh-H B1ZUJVsxrZYW( L*Sڶ?\P9g2ZdqO 1.8 IRD]#BPq[{`Bܦdu3,PN)J1_JZXzəsafq`R 8gkZ' S qΤTZwYc)c\3̐) B Lԭ/A"ˡJ!sXJe<)0ct0J@9#a(dG"@j˜lnlǣkv7lלZ )Zp0f7]K!(r5Fb6_,u1De Bh^dBHHi2,}CҦB}E1rbib]L7VXdyvFtB)2D5}ϋȕT'v<]!mu}1($1U5 juƧ Ίяo޼:_GbV΂d _ٻܶm{%i&XdiJk[LaP"Y>0 C<$rPF$6k!ԌLQ UQ}CI${m Lj#I)sBeHmVmN$!S-}HOW#`6cF$ b)vew#{WO?P&qV~?qֻ~gk{kZK1򣏂b/^==9>vNۦnVMo1TU11&@)!RJu-9;8  `9f`9&1 <^'Ã"?*%xdZT/MZl6+QYdY>4>|pxx8NUU-ϟzw.Lnx}ʬr泧nkm98.˲B(|(21ҳ%Dk87/ǯlV7uޯ_YCW/1c7Syлz \ կ !o,! Ş+'{`A8om$)%2ΈP'}e(JC$)"0  H5S1Bw [gY0f0&!<%;) Uq#"3!4m;aM&cΉ\Rqz{\u3 p |N4ByvZ˶:C1O.K$B0&Oe( Z&bbt-B#bBBH1ЀFPB炳 F &sCC@|Lb %(B(GOO}a6fx4L"O?y0L!w ~:Z'c-*˜1B Q 'Em(!1bTcMA4 U5[,އ^p.(AކWM!dQ8`(FEYv]xx1p¬RaMCBȲ$hQ9(^֍5qF C7x2޽r_}lQQ z )x( bާ)>g:><9 4mll[w|s2Ve?L[c~gZ*olhլ[O?L BQ:p4 a{O4ƄS %1B(5Z cJɲB ?ǟXw]9=9tyw2xv 1c[:ۏr812U=}$`j,ͳyg\~\O7X?㓟oͭ;|чJ JIuO>%b:eY(wݸ\._uV>|u{Ji].^uS*"R)W .cZ  .W']0FbeL(F.ƘFURTR캾;fwg_9>xzbRyFg{,5kBtMB}RTH׀*}1hz?yHJA[A E'Ja U20R*.8g)e_?6EYxXKRY?S\c > FĘVwL >s@up yeHL(>x|n)%ch4U8? L0RBCh౭=`y+g, }pa+%0<#e1`̆@߷Y%l2*s91j~\.ֹC06Y5B(ʋU??B!wy:ui 6ZŒ,86eW'~'J%֬,붥T u)EF^p6lDg%Zk鵞LM]UYqeoc!a:k1ڢL2L7sȔ`ujml^[m X&Cw!u!$ͤRR3@D BbӔRk@)΅Ȋs.4Ƭ@} ޽/?ß'?OgϟBloڟ_;y몲89><::Zյs[hSRFbgb!D)K(!Jpt]Sh+F S*U a)]S]>y <ƛ+T'}EYeLX缏1YkuMbda}׶M({w櫯~Z~{ccrxh\x_Տ_<=F!&BlscwFe/?hO?0UXcc/?L1}/nݺU*;Zc3,SmۭV~Hv}^+B,zCF㓣m-ݽf彗JnomF"fh^ _+.ݭk2ș+kWXi{g{o{o6xEz16! J8Rt$DOh1Nm6Ec0%(~͈4fd9QF_׍`1#N0t2)<`uqCCM99c.P"R ZqT )cbq.9AdJ@)109]xAgӹ(|-MqZ"\ot'|p",cLA Ā1ȢRƸt>ێ1V٨%dUm;fɁg Fa!,NSFFe,Vֺlsc6Z{bX,Bv|UUz ·Ns.Fv͍Yuu!FchRX Ƅ$\A\JHg6(E j_#͋hD >|Y1S صkXBU&9cmJ'Eft&e^-"wRBo/韆mE5 $!xcJɠ\ )}b)9JDgVeU!L,V# B" |aHfM!A <4S`dC!( <*J686ryJPV_η{;ޙӋsk)xcsѣGU5;[''ǀ6,x Φ*P7mvð\I0rAC4Z.3!x 2lc}g|1Q~,4vs]d2IbuUWut$H/$$@[d_0H$j̨ T!Y623썸p"$|Ɉ7Ϲ7^{}7@X6]cibZbLʄ1 $#ĝ"FlZ u{{S#Y]/N񛏫>"_?xowNg;{7wJȒ$I)/7Ði"Goxgf67R5Uwչr2K,o޺u IDAT'rTͮh$8[WU9flٹe h(3wt0o!B -Xgrp (xD :=?T$ӯ~_ka<M&כF󝝃G}OOE(];'Yx`>wjsI>9O7`:]tmjl{kOISUdw,ٲBbW^ytcS6i'a4$yrsa8W@䇟SH >shS}"wJȳ4KDHhg|'IHݙz!8"磃 uBfQ&ֺ!_* 4!s^> zS]u>pvZwS@\cB a{X(ʲ9#9c=#>+ C@\+|%}J _H}t8T|xbEC 'bĜ8D!^F# AB1")TICQs1^%fMYQy>7LWͦ9 5rXԌ6zSVaJMԛup1[x8;?;ORUeDyK!|ljTR7@"k8DS7%qcmV$DZ*%(8;bm%R o kJ5&ܘ*LIE1L1j63^>0FR)3J(p `eDuߵND #h1N(8xK!QQdq!Mws)E$eё)"I&J2z7|1TJh#FmkћmZosmxx w(m'gN:O$]> Rr\@,Mx%:g-#zƂօbrvV^kcc J>8BL1hqښ:;NDmst9 M-D8"\f={Ai dC=NWjNA@L~@8cWYtq8"E1BBN)1qhz Ѥ,IymhTeP*1_dLIk7uaޫTrpVsr.rR >BP*V[cZknovP a-F9^"wKw|uHݛg {q~Zm |ӓ3C^Iiu9:2D Z4MHHb E$8L>l?aYx\ܽ{?9_/KBfQ%BYF9J! pB0aHHm[yo #C hR#& Qly/A{s"2ZrȈ1kcnںn8D!BQe)gLH~Rm:XXm:>rL=Hb8lW*ݟ~kg9?=},=Y#gmmt/w+Rf;*80S oᙺKO-W$3\]GũG„kt1"im4G4F#0Yc]a2)B,%PB@D6Z*B95w9elN\(FE^uS7IK5Z,xgRcB}T>\P/-c  $d#>lȈ \cq 51&~#ι? `\"!ĈDK2spA%^k) j"c1!s~>oVIIYy zUU9iΙ^/TBa@Bhlwzr:G\ Rr<>9؎Tkn̘鶀{e#ۣE{k,& 8ޚi'q,˜֙,MrfN1ĩ{xY\g 5Θ|3UE14IJ)cQBgl&f4>:=Gp#8@PuEJIѹ>g%ŨxWyFMozެWgg X]m?yK_޷m:m"Kc}[w1`i9*l{1(EZEN\HZ9RUs޵]1JC(9\|*Ƌ)DNl(1OƺɁ N'"OTضH@(Gsi#D"5@kBsbi%|!l}PIz޽ݽlrֽo|[K/htXכ>`8*Ĩ~gg Ώ9>z0BdZl0ۻ|\ >'LVJr\ g!SֺZY6LuAt;{\{ދ]:.'GG/7wY1~yuE7h] y߾mk''u闍zYmunto~_Ãӓ%+VM%h$;?;yǟ ʾ!bTW:˳z[w]_mN)y89#%1vنTXF1\afG\2Xzӵ[""8T !p2y GbL\H?b]9gb?tQ:OH!pdbic4ߝ+%g˭5ݬl̐ 2&)ն\,.uݵ1 ;g12AŐe *!9i ϳ,pkcQ."e7D}:"$?sFĴ/ANL!TOF L˴Qq /(tb19?,y# ӗ1!# zN90Ѷfp62Fum]m7hoRiC0tDB;^i*!ιN{ۿ)4 :kvgTvmgY^>y"dyAt> ?>9~vw:ENQgMͲ4M!|\\W%g.$zka!ueuۮ{O>~9#BT:pqLF"!9mm4)ZoQ?wョ-BW_{[-|G?|8*'q w~ig}"xmkM0G!@J:b|b 7M6UnJeyvڍ/ك_BگhTFţO?9>>t2vꪚN׮Y9rYZ JLJQ]-1*MoܸQ#"fE_~lq^xt2}9;g^^օow?뭫5|,ծON6/~޻ݿnmvm0уwuD!ʳ͛7BeGx:2$|)D>+M_vIZ9R?S?l14Zc*O<}$0L_L} ȉ`B HyYfI#tZ;Ƈ2II23ưgiRٺ}Ǔd<4lh$!b!0m(rDҺ8grY۶KBiikIRs>f֊q@A(FƘs1 sU\ޥsdjR Ȉ^JWb\+ih5`bī/!Db{ !(`C뻚n7Hi81,x(5v.(dyQ{lJ0bX#Nm;-Ksn]<99&,&bJ,ץhxD鈐gYqX٨պ, lQ{;JJH$1H`Ɔi&ͪ}pk]ضq,T|+wd:n"xBR8E1LߎQJ}.Xo,8cp>8mˇI5#mqjj>8,_~+Zݶ]!(ݙ{|?Ǐd[&MyGJ1DO,FLb4}O$ITA>>z!Q vGYA k-OSl|!"c(mU'JieivuS !T"پκVTJiN.r<ھJ$J]}ƚy:/w9G]n:'|%|77o3qm9w!t뭤s}[Nfy`67%URUTq.NOOU$o[o'{;99OG{ϒ$y8{WNO꺵fAJɦi8uk$yb[?{$~闾\_y嵗ҏ^ ./}{7DYq!i9mRqqvvnPՃ,G3ԛ+czj>|x!3,f/Ogzˁ$TFO@l2πx>wY$!SY q`p.l^2)v6+Fr<¶붵 yl:>=,v"mZk}",S'hcqsn!xBe\^#D$K EŧD2O╻xvx*}"WO1Å\f[GPdx C)$ 28#$ӷm_#`Y$i46=8Nuۯf\#֛:nwGEۓHxu61Vk-DZmMg,3 B^) ƅDS;D&DFc}Q2xM#1g l|wcfà7~OߛL&w'Izv͹b,9Zccgtm,2VH!um:dLJ2B1X#VѨc$Bm[DbPcaMrɸJz!sv]I "ĺi"#"}_7ۮwB`/Z)w~hm8cZIZk\J\@Yo ޹A '鴼}sx[{{4aӝ*kWォd/Vuu 1Z.Z%E@Tm ㌐ n_O{?,Gf^_=~{ODs)YhzD>4)b[U2ɋt3}ٟ?Y~W^}-_><^߽ܧ|r Ѵu-lmvFS~&HYƸDBg_zOs)ew Lt%4}_*5^ .ǤC ~ve0¿ 819#qNĜsMۭۮwn]qsDxt|j.rڶ[䜝guo !16M P&q=!ERUc0pƬ75M"8kQ&(o6`Dȶ]f y_X )]F1f.4| ׹P|:Wuu./x8@`"ㄌQ{BR! H.˜zIRgLq}7JRJO߸&$1ƶX˹$Dk& h,Z9QGcbYн!M32+6o,KAmێDڵ @}CTYRM4]kBZ_r[Iέ3`i[čcnrTD]SD,K/xgu hi;B11|`LAw IDATuȓݝ{ۮZ^BG1ck׮"wˢoݺۿ[1G?ÇU]u(İ$M"0_!$缔)(2)̳v1F@`#2g}4ˊsu$ J0jƣBIIn6[>N^w}#u Xv8+"@ιd vrPJ"f[=aȅG*˒ZpΈ" c˜qƃ_B ?Ӻ#dr&Ni]oѬ[7o'i6O$nHā$HUR^dE3ƪi##BRNsw?o¸$bޙeG{{8E*WUF*Ej:?|C%"%<9=[gއzz%{DGMU7QdDD ̄Vb)b܆ b]')^/"IFH"r)c;m7OTD%c6ֻQs !sN gqv.CTE~Y}..WsnQ1}S~R5 U/ pFDC`\.@"uE̋B&"IS,^f!ٺ $q9N-V΅6iu:T*9n{%HuuKQ2(bH)Rp1X󈠍#QY1*GdD%yM'y> JH8غb[xdY2yc(}G<-ì(`@ o]<_ՈPb!.@T!ƷYI}ömNW4~:?z9Ãg{J(ޜJ1䓏';{/[jld c3\Esm6Uֳx4ʳF"gD!cmp`{cn\ӲNcBy!ȳ4<NONmSwm휻~QE˳w,UMۭVzӵqZS|Z4ֹ.QJWOJdAPvi帘Mƣ"c3${sTkw͛w;=>ƄX]7RrnTw1)4qRo~lw~BlF9O|qcI0nTɴi7o\Iw﹓4Id<Ͳ,R@H1L8;?~4 ^!lLdb=)HI"}(Yg!Vyw6%bW/$IM3nUm>ȳ,ú|DB !8B$MM>FHH R5! 1Q,ͺ ' UX+:RJ2k9 >٣Eݶr^k;X8JkDFȈ[=Y(I FLBC x1aR!XQL!m2"5b!#.c /qMvF$" b"3nXht]6 H@׾ќH֟M,O[iqvr(g!fIiʲnszVM[MEV(Eu]WN%Y[;j@$Iid zBb)s`\j3kCߙ6x@Yj9su}w'Ku[y[vm2cJ:W׮_?|r^"D/6:>D`4`-;cNjӴzX>JDUHAF[M h\~v||g\JJ!ۺz~-:˄;$flq ΧS^mOmҋKTJw}g\FiN1"='^C<Gn )<u]#(ΊQnm<GȔtuӤIkӶ]1x wѨm["&PJumkC+1hcRCV_vɤ,8Ϭ?ol1Yf&J"ɧ,Kb+?ws>*kHzoovkmE QSA u@4: qrrL)Z_}Y>vuv:D!n,[7#`4YQ_y8b~[UD(cR>y36lg7n\Y/W;n>xL\?|sr]ݽ /ZЇho}''yV Ķmw9!d9gm$e ~J=h<rޭV$M6\惘sΘ(:7 {{tzee;W͢ޖq>*|/D7__yj4MR*`C}`MkDxXw!@6md1ӵ1[cнy*BiZmWŌm׷]C4{}"eX)ezÕO'1MjIēM0B[\8dƘ8[V-,KTq[o1:IRr;!&*ew}/8iݶ:~3!^ !&ӒζU@L{\ŬI Aݮi mtthoyaCGi\'bq:ﯗK)̶uѻ;IY~чJ_o۟|~׬zNj#W˷ckVwG/iodux;ͱ*k vR8q8Ƀ–[n  0Ԕϸ'+Q@4eYNܙyz)=Ԅ]\,\0)W|1P Oٓ',eNL (ƌR@!^O׺͞ !|׍@։բm̠I)^&w !ˆ ]  nwq18fnUx*!,[BFB(JɱCmL)zL1xoaX{1$8g@"/r2-r1xQs1R궥2NdYu"ZQ$ 0&UnJQJ18?pt19+ ୲]ƛp4'59z4Xc"­g鿮3Ú162@ahZr7fEn޶m1٘(E^ cMgͧx1 "ZkۦE|Rֻbiȁ2'ηNm'繪&l6t]wMuJh_^''wc 1,ˬs}~q1`Dn!Z!Jkc r<+g]7SN:.V M\tƎclJq.8BއR뇶m IsSZfVY&%Ru>|DlƥbLN3!Dzu~y];ks6Obŝ{g/:;8#(Ҳ<{EYwNN(BHȢ !Y^_6lvXao$[W}oc FBu0)B08}qD˲L:^]P!DUZJ1S*!:Rʼ,Tޅ8c  \,HXWoZnxg;٧'WOW*wvGW$c{fM].לh=)I@IJH)5@(ID9ӻ4;gyA1 _ΌQ\| w ߻B7NѡMu| :M mk{6>w7FPnL+!gcL !j)"JJ-J)NA€"/U&H+!4SJTqF(q#/!z)!>ƈro@z;n(!zoʜ!F-$ əP1}~ԘPJcWuJc0hk[ 1*πCcRAEc! ^)!w#)Jc7Ąz6Rխw,lRzI c\r: nHN}Lh9k:'@$2b*bZ?}qaC#pNc(e4F/*Y$R`|L \ǘ{ sfdnzo>J*[GJCkQJS%릏11ч4`.~ч_cRVJ# @ !(PzkJ1qx xDR:>v\^K!si-c4\d2%\xxz޽2U0R6fjɵ><< ݵ7zNg]k>}W/޵Mb8KդBϧyiʦI„ *TUZ+hCBJrI:slDH)#BYGV0]O J^YTΪIlߵr5mG2B !S!zC[1q;s!gry_*3΀ݜsF׆,iv ͊JI~uyc\.2+4Ũ$()| ߄]%V<bϟ?-"噦SιCy?VL.//R)g\p>V,F{=SΚg!$$8d0??gw?O>Ol|~}[,?']g=L)ͦ{ggp|t޽{xx|P8:> 8b~}u=?g}I[Yߺߟ5ceެQB,o+eƄ#mC6_2˅̕HOs.i*vt2<*/,יRp"g,$b”UqӢ0 d$ ר#ԇ;XqY)Eiga !>Q8)&E)) لRK!Ԩg` 62GQc#k:a 8=( 7q7QTo7حq c0v$zD`l}#f#)!g|m+?XL]_mI2t>(de%|֛avCR,R 4H ;BդO'UQTUmC,J TQb+tv}}Y䬒"BHA1UUDŽ-㜫SRg_M~:w:HypH &DcbCHC7k9At !!0NP""PF:%#}`Bp8NCNgs`PJh$7;ʅd Ʒݶk&PL!! KXwMUn` Z)X.Wc.'YDILRHB>XCs΄8W38DDgcDSVeۙIBuzݮƄwNdZRw=z?`Lehkr;~䳋Kh.w/_fyQM$Ep ٧ýٓOw}˕W'-'t׀Mm8C@H$ wna0ц I)䇄d q7R`1F6` o=c pTۤ!*nNc  nqRFGD(&\C?P%jJɋ2r2'T0 JCäfRָbiJRpmDt4SMnx9RUWrp@`M?lۘl%*|{IYNI%9ju|4%cf qRc@ɩ1] cs;<|S:cQR8gP~^]7Ck*H0k%$<@N2)'idʦL:xeY/_<_ZyJX;:I0}D% 8]5u3]N@T clN%r}ﶻû'IRs1wTu~ϖחR;y-MF]5fEɈL&Bu@ "2ƺl7,ϴz$3.ʲ:9><8::*{Gy՗/ ̵4kY7& [ SB eV_]#&(wO_Ę w~`B̦k;cՅrp6۬10MkUY}6$ B('$2N <{{Vjϧ?|tVf䋢͐LJzxs1N"JQ 9H|oGf"-ƞPG!$25M3|?\\\BLEݺ^nJohBcxkӽfH5HO _8 "I!_B( !$yMYt>MS0)E#4&Rt!LI%ڎF,+"B ]ge@wqJi >G:xe, &ι3N("Q/@q.H>\Q`#decl$0`c3. q 0ƁR9l4sεңǘ`'e ƴ]b4f0,~i2z9 Uњ ʪ$FܸѵesZCyf85$Q"1(EV;`HSŖi^TBri6c$N*]RR 2=aHiDEBI%kޡ`*&RJ ,jڴ̀JSI){g//^@] =:ZH%v[k`-%%) nx>b~pu~].X1#S!` GjQj(N'*1t(i5fݦnm8$f)P:栎C+yrrRMfZ wUUJu}7?x7?7wN?%VR&8cwN׫k!/I)"4y}.F#U BX ^]n2\ yV 9TQpRWZW_~cv; _|4FY1)z;˗v7t|q4&@u]?dZQ`<{r8RJSJ3&|0%)]nv[ V1Fƙ,ژ,"ajۮJi?sf\^_f`eD|^^]<|-yߵ5wbs-$7q1٤K~>12"H^ ƆrDŽ`ö׫*\SB۾7֕R1u25J!WW82C Fޤ<ϫg_Ng*ʤf]-%ﺞĄ-{o&rTq)ʃ޽wΓB 1/yl_|DiGL+CkXLg~G>bRJx(}1vY8 ÷>ݷ.}tȆq!p10`.ӻw}>8::W>9_.0{l7[猔R0VEQdRzABO!wunk"ϟde5?Ng ;(?8>9|ŗ^ ;dLg.$Rw=Z{kOWU4IljBCfvA( @2tqy)8F HBr>[V!&ܜ`k`t6XOf{Y8Ϭ`PRg<gɻ__^..cϝ{H.<7z͹N*T@(9{3™ K $ %@(#@BPoθ~ ˗y.CݴmfzM(A[W`LA qxx RI&d6>J]m*.*Mp}1m^[Ks'[8~߆u',VBJD&T&HyG!:XcLfJ1 I1v* хR41 kr޹#ARLHA(2]d"cDL1FJu0%$U;cTeM&!xDtޏJ!ńɨH1d% 7y)z*n`D^ќoF 7icnR1q3Yd@Hs}זLJgcӴYq9B,+JyQ*p|R:[k'O|㣣j)91gO>z?X/3$*!C׷ Av~]7ۺ1>na۶ug7LuA$|p>ջ&Z9c`$4!㇡fB?z8P:h[m XgoJGo{t> &%o6${y3AAHR"ֹ3 ygb&ٶnTJq6 "n6b8jVY)EUศ٭W+J)7/Οx27\6X.cƚC]cԘ &A\KT#R(y}}9c1!gk5*Q1@8cL5W_~>w]ۭ6۝sn0* y.ljz5C !v}45*9d*b&{ϟ|%3Jd'w˗;|X^_O<;\pGoC !&F)#+cl?mNNNcpZ~0PUR[ox甂#0vppnǣJ+O1~`6֕tkK+VT? j}bVMONRI)'UQAx@,aL)P`Rヹt:),/|22RMJd"Z|@ 1I)pJQȋG7ƃ`Lcf%dp#!brCgC AJ!1kYeÓ;u F-_yQk\Q'gMnE vmB?t92ʀ0@"w1C#, q1)K!һ.^SJi5!$#l 08;RJoGt!g}LRHFV:w\J0HCج_W_2"/tQR #ȁ1@d1Rڅ4Qjy7|VMkmR޹EQ&JɄ?勧cRkmJc>RNkM i ݻڇ(83.p.<-U*nY׋.~Z q^ovIq  CFHB"PHXoC9[`HH(g|&#kSxg5vI% IHQrJh"Xy)0`9mH"!E^^-on@Lr3P1h cd@X"\O"A%*FU0:xd" vuJ)cĀ SqN)pE zo$ڌcR1L8cJ 1`!&`LRV@(ahB(əoflĢ(J(!l62*)L) Uc"jˋxee6]۶$B23!x? Y)ͦwY| Rf@h^f1DJ1ϲzGwg!"E6ɝ;O>,Ph]ߦ~AnAncn}߱6J*,s3ƅl\5펋BLQ*k5.J@cOX6yMr:,JԊKŴ'G*R1"J3!g3"Ǽ.XH J8BHU٤\/W4,+3edBNeZ0ֶC)weQ%$E!^I,R#",cI3:JEDqB,,{{؎$9U5h҅8{粼Ckr1/3JyuBY##P+*+r=(@JIY}U5]6]g̓j ֎g} 6xc`h!m6!DR`MqDR^)wgby[EJ,5t}/Ãz"JHs&ٛ>[v]a{gӛ_OF7a HI%QdRĮX++WToI,'ELDsA ot=lBrn/oz[ݳZ7,GϸO3}]IeK)yRēc?9pW)<B<8:JID"04d2Rɢ hƸ^*p4!HUDFk:8cBЍ[w1U"E"PPBt,c#$!BgN )>BzdཧثD$>bH‘1{ "y<0*zw/F @ѥ|<)H<.Ju s$!pwzbJ j@L$ Dtu=X!eYc``)I:\.[k I"х@YmiJ' Ɠ1X32SJfY"8OuVVdHh6B O$:Ο;Ʀ֙`m!8k1/{1k]Joo}A]-xerxr>/Bo}O?UՒ bIPHbss;gs]Y* r4M킷1.^}v{cz(Kx0 .O}`mE޴|YUZ,Vt]0ߛ_8w])TiTkg'Y92ISDғ {z3%W5=\E!x}*,ק|R !hsg~lO|0ΎO,f̔JJ@c %,ITI q4f|BPJ1汋#!Fò(DH4eF4JAuksY9@0ԂI"zN3֠mkPJ6H"Zg5Bъ, `49B)b zJɶ4 %TIJ1hg W~RfR&md<chu;o~SkD@B9R5헿͏ovww9gdee &dFZ/H Γի/ ?RpFi^7Y2j;ȼ޹s.H)RW^_}lx#Lfמenz6 _w z9o 9?, ⼱ĵ!k sj0FBY!Uhhh4mpps /\eu(]HR6V_zu1;AD`DV 62g|Qu](%ц0BJp5Qh< _M&le07Y۪딐XD,a$)7;\~}ŗ!.+9sAYVB BaQU뫩dTTveY)>r ִ+P κU1m"e?){/bO ž|$<)E= >>>/OR)}ߨ'&H2-|4icffTp)qR/5RT`<+ҼPXkV2#]GMqXT  \EOR+l+JضRB+u]p21`C} e0RpxXVޟ$R B1FA3Ƭr[CE A'B?W9pJ{[R@J(hz;=gL+;Q!HJKS:yr~=_ԫ+ e{g;>l1]|=i}O$cDM(B+PJ @6D"80J#@vGGǔL sŊ<O?aR5˪^](-{ jY e④4o1|Qs(`#j ǀ !=Rn(UÄo|+?}ZkDvʕ?B b>ܹgA~xprᓛG'YomnpZY\2Lvw;MPZMVj7iՕhabmcӚ1HY|uu5M;P'՗ɝ;T/͛7\./MO~h3ƶT28[ E8ygYpmJlșH5sΩc\LNy"s8iC hXիCJI /tm[9Ldq~TaL)tB)BHyxxef64M\(x4]dŒB 4/rklc2٨N4&޳LiD1=Ol7zk6/rOU}ӗ{`4u]{:> ~"Dh1]7V'$K(B"taAh\)@BIf~1Ӵ9@p.Vl)`z몖RZ'yBR<21,fAc nqKb1I,Ϻ<ExƖ!˳(2;">3 wg4ͦ!^'ȇRuvujA" wSHuVu2Ώʳ`6$}|迍O?ć`Bhun> 2QD19@:7>=!νtк۶Mdks^{NK.>ɽzI AӇ_pO>eok+㟾wE,Zyu{>hL7(Jk/Ye VM5͒#IB?OkDK=<;s΃{WgVD+]<ӦY6K'(nrF!E΃;Bpc:-D"R2@!2GM\xaowO2{!H){V'Za:K-[v)_, #kC2ڶ`oqQ.߳&0&:7Q^(k;B{^y51ł0aLך $:ץ,VNv7^;;;/⍏>t^by .E\6MΘkw dlˉ.76zFϦKVsRkmܰU§Lz\#?W!}d]3ӣŞD"!!y$`TL0ד<ȳ,sc!8JiE& e2Ἅ*uR9)Dvmk8BpއhP81 8_.꺙 ǥEU5!tX@$8yoneY^vE b `4V ɅbnMSwy_2u",8-EҔRj]A`>D`W[GbPufamuUrE #!ԩViC>)9gw8g{{Z`ړ+Ĉ@tں B&%%GI !l^嗯\4Wgެs? /K4_R${tZ!N~|tȸ<b\[;9>*x?͒$ٹ\<{O>(/ftx'4ι|]]mllb>Ir-Tۨ4!(GBJfJijPU-9w݇퐂RDpMOY&g%%̷x}\y>NVV%dz3(k^)"tB$꺮Lk%&A| !uic9 *ų,e#C0TkMx`^| uwn} t\J' k*I|W9_YY}O766]_o__zr[4(iHSRJh$.d7XOmg-@;(,<>y~ڦRRl,iRg]3Q|jteo s!Os})Ac9g3ۙ41kڶ3lniB0%H-t!@!leu2e!wއ{e$Rc-!$ZKI):Ҕ'*HœSʌ,IHTR'!>PJs-!"m#(+L+0J}Ѓe Czqa1J) F!d %4M4Ā$ #@QS!zFigֺl〒|LHڮ.ۮS$"JvmEY QJ Ё3δl6倉0MG:Wƫ}^9(N >>PHzr9=z$BP8 cR$T*R:[la*l9i:yƪ|X̖APe8c&ʸڞh,8!9 r~2YOGG!^y,F $^|2|h0KY62F4()xRk!4IIl6L;Νyݛ7?Z.+Ny~w[o}oݿ{Q(ʁF*!鉵&Ƹvҥd5!NOŋ꺦9gLJqRW_>ݹ?YYښξ:뻮cg,KG)VM[*);42kc1TKƈVxP1`Dk[!2;_w?w0rƮ^s[+9!`/[YQv3&ͤvifzy۷isJJBRTRK‘OE̳R&2"TZp/I΃{yFX" G+4NNOOo}Y7|vv1$r'v.h}=ruYm"k]U܅j#!@DH3.xx~.NVa2OcE=H =I$zg{>?0B`oPE\3D b/.R)[/DXD vN)9J,idiNp0VWYrLf*Y.+lY`cuePe t<(c뺮u]gz[Uu! 5^p^b0,|U7%2AY,C?pFc:)TIqJ(TK#'"DSRH!` Di!d lB,$g@=c$!x% yZ5m-ˋ3D)CJAXO x1 ZI$"lc}XVu()gEZUKgM!RJPr%zmmld4,Y[(xf_z~o߽?RH)|w7>w苼 <,oI0BT'IRU%P-m[od~W^l/W<͊hg!mX{_~y|7kr&d20 d眳< $R"R,)(A)E$+t6ckzI[׌FIc`}Z"RHV!ޓ>-<^gd&JJ諽'?s){?LDJ@fr}c[Ghc@ۙ{jxȇd:"F +4Yd2i&JdP"GBR)Ba3gm49f3p" ʢȊh:cC"c2/ ASBxlmn )UmCd\dZH̼w 2e J HM FCy{*#tc?b!J%cIJ΅\1<S R֋K1FZ9RYɇH4/2UZe) i;cX4d(mVJ"lHQM"b㥔UDBɊ"s!R:+8By6 cˊxӼU]!9rM-454BBi*R9uH*klǐ/()sއVicDG{B^==95eB  #V8DJlZey9T_bȒ4uMgqd4MR%$2ՍA$֚1Y[mk:Ѻ5(,K w_=$"!8 cZ$cPbԉ\[1t&EQh8Hr1K~Ug (%_ipRW_|嵫W_{͛UUI4/Afy^p!e/vcsc8ZMVd4Y+_zK_~2Yˊ7ƣށd  !?ӳ8/ mۜ߿ ?n|ҴmvܹeUt$G}3 -wvln_8:>vZvy "4:뽏yQ%#`ww)O Uݴ$Z/&@$q.tօ aёHm_t|trOw:JX\k]?8>=^߼{K_99;!|k_|7/$ͼww#!Z)։1Ƙ9a`+u]s`uWONHh"$H(PGB{uLRi}HI=I eE@…'<^q! O#ȅ !<^Dxdx@)c?@J wg,('D" (U2=NpF۶uVD"w$j͢ѻ{3ae2̥P91Jz)BH5զGǂ Έ"R&4hΌtuR̗US,K( BIAu-"szp8-#QX! 3d gyY= p!7)Gp8[4RHX|<2D1VKtqΓDgZ#Ecm׹`W&$ME`8.q.VWWCI )!A1J)"-E,k&O\ c \ĮmRrB;<5+Mb.˺QBQhۚq.6)g,qx2.TI 1u$Z+4 "J?A9>x(ӳT%z8zZV5LrQM I(عN2LF5.s)狪j3X>F\wkY(S` >L%?8;;K JzQ'GZQJA |A)=ːqqF Ͳus~뷮_G?]!0;/?_.DK!(!Fa\$IQW4QGߍb |Oo~87]-Z,IEHν;vpx,۔)$x<1&++wvWjݵM\M-LeYA>|r|;{hBD]5eI(6m[d0 e8<<cڽ;c(c4DN\,%ARjo'qUU wX'=8g% u*湚Nֹ0BZ# >0Fw>tm^Qܾ+|9$ɷ?i^/.j{UUM!Mт-\@FYwU:)tX?ye!ڸ:k#v$@~ 1ns>)'} TB0gL?M{x֛.=#R,ןE>LUI"iBd(@КRCD$$Crbi,0FMsstV$]_ @eYIR Lem\K%RB!xk;$i"ikXkOκ"cm@Fzt!o\hZsGR1#B]WMgs{D'c1ҩ`& .Cmׅ(=8{ }\pkU0HW3g(S:MӤm[b110BVq#书`!BH_IcBYڴl6o[cж]mDd,5 c( J Ȓ9cEi X֤e9YYbe>FdEq8LKA|8^-3n,G'Z!= f% R, (kC(NT*c]"jUH50)ʗ[9=`ޣB)!n!"rW )!9缩jX 碫QR$ :ε/_R͏?6ֵm+٥֚38$IZ*c3c Ҵ<=9yEuttʫ_ !?ofg'U?|/rmc,KӶJIx<88JN m3|>zjU-ӳ$[[U1^p܅t _x-I/=L&{?gi"A|ԿJ@W^ym[Jyev뽆h<~\XgN7z9CTRijk;L"7Muṋ]x Y۸LJ&,F=`ZEZEiZ{6=y޼q߿wgg "c,O$M&2Z'Z)e:y$>PN$H/\<_-u]3.?mQYJΛMӤ,JN5!RT筩S?>:g2٠('A^)B:/s/^ݿ=}t6rݱ}E,B]w1O\@J) _|i>'i*X.b!i{u5Rz1aSmzIaۼ?lzv1>ro  G@MYa:cL!P1-ʮ]Yg #pA ²qe+\Ntﻣz R%K$"C Qbw\KclSWsK{o-` ʲ,'L,t0U`:v=(ˊY2{Tvo?H]@(!,놂$(||g`r:-<0iY>(Ųȳs΅8r4.Vy-B(QS/775R&;_~\W|>RmlΟ|?|\YTUK"FЩWїlzG?Ӎ˲afRb~v6eol/f=c;u?Z,F"φ'ᆱt+|X7_!^y͛o}M+_;;ƣ2BkFÇsTq$a2mlmyWLN,KUY]\olnKu(3u~um-M̕t99ؓ`bјJeE2[1<ٜa\NfyS7{{{m[yggBIQrF !G /~{] Z)7s( \ %_[_+ˢcQH4rn{5Ļ(ɲLJ&:M<ϵͷ>8+jkZb8YxҥO>q~e|6b]'!r+g Hy8L@R4-cP&8ᇜ|~ʕs 1RF 3 (gS~1swxy7,Z{3ÝoUWwf7I-*4R%YDGX2H1C@6   b1 8MъEќE5dU]U]]u8Zy8-d}Yw>{}2ޅ{RJ{o/̬ͬ1|0Y,!ɺ,7$"ϋ*wenU52D',p۶mGDDIvYAAŪypѶ}T댵9N$k*2b-ag eM5MQyRvbCcJ9@0ۼT!gLXޓ("kfM۸HҌacpR@̲YrĂDeU9gD8nbo)! -NiEѶmbN)Zc]<=Z,vC>?I(⤓Cd"MQGF0˲<۴R|JBh]eΰb>;[W6Srֈa3gZ0NTvv$m"7nðެncOH_YV?x 䪪5ͬEt8^?ɳt5+2c *uh޹[WoYT04a'<ϻoۮrA >ض1|VU5[Z{MU]6uen6k,7֡zټfj}c֘{"lya0` /js_wR ¿_{OxÓof}yz@?~'?lg~<(\i7,k뺮gy3kmۭ<TW_>ew-I1ŹuW޹,˲2k݃~uYoPk7HjvΊ?ܭ[o'R!mӓ~rg~> dΕr_;<\.X|O=˪|sxx\YU廋l~/'}s7oo_y"ˮ֫|;or<3ԏ~?v1'xw߼ukwov:y`^_q'<;8;y>7 CUW wӓ6˗8XBx_efG%k,ˋӓvg1`lVeC&G2 tou쭻o_mnyuٮ7~//.ܹC:o-MDJX_wɜ^FBywi*')AN0 }SUd\f}ꩧOSCz[qb'] )xxǟ|?|̧Bn]jgi1k%gwߚ,SO\nOO]n1iq^eY `3meeU58ɏ|䇾ZcfVuX7~zbJNq^Y&/Sw"d\QΒc x|s"+:yp~> {ӷ>~g'>ϭr2;u3?ߴeQ]\e5kfwUWm]d1q8tRh3uS+cꂈ\ఉuSFύoffggC?$"4œb1$ ![CH]ߓ;Z2cg0:1dw-o#vWkVAcAcuY<[W)E{K5-~g~ۯ~=~3?#jW|vv\V{EYܟ{w޽|;8nף>5͞{x?ͯ~g_ۗȿQg{1y k?C-bseawo><_,^K˝ݦ]]] }Kߜ_wo~ŏUfvE  yc}/6+d$^U˺<ۓAƍ'u6k21E,3EyeY%Qfٴ".KΦM3kd ̖fl6wwԵ}b.Duڬݮgb>oRd CiqijSO/|hZY*;g(yTI2QWWW1q50cc!=֘[nHR6 !M3?5{'XDT'hBxֱ6a)`o=?6MI~%;_Q $Jh$BꆰǶ@mM7lۡ) k cLH"JO]u-j3Iˬˍɺ 1>tcUTtvCH'= A̶V8b*76OU7U7Y:z&.նo}r%ˌ˚6+ @$("'|\fy醐gi$hvEw|LEUxv cYvCH>6/|`YѲЎ<&rg1 "nrsvfevwcLcr]m1ɦ 2$CD.Ft^1cnh2n~1/]=kv?I"$1Bd@$v>cB}71]^LX6^\^n};z}֭۷rGg|GWTo<[o?~G\^;z;!m+|g2?/U_җOllë/ɽ޽Y|qN]^nׯ?x󋳓|?3?WT7n5_h۷ǟ|WW?1+u,LPWWYQw_7Ld1$I  fYwfgo$6 (HdreVHFUy17-8 (>xhUeOiMQW.YCJIՔ?z /]^98~j:$YCR 4N Yb ' V Wk|jmOǐ6ȊOl1^̗{ tw͗\^-vvk޹6DM9=Z/$(@Ȋ ѯ?C3=<9c 5OC1r` I"Ô}~1iL0M!91JL$!rdQKHYC1rHC!05q' <]=3|OYƘ$":# *3!N4 !XcU +,D4qrDRYиi᥊`ZcclQA]ש"p䪪RXCYIbTPD8vXk/3gL%נ)1eiaFTQAIȬ]`%w<3 2} Vem WD5cʲm[lUUni $CtN3"2G0"ITE{?q؆sSEwm7"DK٢FCLh da& `l-[1'V5 ^2}eͬ1d`?=}0p)uι=u]US*UU]U53o6r?Tom( 9@[U,;o|m<ԧ_8?yܻGܹSԏ1H_{r{=1%TսW}bO}zo|3)z7nߺ;8NÇ~١޺},kmkf2lqy)逸o~E5 ,1dMUu }puu5)[T3׎11X̑1,8wvۍg!xyKE0n68gEDEu1aX 4M^o>߾^A=vuyRr.aTLUGއfyQV'ߺ}SQD) n|'YC~g /|n߾e%cTZSeu!i9$D(Q5oy?;_^x>|"议6ƒ@٢ԉpU"|`  D1&@RQQDchJ #cQ)#1©$s2Q!1!NlYKd!|PyU^"veY h:0,9KdFa " YK5̬ʄB$lb۵|7TvB*#V"cpf)"˙&֔DU5IjHu:v6XUX MӄPIJ̀a\;/438Ĵ3\u "PE@:I9g!2EtYTY!qD*RdBUcc*`-JHgZ[$Yaa"X*)$iʼڝ,rJ H~gL+U5>2e1(y1Ii1P5D$ B<_㽽~e};< :|g"ml8C!Jf E>*Q$n,aңɄc/ySobNE #g@daU0efI(Y>(2uQ2iJ:3i!aF6s&Ƅd-vU;DgȀ"="G Eu*3, $UahrA.uÔ Ƥ`،"2>* "bXBfP!3*jd؎%eX EUa;j`TՑa#30II4 49 QAfkGӞ*z@|T(rJ,,5XhpGq3O(MfAXh $Q@D%h2 Aɱ8&ICBX;1FCRPh (6B" +^"k*$،YrJ\⺗$0/)3X@(:¨D%Ew (lzQENրO ucRK aFӣHcQU ĵWu"cK!)$xIwJ&m2 *謠@Lʪ` $qV#IYuQ! aV#if0*A !WNi iHUA>%BhqDgM֣)-0K`X-aT uiL j&" KYeV{lIhBG%@u)(d4*"(ceesQ$1sJHQ"09iGL:q3HT]IX'q t50 cHSVX4&A$q$D4ŰKe(0(+l"7?eh1W]*BVG$HWtLf$$ayiQ&慱!(r($F uN0tAՔ9$DՠeQGʬQtQPdU@PtQY̿˟YKnL1mY]7}oU[@`VhHECE1D1IuY xבuHjv*گ)0V(Zud~E5zs꓾M]RָSQdح)>:ԳX-J G3jl:$ ,  'o! VPݩIQkJjګ(tߕWj4Xitd- (^`B4||edIu"i'?(ԴSQ`ݫ0>&gX,%An{>,hV(pa6Akv*کPNZ^kոW(z8(p+ճÚvj 5և-o'/%qFکg9ᖷ>wgxff9&s .K:Q)Q,n5npV)Qڭ1ȳ>>'$qک(ՔxzjڧG,*syЦ6g4+P崓m}*o('t9<%ݯq(z4w-Acv+5EC# IDATчz8jhYg9>ȃmjGksDi50.KƒMj}K MOъSaC>^M`'(`QN[Y{$ny$۴:&I2 -JLnyks(Up;τ1,|MAِȺWlj50S^b1W>3pИ #AMqɇvJ"G3nc4$ - գٌ|֥.g uÆ^EpҥAZ?6 x7%]2Frؘ2tP`ٯlz:6 NY =Ɇgjsֽ~Ss3F9mcdL3ӌi36vAg>/ՐFCH²eCԃ٭0 `F,JcAuYxr=>:vJ@ܯFݩ\@xiJ|4g/*3˱ bJg6w33XUhNy>U ʵ$xՀ]RdTdV+O6r'm vkڠ^=,JİHa!9(&ָd$)c~Ų֏-+(UNc^2׍"xU,,E(זn1A7w=x{* QQIˢp<[^0/EIjN6igTCP2Mq6y=/`3K,&%@rȬtvY٘gB:J)bp^蘰(Jߵƕg >\WitYv`Dt5Ƌiy'sWYP \t'>^d}TS$)?~'J)C(w'2SE{NOwVj{3 en G3Սly O@`ո[Ѳ4 <6&M !r]Py1v}uz=F!*Y,-`AnRTWE 8wvQ/J'AyާxZXiQ߾\. ͨʺ!$h&^.X ׍QTCЎ6rVK%g1ek{ݎ8 t5*.fAKb3 j[Ij&g`YV!\2dO>jSؘy ">b~mI1Ac:gۋjQGO։Ff8hRsI";eem$,uڔϳg+XQy˛>-t̷c8ow6W_N^8ho;\Lt`UWnC>$MA~Ӎ~C8+a٘vHДfۧ ~֦vZvۓ- >ZY?8nH \tpnڠrm>yGˬvJR̰=my;kܳ?7w_/2ߛ. ^_f;% 4Jyuz?e.R:/Xe3B.*CȌB2&anLFdނ0#aPjT F@乣$Ӂ5XI(80i8q / @e9^4Aq셣! h-*LOLn $&AY-kvlTN6CȌUJһH0+4!,)\leY6z/*9JI!XbK茳XQch5y1 APgH$)bf20.vdP7 53ߵ 9޺un 5HBXyficqtMxĎnw6-mIH h.RI|=kq /dfd|WoP.].F3EΈ*l\~Ո!Rz5i*x L.x pq(eRiۀL;ɉ;G)Lt]8rFu>1igJ;-@jp@7J7I)z0azm 43SՆ9p5m4 |Rƒ|yNX^6}ϫad8 6ptF$-tS C98{ǪT*ΉYf Q 3Hཏb~S`FLǬ)[[{q&"R86l1Tr*ObEQTMqTRsS#_9Qcv 3**f{)t&cyȱHXC?8 Lpjzqʩ 8f`4=y#-lB}w7/:/:f>2e;}26UMR)FB3B.rR5BfP狚:bl}r*V7Ӯ5+l=18WfF`vdL=(`d )٣p?moO~y`^Ԕr<ۚgU:g( e5[[<AI,+0 09a KA"  bD 8a %`LLUq8`}(2lJ`̹PFݨug=3"(6 v!BE@9;yVf*g=`3+:>! ;,N׉viLE$b\5w V43X:{fY ̊Eh *Ty0urIR\h iEϹ )VaoBl(IJ ovf6mO'V PJ~X}'N!ރq植ttcX2 9|UܖnݸI%w)e"lQ}d6uwevG)7vƹ#QwV%Xlϟ[aCƟsWAyHЌeٝu熦M?Z:3NOGn*s9y./l]uV7 2N#X)B"znKO:шK`f݊gX5uqk19L9hLgNDjMdFF&T&mD*(BjV"ŸʨgGbT}1*6 5]RRKxL+TF^ cS:e{ xfg(+ywx3८-:ݎm59xIzŢ:Utx}ܻg<|B1 ʉR3֮*e(A` dPP`(G\k w]p=3c~zcnzHV- ;YA`֣˶Mn  .TLECym[f~žvg &6#F:}WX ,6h8^xmy <[ͱ3CAZ6#!OP2qmM) ylRlV0@WX(@"h;.+<-wPk'd;)+sIKM@ ^:Onz:A0!*Ȋ+ ɖ1UF 0.Y a/P'aHoˤE,QeLS4հUeSa4ʐ§}ZᩧȘ8(deDwn,l^Ocd7}ɜl75TQ:aˌB"I tɊǯ [6m 90 E14; wNG?7t7jɳu1`VUOqsg0jvZ6rnɹiAfUE ņ6DTa} ǤdN*3D1dtxF׈FDs@5C1:׼wQlU 3ZX+` nf?-ՑX'fXIHcϐ`/H{ @l8 H6d83Sm.=CTjG0#3Wd.-sӐr!h2G] l%C%h*;K0Rr.};sV@0x tC׮!M9IV-ьPFAB@ 1 YIzpqK7}q4չ'RumN:;jf"Y* MRĴtp?BF+1xڇit"R!fY1gM+}6)#⃋cBR Ah@Mg!ebh6Z8nmInւKs1Xf:Eѝ$YT5: y0)#Jš̠B z@ LIsdFlPIYY36L4Gt5Pګn謌抐!{jpEn|\ά}pw޻v>}s}x{P;:׎\Z, nΦmlDTTl^?z.NRvZ4T7^JX,ƱSa͙4N'@4jwUzjLJGׯ޺vڬg[ ;8K?m>ոߛ%n߼b|ueۧnR5[M;kCUM&k_*aXN[W_yeۚTI*ƎBf3Oo7'"q7;؝ln=<&F1TG%m]_];><<99>A[vg\:;{zhMMT4R2{՛^}ދWܾ|͓E{HLgMňؙ02({08PKwqgoUޯׯ=KDxU#8og9~ѿ}ڎz2F u)#Fxr0 x+fꥠ;-SOI"fPj`jȄBDmb̦:=W@P_-l$X5\TD֖sƛ]BhE ۴& ,}2DBݐcv# ଢ଼ҚrAgd#F\R 8 ĘrS9:ɛN <֟w6nCԨNԊpquw9r ;Iu82S:s;S!;wg/|'~ ]~]h_agaE3F|&&YdRQ[t~׾o?p9{ﷷO?wGPˍ+ +M:д°i}~__taqC. Lt|::Y}/?37mIw-,/vg{w}dSD:6#s: n-盇^׾ }6^|$m3tcِ hzηw]<8]l> ּeFhsSǒeq^mk׏}sԣ'n޵ :J"[$ilagbp^ʆxZ{Z,O"Ou>=RJ!3_gl;56=x8+Q.7kZ9K~FYC9Wr1X!V놣O=O+9WHTZF9Z.|_k)2λ|쳇Oֽh44XWƣu?;86fZ0Zv;qٔ{sMԭmRfラ~෦T6Y=++_Ϫ9bSD;;'Oܸqĵ5yW(g3աnb?߼GGgE\dݞϷoK h9s0"騒hLZqjf&@@ ;+?`*M1 (Lp)"#MG tǝF6!ű萋a'#t9BI+*7 T;FѾ4uapsTHvظxmenȉbM`CElFih҆ e1حٖlw㦘\V['T %[)pɪpH+M嶫y2*ѴyCcAf)Z bìeEՖ]Sʩi1T!S'\4e#j`փ) ñ!0چ=Yw̜ƒTkb6H-tub0t ]I-Q IDAT^@ZTjH vT7>Y4%)#znl^[n>EUP2Ry DeXpRHNٴ@ʈLiwr#v=3ޚӼ^S2cN=sQRigCw78\"w%\:]ll{{?=OwkӅqwb;w^-F=w`2u8ޔg;{awvZO~~^Ɨ͚ʌ|}󺜬`:|\w=~ڞ;~"vFӄg{|퇾mߡ>L?-*6IB ,d,ԗqH47?uWfcF.6s]z+ߞc!0|#V̚2c>YVOmYtP1bl8Ot7G_u~K7Ue }TKܷ;~:K([Ͱj!9G/N|??~'qS&bVN;<}Oc0{87]۸0ü&ڍH.xz/~ߗbͤ??G]&s1.ҕRBMѮo{oZ/}G3cƙEAd|͛^.Y'蓗o ?O=cO^]oOQ#r_lMy#O^Gfywޱ_]>co= ; `b0*#gSE00L+55UC|:EI@kpH@'A` *mH PX.s:3J 04aU V OAźd;tRyO޳ tYYJt<_d!D5)* P QPٹUVpyRHz+~7 wN0op #B0llMt>}u@A%QŐ11Rcrkyf-TY@+gd΁BDqfU" KFsJ0\DPQI$8ey0JM[Z@ J&>@^+ƥ❢8*a)=l)*"/.eBRfCH"i}3FlYtBrf;Fιj ɬk!B &bӏà̮ 䂵'D|mTy؈,IIg6Aw/W׻O0 cv.`^cPdhZLA z2۳75|;۟|ɔcVӺ*۠m!-i׶qc[13_u˷t E,4&`R&vEV/cזg"EoG#f5n\?O?mqRU~^|Xн >-Rt/AԪvI{ʿw!LH j\}VQ?ևw_\d0f`ŐQ 3>s7Q׿گs?uϜDMXSlb1"ƎЛls/pE|6=/{uof&:HhO''=ٟ?jiرJ1`y=/k_뎟wOȕhah9{9;dԕ>w|Û$tg{l{)LhguTՁ9D)mq.$$-LMa_x hZܦ/Z6.F%猼A(F1|䣏~߷%&mO|=3d٬:#0pW|ًe$|_y2^≧~G宓rv c_zI[}Ow}p UW?A&{8G @Q IFVxOԲ3@Z,k`xC`"t$SAFQZbeNّn 9̮hCs{(+0/7xjN0hZ:b06̼tà=`m2z_(8O-@@MT;gVv[.6囈l=y0+4m\:egjF̍cϺ)kfR(jx* ̜gΤDZ CҤ6f8绍l%E3_đKQ,,(ʅ@^#2a&S58 'kB(%-{ZmhMp@NNeuǴ_cCQTUnA+RPADVU(BLg$[QR R6W#;Kɘmg^؃GsmlNbCG ]N0֝ag{_!ì+ ){>ZꍥM%* oA<) >w|s_rOwEB[vn*phRݼrio7}Ϸoo|}[g<>2h'bI sP-}Խc2^{wg&}teԳR 1Y5ӏЂՆ%#6{Up]~7ɭ|W<'wbތ0؝ M[G2_¼VʙfSGy"MC~k@a|kws-"ACfgȦB8].~G?v5yxg>_}gF@xSoX~۾`WwZ'zͧ V U5|0Drko)f~ DˍͦZDwɗzfpZ%e:,bUV5#%Ȫ*zۢ1[`3ѦBEn/cx~{9%ԉ"DTGɊt҄ pML_@=v *$Rd0AEȟL8A,+F:ې`t jRJ'JW%qFYC\;b mH8H,F\4 I3`0ӳ Jeidg'SK1BpbιF-À-{ fi1;HקM<ʙAMC)D CZwFb<7-x!zxm@q 1yOR؉w=5M4C &ә *A4ZAc J0BT-zNX3s@=[C3.Hm/$elk" JE^ӕ4stg3\;[xN=jdw#kvҮ-wqm5cmoz]w]_ߺ~ilG 56o!8P2q C/9cs;>O-:C"&|=Xn%(PC`2S3=)(hQ}E赣Oͷyq;_z'>q]aVƒ r r0USTG^ 9sW= o{/kxwu0)rz6ؑYFIM 1#v*jذfrLϵ03??+[[u}|ޙͿnҭwUMt3&V;̎  &1ڲo,pσ>eÐ/~[^|2'̚*`7Gy^ffjjwNtbC͙!;vt;=~n\|՗l/^pnfP<[.P'8GgWP3fjWr jm;@nq"73@0Xߍ__<3Sb{{g~ ZO/ wQ;{?tSC̴U{NGA@0F<5+lpev, Oj =Te30G y2?2 T(P:2xB,*&A8щ4@1dB0ez&q4**@zJI|+*D蜡PE?Ԙβ"cj `$Gƈ mXe(1I(L P'T+CDqvh<رݭWϟXHgneQ0 M0%X%+wE];U41+R3X2x X&=ZPVyɆhRhsSѬbR*dHmJmh!k)C~@(I8HVeAcqNtIHt%0zfCDpη"`ؚFYC0 "ZPE4UU>Pli"bCjhTNzw4l1`<2O U7쳨B0kӬ1lLE$zap\@F;.0unP ľu^liBސ SlDl$Wu64Ë>O_︸}xsq_o<< OG[500"aƷ啓:¯lox+?OXLVfU,HyTMp V]ڛ6PŒ!̌`{{: )LC`3Biq$x:\O~ ە/kKz[_yt/,Y彃siiƉ]f 30ѳ Pν Ӛ V RKz߻wo8Ưz#y}h۩]pֲTàSƉb$?aJ!"Ŗ=^?3ڷp7Kg.ͱ#3dԅ BǝhP@UWoy=kt'HQ$qDK$Zt[-- I5?@Qh@:SؐԎk)E RM{xw\ ~z׳S+HacfyODȰ^,tT xzm;[a+LILb F "2fj3Xҁ;D:*,O!3)jP&b`HK4 zJ+0I꾠hf);Vwk:IN5!1͜&*[`m:׽va?b^Uu&'|f>`%v wU /P ɡsk}atoڢ|}{@$ k Z`S[vZfDLŊD dg2cpۜO=覵,B`l\D$^WMkbA߽o^MfY[G|s-me>:>i>0aDL4ήUt͛6o[M %C%[ZĜä`Ry[zJ.*6맍&#MQ^s31ARP0Ċ[.k= jem[] ޫ*G˲dVͽ.jv۲KᾝO쩔og>u8k@teh^cS, Cli.I84=o]7~ҐV݃rlPQfr{S7Y2|*Ȅe:ZaA !]y\ .Uf MIĢNE5RL Bo0cBn:O`p$7apS1`&7N ?lC/ɱ9 crlΉ#UC)-Y%22( B ̈*@~ӵApvzߴq8jRa H LQZ,3 gR@3>>cHH\p-I3cAT޹&ĉ5Ѓ֠L b,XwѓQMU>w^u}wS.,fl3]ANл6=dE'̓Bs>FL(D -7lm; #xrU(1cځ@L13K;uW1VkæQe, LOTŊ5NdH}k][mެ5\ނjh=7n3Wj6) :X6i3}23-Y+W/E@iҸ_9GDGGȄ,uCFR.˔jzZ9uJkb/΢*iDž#{LJ;_~Jf"nK7i'ܾ16>c~i_qӦ/|bf?ǎI M?i99^mV`cuQ<`2Aq NoHc #o6P#41E"31pc׻?]פֿzۛЬsź&θP`LTU}QLtT=]x+mIU1 o6ntnAR7r1XckLDh}>bL_3Cs}0᭣ls`(arM-_W<$ugsS?=8ڝMJa1grYʓcϏ~7U?]n|ݩ9SSc7vcY9 ,N`Hߺ*9VW=?y^ͪժ}wנhLbmiVA*G&;q>t$]DD4abug] 'SUl34Uy@$ @d4 S[ IDATĎLL 0&ԑӰ3UvЬE 3^R\̏lfbFԊi,eQi=2LI>N E Ep ø|.sc7N``zz,ӀHUf,`22h0 l;-WG9:3^hh*Z׋cM8uP ͨAe0siRQSb[4PA'TN7ҷcLbUIəZ%`\Av+\wv@Y*XJr֔,8 &26À*XȧZN#٬Wڮ-X; 7kFcZu`gw< k4q\ױsl FAl~ ze^wN\a2zFi;Tl%ڐ';\Ԁfs7]J%J7x/`P|2⫿O|ՍG>7_ӆBϩLe(#r>Н.›W@;JJ}Ad2 .nYȍ1(j@ܤ6K쁻vꅗ7L !Όk5s'c#l 3l+ؼ_>?xm;Gج"-ER6UinʎQy8mzqQ`M2NM(U7O9m44KPjM-fG~kO>Zno]yO<]y?po\]T@+݆Ʌ 6jd&BJ ݸns)!Ҵ)B-\iP]VwSU_/*/sgdRM#0QMj 2 >|CsNO_SݧV'_;?$VV*( "%DrFf2⾀E4X`oFhV#+q3#:$S`K Ğ@lL&Me|*`dT 3I3|ʞM+ RT<< Ԙb$c8`G,gV1yu,bYrUX 4~",VD܋tB>$(T pA3~&`vڛi`]BR @`!g6qAlV`SȾZRV*Оqf&! ]ƛW0go]J42B;0Z]1탙`ZSN#+: DnEM }'3 ȰXpģm^vf)V䳠qVjL5r%9"9>e-IUb\$AKA>8*bED}p٨Kue.Ƀ,ml?Ӯ㛝tJo[s (HtN(yflm5(S?RXbbH'[Z )C YR[v 0of*%8!PlFFb);2ǐ5DTi0sfJ19؟*Ԯj ӂjݚ_9Rbb8W3! xQ8a=9*v+D9/'oZ?:ZO}t>Yx~C.bO""}k;~ί+9he|Hӣ~s("QQ1&כl(4_C˹{b60lj*2,[  {7~s"j&R\@]45`f^(.0Q%%6b¤❹S~Ӊ5[M8W]m>j]vYΌN&[P 6t&^@o\w~xo~.EڟclbNL 3qfaK) 딳F 'KUM\6!hZaL7L9l 7[\; .]7LDDybRʃ P׬J;]J)/>ا7_~7ut؇ϿrR9a@ -PO(6`Q06k*"*0#:7mL<3KdhrTE3 Xd QQj39וCQ*s&-)࢏{3 ":dGg#@IaPі 8? SqY ̬S D:!EҙHj(ְhHU٠: 48Фr^91NQɨcc&T1YT SfǤm.KAK#̪ݎkX'Hˀ[DjRP9cpnƹa05tEw+DZʞ7փ c&P~f0 xPtL qDwfǻA@YWqQB MUЫa .h S99Q.+趽 HҒE_/PL( ;8aC*2Dsј (Lu&L]Y4Z1"ryGUr~WVWе54Pp\KNNB~,<&Oų{rD77D跞/|܌vjjWC}q)˵CLUDJu_͟}/§=\m.hz0 Ί juDQ!&SuN7ɖrFP2PTڭ v,;bGr۶f~o VmR˫5œbTRӵn78YO~0o<;5_78XON|_=,/ HmA3ŬzQ8fX7ߺf.Ul:"@<&hJ6XNy:q [LJnmpw6l W j EEzpǹ'>7^kH_𧗫n׹~`W)#,O@**)\B9eTa@R"9J)- ͭ:n[{>a -݀U26>ˈ3F:srJu&LC p|"'GLgJ$;+fElvw66b 6@kiҭ** +yjB fTeTfH8ESZQؐAƢ bSh&ʇ7ssfdtz1#ԞvE%|`m¼SA `Q?Ig!'ݬAכTŪi(haF!^/￴G]K'{߼o.J"ਵ`'x嵫~iqZ/<y)vo ]3y*:4bZoFl='y\ookzo3Gm[M`R lr bmg)6pmƎ%"W:D$LTUFHŒ@ 0S"JAA h[3b@SSU9UӒ eOP+Ylȣ S%u3>h?Up@$0L``81(DArƨEŸ+i[ eޓ;P6F "PJQ湮b#21ZAe4J[f1Wh`2OQt#zÏLP0r,@;.b+UYK_f9:ZJњq㴬{9Q30Ց#R#DF RJ*tyg 8P`h*6I^#A8&!T*PQr*jb!*ٗ8{f'CYp3LUD"u]RvN]O\Cںʁ26HAٌ UpŨ_ 8l&ED/#O$(ƲXNp&h936fVy֪EXn[ʔ{/4چқ]^v@7rQ' IG 'c!$@ক.F{*Ӻ iI5d$#Co\M/{wm[R]lP֧>=>Ny]g?ǣw/^MXJӮr#齗1fATRu:`B}ws^?_\]:;N+H0nlh;%x{'FͫGD#ӿx&E&$uj$:C$wB㍕[KMEhfPr$7\Z*mvgz-~9Q6tj03:ۧy 6p7~lz\ZQX\W~ٗ|@9l7}~7o.^?~\H`*PAKͥⲐ5:>VV=ZI.+gQz1U!aږsU`r9^MDڝ'd[m” i鉴vҫg7Qs37ܳ(䂒yB9:J5BegrF` b, d#9pXk5h8ٶ&Zu%T.2%uPRag1F炲ȐeMju &җJ蕝(e9%9 fVY, oQ8J NK.DXjd`B.`MڟC ;JCߢn}z9^VN[OqՇ s^<__Qg[>C'/~&@ ;v̳}ŋ}Ly/rtE]luOXܝtKjRlmY[?CZmo}G?r!Xl,8)ZlR#G97c+Y8 6ѢwLDO8w;"1åf@-v@j0k!IĐkk6q೟|9Z>Зӡ6E\W~RzQ%ǩǝ`Yt;`c-~DpcǬlR$LdflCv@#DUX9$N@"(M[0EZoC#u-vf=vm;LBgcٶ8=6qR ȘCR8$bPFMY hKjdvLBGT`K[f`љ[%?z**#3A0"8a͋kbTjn74n fMC6Z |mu'ZW099ɥ(}Ҟztъdyӫ{w~/ ld5>ԃlC~^Ժ1tw f\S^̔Y/Ԃ>/}_O ϾtmAl`4^b>5hnE{;>7:~ktMlQtU!F)63*0Luܤf.m60j\ܡ94>no>[wq]ڡ~:lń0R\wkGjGfCg]])}O<[/mBg#,֦m+mSQgJcJ&MٶV UKz"C)dS&i[)BO<4 K_{q1 МCƛ7W=}hsŝ}ޗsk:3e=`@ d "g.E N[ pEbof"Ųh**"j(HDZL Σf&fF)2^3!ʹEX XH dhBjeulf3CJ@90258P Ѵ2# HWj sŽDfY # LH }ZR#YsA 4 @n)1Q CI & #6zU*#mg@RL$%;9#f#B=ɩ(Ec% eTTA "Kg c2x3&dy0K5yp4 fCRPb<Ԥ$;އ8 IDATVAV$4{J*y!sjlw3J-Rrnː1/\]R+0v@d&b:h: @3b"TaoF;6օa#mc2ql[ޘK4icQo A#f`pjgn35%Xm.W'y Fc>ʢ]?,~x\DaCEkppBdL'-;$CZW 碣X$ci?מ|e{?xוޘ9,{7oiC;;~L1q s\~D뷿 3^/~}/HT SigaYg+m{.?~݊Zbhی¤ȧG\Gb [xҏM)0B H YCy %˪~|uQ]o"9 &P{WQ/S/z{!?x~ܜLw~tg_G;=m~7a&ٌCIOJ{t"c=GIvaoC La v;ȧ_|koNopŤ3A7((!㴳s;׿ۻm y/=5v;?쫛yMP= l0d+E0r.jyi0])ضٌۙWMl͖8z󁢣mbI1(W3+@Jep 9wYM-o9Nᄅ @M@$8 8DMEDˑR)[IUo؊S-Y2ed)d8#R$ @Lsx{(G*֫ۯη7@ R3QYN#QED`"#$1L )٠9"D 7XʐB67ĮPl61M.-; (_JQ0Hn$( k0tЮрhN4%y+Smyj4M/[ 7_yӫO=ąǞ9HCX(8 (}Ͻ涓)v;#/2+ H4E`QQiϻjž~?|ZO?zÊ}ѷ~_s9(;°1No|䓿߼3>}n$6" +[h"v=:mlW2X(c:5H*3u%E^Y7L>+n;LkѢ6Y+Qd+;N} @Q11ԳN}r4b9jf*] tf?r'N2 \:$\oD6i&_ ȗ>hУL/*ڱ܂X2uG=o>O xo>6DV0yB/J6ŸbL8 U1YER-Зp薛G}3?r+X3rTsRt`j}mUn pPt]lw9b6u5;s᫗1Ba9yv2uJp<lS;q`1UE:QUd;^'Q}mo'Z hds,@c&- Dٗ,;W {8,~'!&CzDy[^;siJ"'Ԧ&!;Z9. v&k)ҶDM K2hsTUrɬ﵎hW*ZRf  6z98XLf;re k8^_D5ACPI`h$9.Y1%5ÊrRpp*9KfvPe25;@,3@jbt:jE2JoPF-h",]7S fWoPLJ @# 9\0e&\N@=lyNuhT bE,3G,h#$!Ud U`&0C _I6A Yowa$-̔iTRg8GR8F pS4+ 쉠*m{rEJf.{FDHLQ'M3M\AU՘}m(fb3"Ap/5T#&ic2̄20*V*ͥmNSU66a5NR g&f-U*s3^= ?[[/| 矩zqV+[[dB,[yyO^W/}ۤ/]n[%ńBq\M8)@EDa13,PR=gO_}`pyLEÍNno<{?|urǺ2q"&au@s9uC'UՔ,=8^{<O~ ?#ښ.~,jz(R$$jd_Xvn3TU,{*l'gZ)W?a/)/\ul8H Dk=ϐ6YM3<]$fyΎNB ؆ @pޫ: hOQ2xaT&A0do:pȬ)v, HFCCHjAm`!.DnVrufa`;#]o^7cfC# # SйE:=APx爉(m^IDZ:pdc! ˘Dew8 , O5Q8/ieh!FYѲZ vjUaHllI%{hec>;EB[ ŔcpMUB ٲ^XEa-)AP9*b6,߶U`ZXDL9 AI`JRC6X# /T"XsZk=Z6® -,e>QAjp ,aӃ@ ,j4W8U&:^Nm:W2Ԋ>-#2)w;gc15KlW>ptU^[E168"&/~噿o[V/W>>Gc1j9LW8sNߧOg?hJi|ӟ~t$c)-#܀ ac/=;UK?8{S $Lb0DnO뮕QɎXm^.SH/vsjzdK6< ֠]uz)}O P*Fm_2(E.==8o{p[Ξ&>1#f)mWse&#e-Kb ;5Rq'~ʻscpS+DDb%sݢW.7 ӼKO~cMSBb(x /]Yf{ מ|[l\ ,2R9IusG1Ud0 jI=S{c% ژE%αMF ZZ}~QzZd#}Ot!zn%'xB_Q/ Z#*UI_JNhң5,cf÷9c{n/bbA"JШ N6[lnVefәb+KC`Rv(uen0!lfeN`?s7ڼڅ2}b6K9Xf*Op^W?䲎7fqwgcz sctuJ Ҷ-vr}ej\iDUdqGW̫R2{j.Z115kټ<%Y;Z8.T1(PĂ'" Dg0eT3 eFAU-:*}igܺI'ְ~T&SmZU!-V HmsD>~yۍvGiuʶ IDAT|K/;o9zĚ>vnUPpB=g}ܽ͒nF~XX278m_x7~[v)<\9;wy~rkSGysS9K[sAesO}=*&{<_ڑy=710Օ޻Y6H}[R"➳|Aן۬_1ėWl: ǿ~š4T$˅/=rg6u/k_}g.iJ[DJCe qTGZ! 2Ш~뙋Q薛>=_~K Tp&ŜniB kSϾO/>8m yr@鑠QLry]w'}ڻC墭읻;^~ѫW_9cg)%mPpݛdf6W{ͩA?CO}&ύf죸3o:ux%7m%Mj#s۩ygisq(o  C^}롂qmo^OyXrqcן>R]lzM![[,͎wp-JćȩǕm〖RKwb8(7?+oꮥ8^A?|=鎕b UFؽǕe9it}??zO?ԃ$ xA1|޲6\2Mf|ÉգՕǟ.M ѰpaQ78v/7F{˪ p/۝b>o'fʨѨzcp$Pw|8&.]~և̀@dptyjN}ory[]coYøG!pnR|W6=2ڛ/|0{JFJeF#jλNW/|\ZC}mʧlL^hnPΑc9LqvgDO+e84@IRB(0Ec-e_\6kOT*|O=C\GCql]7ه_]`ڬ[[n?3W/Ncgw4mLݝ6!CÞOI~#d>y;]ߝeO:ү|Zⓩ#(Z¸G]o۝1dS]_j 2ĤZ^+w o/f/`o\y<9KjoO\SdK [ c^ ^X@^sS7hggy㙵p>Rdӂ;ffح x9ףv>\J#gNw';T5#Y_?pi㣧//1-ZlEl7/=CO >9Ъ%C\4T~Ũ{+|aQ+@mUO]TZGԄa\ GJMpĥFkKA߯SY'{WPI( TaJ1FASVK ,Oo(O??5А*O&&f{[O䏽YYz+}8-0IOl[vO+Wu-8jVX;‘bF55?So߱N|pÍsi+{{O~M+/}gl\`PPE6X E52] %8e@?,#(z۬e,G~^*Q'qb }wd\lMh"_صM2N_={Eh- xy쩋}'4ϊa!riP%ߣվ.S[v7 ejcZ$R҇EcjeƐAD/Y$5w~WZҸyt۳ך%V+HTםk uf*pC`1JȆȀ`QTLK5& KE6kQv8E+HJ1 @ߣp%/ѯr>e,grqUd6#W2W[:9%e%q+65kMRg[f`AQ-A_`,ɤyNѦ^; #.=a̱MIfIV lyL1]ТZM'{ ƲZ$7fǟ³o-kxãg ^{79D  IX,%d9rqFI;3 Gn?_rRcq 29QҼ"-x JP g'^!cp$.[u1Km;DEjBDBS18q?m~7x@.P ūXWu%s( qI҂͚̿xqkϞmk`Xg|T[`8Khe6 aӕmw#Ip~}4c&"'˭#TNc;@f&ճYޱwL|78KXgDAS rq>z+_CM fy>{G3uS?J(Py|[O~'#_Oԧ|~ŢKPig~QfnĶc<0)Ownm^uf_n;Mj%QH=*:p".ju"$ձ~YSC 1 Qq>6]U4R(1%y(,m[!UQYea}du1|;#΢L8mh$@wd,  l xVz(wri2댛̣*נ]gB=U.K0mcmG)έ"6q?' ">Gr@%;Gˢsxa&ZT*wS0!#$Q7<ݢ DBFI$Bⴾ"x>R<;)(1^]Fv&:d4GuBTfWdC-M^8K3ޗs0o)MB j}k>g[|&H}<6吗]A Adk5w-,` M//kUx~fJ.̛ vb%u$47QN+{xXUE 79xcw??v2w#[4cU@~͕;`{؆I*O}h_x@(\P鳎>ї>oώG\'O7G59%c"iqqگkvCCRK'g/BQ"cH?= u$%J{.ڻ Wݟt?^/?7֯ f.ہIjj4U>@.mCET5#ZV{zV BdU&UscAefe ,kq]?g3 J+.CvTT1^_ɽ:ڙec U%ei.\k% CU#LyLF(xsdz @F eNc0wH)Qt.64I; ]}Z^ MX!#̠ GE,46Al&J:WbPP'ƒ)}"(F $#"5Hn(P p'G P"PP0@(sZi%GdJT42یQEHZPXJh"A@j#QC<|gP@#LD6-Edֹh ee' o{.,d[=&Ww|+K5_ENL]c9%cL,b`È҈`*T2ь]aNzRY fySXZY4 q,~g #F%*% *B?QIUP2YD\UB!9M_^-і A,h)3f|Ծs:NL`&<yēt@Wcuc=|B.Ä.p.F:W*Dg s]:˙!&ِ>8PGo;,ZR& Z&xq΃ޫeĢ6ibjSIɝh_+o_MB%a%Dg+Vԑ IDAT\-IT)`U Mk[[CJ^J*MdrʣLwBc|( HRq̓V8 BTMA*bfi'ਣ,[U7^s€6J@ >,Ո $"-ApU@+ ls,L~`hQ\T(DLjde2pKZPHn$vľ8έ"#* QNkȪ0iA!R=0su/>\X )I!:8yrEf#8SJ 24j' 6)DV {\pRPZ.gLl&{ǖi @;56)&cJC;ۯyݹo!BҔsu{Rӄp"BUC~rlb&\h6:^__[_ Nuv0048 ^v ]rf&wRY^}\(S#yfIЁh8yˮHo(. T>fJERWk\pٍ#Fh*HLI ro; ĶG#&!U!3fwVvßǡڽp߻ܮ6 H!R![6{YqsByRY g5U8Ja0̛TU!Z* nfC}2r&&VQ.XE31pVmcbF9Br緯 YVl":qS;$ Z&FtΡ@g  4*J2F B@$eZ:ZbSO,(RTNu58*MQ4GY:ߍ6tj݈\P`*UyA1G(Ty LӾDH:&iD*` WESlJ](xUCP -"" !r ir @GDGw):'_JU(!Y啯݂4;k% NuZ(3w-U9UmeU(9;%ѧVʣ'Vd=Rd rN*R' V@`S.ySRVy)Sj}/, @+j3E,,#XpeI+[4;TsDI:MW2smN:BJ yP>:XꇗKoUXoG>h>:pUG%*ǂ-6\oJx/?*ܿo|_o pϛYtҭ8&닑pD]z :%U0(Qy6AtJΤ%,`?EMfv!&l7*b$*b! & %2Ӏ 0I=}N,kA!N'+=IQ+5ZA6g?fիJ;f" RqZUf)4I;D4Q ~v?d~ ?~fH ZC{;9m6JvXY5q(f,0YLF'2T&]J檽;2LK,6LK{^Mvyr (F"DdEw/ a.,weDEQѺCO$h]Ǜm!Y=?[Yu\$l03kA&N'A,8MhroA: ڹ|tLj%o/чM]aU0:fY]Qgr:0bw*4ꂄ.@wS O^+}-{!?xԸmA>0w'$`C|gK]jZ ̲Me'+˞RO&7(kg@l % Ґ:b+@6v\ vRli&&؛AId +n3Xe[-'?3oټ "b|~DA :k.hhYg2I%~|}/= g{/~h"(p r%$@);4:fS6h z]C K>Ǻ043*ݺmPG.+H YG!T|!۔2yXʫJ1"*kER!蔔 RPEĔ2Ͱ4>XP4C)܃yɁiw \0*FB"U03] <گ8PW9ݑYUTɼ]\h `lCгK?_4ouy=U%sKYyz@?~o}' Nj#]ULuD]}VftK󵏙!1L/0bQW#N#F8B#2`+`Gr.Nj|T3 6N^(1ڡ4S`(~LL6ԒD" ZV !Zd*X_*e⨴b@],|"$\4WKFS@c5#vCEv)%.[`x[ITIjdT%gX_W;6E!SyJ:@W6;vIU9ԬtA #tS@G'Z 2<' 0EY}qd H "3cb1#AŇWv9 &`J,[@p6uS/y̥{{=.˵}{.7 ֛;U'&A bȾ}UwźZD}TFh+w:~kK:?qt^={My҄75El[ E1كA(^΁:b^=^2O| N57~￸֕E#竝m!F^c7h4I->~OoƱ;g/۫ޛP}bPb> 6ZE֑f^b9́"w;RiHM1^(+\0|iwRon)[MwV~逤X\ZPvL=|jEώwVTeD;tԁ-p2#S[+qrn[ )x?֘FL"7Q zGO4)q;@5C{EL!O}w$\#+8̑ȻEP89 Eq 8(ک#]};#xotЏͫ{[|߉ գSJL. jRԁAY20*HM Kv0N_"*2pn1vBYG:7AA t NӓoTIσHpnҒǫ6l(v[~?xo}s!mb~u:cl,*'F o?=^D#˩VYLd s@y*T A8wz[jȆ_ >A.ZrUIM@1^r=0Xwf=?X3)($ C 0D€z:[$"z$!<&Kʥ0jeD>S#PHap5Oȯ{[<}\ W]F Ξ>}ݛ_/N;ⴒL]>eO*/} V8_=}SgM}G?&yO6̾)]pI0Egm=P(G&b@*ٰ0tz泪dG(nz~>KV% %{i*#(HhxT2LMԨ*O7Ɉp" H(|EvŊXE`PIGDZ ;nx)~L TmU A& IDAT"AGxh98 A:aM rUh2,J|CHHdhޭ~` uPP&x}5JSqQk;MdQ0UxrQ^_-A(92 (@:n0p s"iY\̄y"+" TNuD`P 8 hD+Rdْ[k{]4'(C""Gh>PfLhDR4J2]Dv~^ۼ&22@e۸ګѢ&mV4Fxsfx787?8YkgM2O{/^ߺ˶FBeXݪum W}>-l|bݭWm׷ЊfjyV.4ťc妔v/|գȠB$S >TVh2WIX'4 [ 6Tc[1+m/;$zGFf6 gX #[7L`v"Fǭ)07,"Ysfn6 aJ6{hf/B@QtlGt9#k@r>$9l!z+X }UC-M:hȥGC[# =v|)[XYی-; guzh\Y0%cAϻTBv2ZAbn h"iSֺ;<5 uPUw&o_%NkdO\tNti\cuJ;. 3RlzxeMӋx#:;^]nu{V q ^o,ֻm2?g<jW^!8Q)0}r;,~,|RqZ=d =^@kyG 9n3fX9O},^LüQh{$G5"n{5AYeF=EeHʢq3BR%L'gƙ!J}F=!_~r[͗/7?b/)e<-ܥ|}ųƖ/{PCKO 7gw޶ a:Z+dz!.m2VYjUK\vIu>_V(Yo|al}̴yp_kj=b ڎ~xtjȯ2`du婀`GuYNdcI6VF@P$6YA{8Ǟ@-dJ#b Kx\rtT|Xr:'߹ f{{'mwbj +,ڬ >Λ"' \- HV@,%Br}m<س2\o2cY}UFE3'֩f,yiJj'E5;7/_jm]|VP) g ?^kL:t&&a}9T2CcweUӆ!:cc#tgHBpȐJ.*,GMnoO&@G25 mY:.tFnPWqDH]V7~^:L3:B-OaGtT5#~E¢l 9 PEYtF,"Ojd Ze$:0cEsNM*#q1ڄ0w,L' OkL Ѳ㍾f4im*4@]h{8o ||ip9ݓ7o-U'bL`v4\U["aU* TXLLȐHa2A3Ay6&ƊMT & !گoZ+]}*wx$ibNM L@O f:wbăhiSX!9U:]%R Nh"z` 8)Ƴ>tr6P0ؓv Vv-!r!0YȺػW3.Ew0|>Gx {(#U(N ЄX4G̊"BwNǫq^JɕwJPj*rƦ-'%@'|Bam*{2i%rc&8Xr47N7/eḘbrsi-̅: McUGDLxvRf]nyԳ*9#'ȳ3 uzL]?Tkw@)HT'[ӆSڤΧsf-Z#kVϦ3| ٱθr4 e8Ҡ G[1kuHIb>M2kSA'&~r1լ16_wZuY>VMm/Xv,JNrFu=($6 R d~,÷fU叟=M/MLH[ hW%|%|o'˔Հ Ѱ^kӻج 77;6r c!U81Rn{19?_?ȅ'8 Mh5Ĭ@w 8`%Xm ~A PoSbLH@w^_zhm}% ZȀS {[k6uCQY&<4ֆP4#S.a5fO*YK@c4¼gƩ*W=2jȢwᷲ|̌6FAGrXZv^{pUc<[@>Y,6(V2e^/PJu$9X2'(ǻFVߴYDU7U_U^/ykeW/L"<-~UM [jVluYhdMi;=< l )U#t6I0n {۔h)5Q5nHB&7[+*o6i+Z=fIJgE}6inSil3"GOu+ey <1LM+bcٕWwDۄph 5FSmᴮj7=*C&/{,jQө۔7d!f=_vDӄӈmU㓀7϶~pjj c{-X6t VUxu,YRt{,׬ I6U@m[Bd@c 2 1$_LLW^^.;Oo?xq~u7D|6WZop3e=9l cNںjICvU7?GoO2}bަ*Z :>xXKqxw yoc!Fh * rX!ց>C%d| O]`8~~\5B#D4U` xUºcrN*e)d$b&\ұcn֖}O8]0i߯6iA7.Dƚl2cmL9q" 윛6O `׭Y4XB0ClujQH^lT%Lj'x21h uCF*e\'f'nKIQhnW!keNŋ--bR|:S"fL[5VlbVp8>M.i0Gi4g&);ėw>8nYȖ!$k֏2,*"/?jշɛ.N3N/NM￿_W˿u?da&@"Es,l 1%GvQrN Nڊe+G:a#liēW]k#, &$$0Jxprnoñ=_9 >ÍȉY1NB T_"O@lM XZth O#KPB@W>%i=L1 P#'3*K wRkCZiGϩ;ҎA4PR4 ʰ1 ʁvx) ̣U^iG'lMMT;]otXKFn772z;6njn:y]<,W߬_jUN=HEVzhn5t  R3j"U-2[ft'uCyݫtey>YusOQNN飷۔0Eg߭˗7s}0W _\?$/:Pjn;M8 ^/c6L9zv_"᝚; );*]FjIЇ,֮؊/SSŜHBMÉ)bo넍c}63Ӕ@f P}^xБHFlVt,, pAV@MN2[ٗW_g7Um8X$c>{[)pݦL[>Z݅&*;AI.\!J2 c?XZ%ymj0SAk1D2qOqw Tʡ&(ݾZ"PSTcoH! d8ow\3C_bm R1Y0di 30{ܨZ6ʵПr΢oqN2Hܹ2u^8c=􈿺t^3?}ZQc`qwu@EZ}jFbf.%FYH`~ @MJ2=F*4^ UYD+2ïPi]6h\b+Ȗ=`n} i9>\C!)NEJ:߬)yF;1%_Utl<<ޘ|hQ 4dB {cN"Eeѣ*?T 1#bPrl3]qabp> 6#$_wHI5vN,˨JZʴDc%t;e& Ѱpuަ!&M 'e~pXŰ鞮W eٙ}Pܦ꯺M?Uf+*DEBPuiՏ?moF5 ^u93kbTfn I VLÙEb03D  +W;yxջ*b%ZcERʼn ]0CO4d @GTupCћ`;Pk >b-Y ьU&4V]MFh)x$94" _ZgpQԍOG5NT泍FaK蝭CGˈ;9?03KR{!o<+ `Z֊|:e˰~v"tN逢G?w||o›y %.q5<+^Sx,NQc7J"~1hw53!KkŇ8Ni\Е!X%+;m#`PvFvggs|:Y`PFqVg!ЁP.ex@^Tv* #UL*Cւ[(- #3õSABvZ\g#(uo/VmU T~>dgϛWO^ӗڊ<zta)f!Np&LnuZ!},&&3c{q΀U& + b,г&O}`X:֮>k0UV>).hƊSC|Cͩ*"j c/aBD0lL `D?,0r E0@8*ôϧeAM`/vS"pap$` &0m.YD!U])0|^NG~ USv|jHZ&u9[<.T`^j:q wVcsCxa.}P$Jb7Yc/vS!Lc;KcG._[! >?ʬtYGHA;"$YYJ!!;11Z>iƿmA@͠@@il2 0BH,L:ڌLfʹf udu-kaoo 2lOqȊ؎$`Zqv2ԝT=o'-.x2W[2?KZ ގZh nV QX^TrSu2񰭸u؃+YVfQ2{`#9`tewĠg5;Mo~-LrJ̬'!a̢ܯmMs1<87oݫ,Kh| IDAT\] )ֳ hX2p̣Eat \"rIk gyIB`xl֑]X :ڕ YH.7V:*axY$7w@~=ޤ9)V!ĵ89N[{̽|= :FTG)͆awi|h3+T k[0Y=ʥ\%>OaQHBRhu+rANJ8 Ob&)9'Unڎvr"JkxN:UPa4%6VB$/8gN@=v50)![ .13^uK%T={>7Yy^I%KeBDp7­CD 3U5{z/zY'xLOCp\wrƲ@XGh%a;~;h%L؎*"jiO<7N D^&uyQ 7%f59hvC`q.fNn85́WzGJXg" \g0o"KȢ'~;ț#!umvxfe{*; lvhz:p79P>Z:)m@^XBAJv#|<1E=d8׏<Լ=Bo!B%}H(,c\hkŃ&>~=t=H+éa.oXVI8%DZg-*֑hq^N%$G %S-B(4Q|g Ёؗ}p# mU2C9% ׶Vh -ë+v9À8dN4䴎\,mN-4f)c~lD"ߝs^1dc8 :`xC&OS@GL/Fzzؖx 1Gt@-'9.X+p9Wa LӘAЌᬲ HH[h!71UlDKpVwQ6Uc1N1z_^ɊLw;D>`pƱɡTGh{+4= C2u3+~Soya8o~mtB[^XZ:ȐAy6 "(hSyCoa0h Wb?S6L]eDB!$we! ;dAXERƠq$*PGЅa < |2gIRl"BMKܔW'."*eHH:HpXgirn<y?ELxf WߴZ&ٺm 6I?K$O&7;C @b pfakb[2=U%QX΁'CVꎏ)уP^f@L^;8З"J;zjV!@ðxyrΦR}(V#H`Ww#2N9j$-q.S޺4d0-fy[`=:` i3cS<ͅ}J̐!io"3Yt|\T/@cvlߟc>3q21\WV4Iq ~qC גC7ϓ'!;@UZduBq8nWVRenv`v$sig qsդ!WYEPsp~6#@װe/lG2dJdgp obf7 h0H% mۤjV :;0z`; 8Jy<ݭpfiNӊ2~:~u{BxduOۡ~_j+zԵM0~. {ѐ3l(68EDŽ O;GБVQ^- Ҳw|T:C RQS` Hnĸ$l=Wo\Zvf|=0nxZ秓7_nFBDI=!rB;쁈!ׁ^уfny41ޞ b5DwXrpq3כvv-Ǐ"q,@ɪ-:Z{Sȱ "ii`Ągld^ BUZ{y./nxP/"kP/HV2fe!r5.ȡ;7 M G1s7|>F>Ha+]7RF(e9@ʞ4GC6'TmAFGeq#3t=|z*ְr7vv+#.];[ݥ9p30P”`S,aB$׎ 97 WcaArX?7h1 F|40(8Q#ae;(B8xXeіƅq~zT,!J¡" r?G,mw5;~wS)w͇Rmy!Ca;%}ŹG +S*6!f3F}|۷;>KM6\fl>cg-dqo7PZXu{pQ`z&Q CАS p-tc z>&Q]I-LkW?hi|w+cC6B8EZ}`9cq%eέddAsk3S_{KF3rBRIR -S:`uP8=0eb#1= 2Iw o_cL= op?~o_`2zsE6^0_9lP!Q?Xmy4d{Hi+GYZ>BwT%oǧHo<>`8C`76%؀衚Ƒ pD#{DX y1X#a \ےAq8b V9cA(1lo v-\I5r:l+/lJL(Fhh} /%MI#8}-mIjH8ED"ς5aҍne716AG/|b=2bVǼbYi} =um>{`7lvxD0gd4 wU_jpRw\n!+UQf|sv)&aHK;\ȰSJLBmwrI1KIAGSl[^KZo]Kc1n7Ut=KRq@[ iIBL g/c0kĢqn'w9T Ffc ۠ːB1@߲Z>{n?T$L#[δofYoy[?v]o/=*I |_ާ;>S+;{j?2ّGcN2[ J%T2ͤ{} ^ȃ;c.y# l_;ò$8mY2] yThD@uyzj>$?pNa4B5gOY9(<'e(Q=T?y\Qcx$îòW=4ɝŰtܛ_<&8Pm,b),Qz0d)mWYvΡ"$ί~.>"ݓ{P ~%=`FN)ڿʬ趀gYgIɎgD]#{hh)l'?0=<7k6# JU}$ &kGF5qE6DLbYMJ tX#©~oZƐX}yk?a> rCqKw ~qTM@Vgr AIDzrseZ!@J7&)>lˠ{x8<1W>4qCK0Pˬ 9ϡ#11$^gGr%C wl^u rUd|;q*> ]lXQeÊ>?I.w`&~ӧ/ٯ~4 u">}ǷRwDm~ێz|㶴b! n˛\r?ouCd&8zG0 Ai@ dw=۠8QI3?; F;X1&w:t<{,d$TU5UA}k5$fJ R٬{UIUWKdt2 0N Y>6ٓ'.- $&Ms~q4v-y3H8?٬/_ff&k3;??|>???7X,RJj^Em'I|6m+wyg>o6w/?Z'E=??]ו?R~Ue(}}VA0qyI a7&^ +"x.pˏ˶,q_^^[C||~G~[իW?aUUO>~Bxw1g?fO_^^Snkmjڢkɲ < iI;0 L{KBbeIa l0%&I:zm8{wojc xt~0xh4>~Rh766^xÇ^W*ɉ,1 ϟ????_[[z`0(JiߘRyTVuyyyޯZVkr#Nu|ppÇO2w~ӧ'''S_ZDy ΍hh^>T` l`Eie(= "8-cevURf[FXl &580nC88˛Tqi`KGP:<ɮIJjq`$dJƣ0vje\lw5 ѬXWX,]}:*W~>~Ó'%RJ䤲(T,a躮յ66RR2rz8pwvKE.(@k}]%ddnW’~l6ӐeooO/FѐgmZr4vagϞnB(JE`0v͎VhVZv]իWR]Fl鯖ZIxl1XSBF N0"{R= +?~=7q:spa,]x5,Os lvi=K:x&Z '[?i,TT&64H%%k()Ȁ Dy\h/WTVUinlֻ<5[PTn/.ykۃ~uA[}$`kk3:u—/_a8N⫄ a ?~*K#l86ƔJۑL>==N @M8svvc&Vqk({ .Fӧ_3:iU:8{=:J"_)U&'h+*,ڊE0F09j(ӥ@vVcr":!8NfMж ldpZ53=nxl)-2܌z,j9 = V(;Ptv_^wϞV{w t/_[41ݍ(V$ϋ}ZrZ^^\t:BX(٤L{֍u!syjUf&T*J%UAy׫V\.;ϥnUXJYծjZcR]9!.K@lˀc{ٗ R4LYIUV.5,k4GUub ?De7;(/Bp6ϑ\1:MH,3Vԉa" G^nTlnZ-?\7ԫ_o=6h]i>Y~tIOxv}vvvppj s8Hjׯ" fƒu9"i”^}q0$Zu>DIDATxx(T$ջ_dTPd?'@xϟZvvvE o޼9::z|Ǐv;{<նr_P L4wIV-a1 u T[ QP2u(D]SVD됎Q4wC[TJ(VĕLPp&"Q20=<^q#‡*WV!0I&aJ}(RyϓrDBaڑzA`0H oIT*Jfivg,[p\7"caP\N)%}T ARuu\m4Ba)nllQѐCd |>DZ\sX,F#GQav8NGZ!u ʳSW0giROiO><&96%<8D9uS)pגj O`"\かb΁}H0!*phDBÚc8ΐ)GWbrbkQ=# |RHːQ8:;,ӝܥO߫Ũ޳ln!4I >q'1`bw6xa,s/h#.@8DJi8 { 0bá!g;frzCY:eъj"vxT֚NlJƉd#$d%V} #xbD]y nϳܒfOsNN҃>{08t`$׳2ozDq K,9bd&fv)9&d( 2 &=* @ĺ4s Rj^rvU3v)M%L\&#^~i^`ј_#X ʘ;_8 ^ P<&B/J{c. ԱHfVN16$%ˡN"6IENDB`DarkRadiant-2.5.0/install/bitmaps/dontselectcurve.bmp000066400000000000000000000003561321750546400227330ustar00rootroot00000000000000BMv(" !""   !"!!"DarkRadiant-2.5.0/install/bitmaps/dontselectmodel.bmp000066400000000000000000000003561321750546400227070ustar00rootroot00000000000000BMv( ! "   "  ! DarkRadiant-2.5.0/install/bitmaps/dragresize_symm.png000066400000000000000000000003701321750546400227300ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME.2IDAT(ϥSA0kssC6CHᆤT0ƑI앍3KYgԼ'j+x䱇d< H琗Xydr~boUM>:q;L/ĩEIENDB`DarkRadiant-2.5.0/install/bitmaps/edit.png000066400000000000000000000014431321750546400204530ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME!"+IDAT8}MhEDm 6M7MJBChk XЂm`"^{PGE A{*Z`sX#؏fM`1!͚dw޷Fa͌QUppo T{(" X,dOS)brKI5Ba_X3;Io_c0EnC]?Mi;H` ^.Γ~r)ȍ|#R@-(` ߿F1gc~9mcuul1с_;X2_gxk(#M,ZϛCmq'atzڅVW64#ngu;o)ZW8A!?ǏC=yW1=&@?P I9.D5CB훈\+ C-3APCs. 0މ>z \DJĈJIt.]%Nٹx i$F-$ԚpZ4!.֒PkWE3(j$*)㿁 Q?DLwp7 D_w'bRsQR邴kw&K<ʁUhNu]VZI ުW œpM$|#qm Eݾ5tajhs.8++ 7hJnPCyOM2MNIENDB`DarkRadiant-2.5.0/install/bitmaps/ellipsis.png000066400000000000000000000002311321750546400213440ustar00rootroot00000000000000PNG  IHDR &ފbKGD pHYs  tIMEZR,&IDATו 86)lX~QSn#=IENDB`DarkRadiant-2.5.0/install/bitmaps/empty.png000066400000000000000000000002331321750546400206600ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME 11 L(IDAT8c?%B0j 9.4a)qMƻIENDB`DarkRadiant-2.5.0/install/bitmaps/farClipIn.png000066400000000000000000000005361321750546400213770ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME,<IDAT8˥!OQ5j(H))&$Gb@4%mM˼;yMDuQ}0c?7]^ە5-h \X^ߗw-vhC΁WVߕ h#2Ms_RS/;#.Zm"[hٻ _HvȀ,?AUT_%WjIENDB`DarkRadiant-2.5.0/install/bitmaps/farClipOut.png000066400000000000000000000005551321750546400216010ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME,<IDAT8˥MAD-!9H!X X6!ME8"0p6tg[Ǎk_Mb k h6iB"qT!MzUzkk@+ ̢AɥePgjIkΤW`W`3ƖO fN ` ,{k2?/b@|dfsIENDB`DarkRadiant-2.5.0/install/bitmaps/file_open.png000066400000000000000000000003721321750546400214660ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME+X~IDAT(ϥ C{-LS8tY-Āf`=\ճm<.>8*GW"3l1[ FXK0*}b)H*%RRN,e1O*bEc0!{= ܕ4IENDB`DarkRadiant-2.5.0/install/bitmaps/file_save.png000066400000000000000000000003351321750546400214620ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME13jIDAT(œ C2y3PIX—<6H fZۑtl{rP+FAO/Y RS4d@Qfld%U7IENDB`DarkRadiant-2.5.0/install/bitmaps/floorSelected.png000066400000000000000000000005021321750546400223130ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME13IDAT8œQwtIԄ+"ET_BPDdȊ\A9V ǝq7aU%ĄO\ץYqi mw V!V|JW/fD U cOhzH L^Ƀv0es,HkG23W Q~qT姕f"qvIENDB`DarkRadiant-2.5.0/install/bitmaps/folder16.png000066400000000000000000000003471321750546400211520ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME+X~tIDAT8c?%R+zaza^`dd!rƫh lŀ Ij`?)Q`,Iy R NgPg<<IENDB`DarkRadiant-2.5.0/install/bitmaps/free_model_rotation.png000066400000000000000000000005041321750546400235430ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME*_NIDAT8˝S <,d}=A)@>()!&M{l͝@;,((, _Ĺ 9%kZW`T0)`$@Zݼ_z 3dIEZ5188v--dw׸!5n=i<~MwIļ8Wu"\~+,og"-LզHz/IIENDB`DarkRadiant-2.5.0/install/bitmaps/grid_down.png000066400000000000000000000003151321750546400214770ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME*_NZIDAT8퓱 0 ֠`1)"Ӏ@p*$7ⱍm$EҋmF.OhOYGşr#I6z&|bQHIENDB`DarkRadiant-2.5.0/install/bitmaps/grid_snap.png000066400000000000000000000006111321750546400214700ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME*8bIDAT8˥SJ@]C x,o/ iV҃`ORbOB Đ>Xvyo$!"1ITW."=P"c{, L[q9% =coT#s̓ʵ]t@)$2 j HZ;n*ʙBv9PcE.ʎa@ȴph&¼eN :W5ǼT S |,{!xaр| 8IENDB`DarkRadiant-2.5.0/install/bitmaps/grid_toggle.png000066400000000000000000000005611321750546400220140ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME*8bIDAT8˥S1j@oc–qV)\R /!!MADQ&E: <0p.H2hxP߅]̲mQ!@$SK@*sUHBp!&f:MF3x|GFH`/^D0 X;Z@o.{G-M]綾tn<0]zQ- mkKnTCWW^H^P7IENDB`DarkRadiant-2.5.0/install/bitmaps/grid_up.png000066400000000000000000000003051321750546400211530ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME)fV9RIDAT8푱 PŞ!X9D/[8FCN \^f5ZAMLyrdQM|_p ߒdƞzIENDB`DarkRadiant-2.5.0/install/bitmaps/group_selection.png000066400000000000000000000004071321750546400227260ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME 1$aaIDAT(cd`̀e X `JvAb;0@h5. @c*#*_?6UL ۃi3~`Dp?C)y"b'8K#  c/ D8p̂q&[22̅݀cG\5!>֑IENDB`DarkRadiant-2.5.0/install/bitmaps/helpicon.png000066400000000000000000000016671321750546400213370ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME(Q/DIDAT8eKlTu<:mwiI &H+T4Ѣ1.MQb|FMX ՅM$&.4X4DЂ`(Fڙ-{3ss]4MY[XQQ̚#*.0;5;=?02,Vʺ;_۵龭RSbXia^ȑ5 ?>b׀9w8u>,2 @RSJC}f+3s \tc \_I]]{;1 n%?1#=Mo>pٮu`q1cXV_ŴtBWvN!ո˷ H,^׳OڄR9Nҽk//p!RPW{'y;Ci*^;V;:r($`1~A$ pC<'Z!R)JUmJ(\Yn.!R <]}l=7],PUquXDq_y)ɉüM7݃D Sl M,̧nS K FUSvyw?PDyKIL^>Iǣ"`n [k d<7~WCe*gp_/_ezX\gn~M21A.c]Lu tбm_4A2 em?F, NH.BY oYf7jTXKIj`@ K Sٽ@4.x7kbzP/9102SnIENDB`DarkRadiant-2.5.0/install/bitmaps/iconDrawBBox.png000066400000000000000000000004341321750546400220460ustar00rootroot00000000000000PNG  IHDRH-bKGD pHYs  tIME!h IDAT(Ͻ[0E`Ÿ~wpO@rHxNҤwzj*aca )mS`vEA0 (5?XBhmuۏ#듎9Y 0=1<$af;9keC|Pᒺ[G}/kC?5f6IENDB`DarkRadiant-2.5.0/install/bitmaps/iconFilter16.png000066400000000000000000000004711321750546400217730ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME!h IDAT8˥S0k[0n@WKp|CMACncpI]0q["aI$">mPO8qߚ ( c)zìHplɨ9r4]K'o"%/(HB ĵ{jsn?YE>Ho,'iب2& ZOIIENDB`DarkRadiant-2.5.0/install/bitmaps/icon_ai.png000066400000000000000000000006511321750546400211270ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME(Q/6IDAT8˥AJ\AoD(DE) *xZl$ t6Ǜ SJm_:q4|Wm=bf%>F~#{2-1YWmh~$<5j4EMp%Lq׉Cm|O$]\|=XyLNC9/뵄V+7Յ*t5\ׂm ps|(9S5b%Ww>oq_XLsZOjՐ*&m.(9xRXe8CwIENDB`DarkRadiant-2.5.0/install/bitmaps/icon_angle.png000066400000000000000000000004601321750546400216220ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME]?R5IDAT8˵ 1 EOHT`Jf5Ґ RpeF`*zDuMBcKc$ DM 9 -!De0;͡A'܁)D\PpKI@ 0rDYg̀&`*mZM\&)jجSV+LJ[h2Sew>IENDB`DarkRadiant-2.5.0/install/bitmaps/icon_bool.png000066400000000000000000000005251321750546400214710ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  d_tIME7_IDAT8ҿ.Aߊ(;:hD e[ƭ% "! DFb-T4so6{s3g'Pj 7wr|'E/-|g.q2az/)3_~Ӥ vp W(Cތ۸nM)&ZKna9Owت%<ĺ[[엡o])V+)>n%8y IENDB`DarkRadiant-2.5.0/install/bitmaps/icon_classname.png000066400000000000000000000004221321750546400225000ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME'2>IDAT8˭Q 0Dߤ) R/wL?jv YHvvfdZlaz7R\"W4I.%6vZŝ uюqSPrXw pqq 7JiޯW<3}Rܮ^Q9ZN3ӿ '_Itw>49&6W}IENDB`DarkRadiant-2.5.0/install/bitmaps/icon_color.png000066400000000000000000000003661321750546400216570ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME'2>IDAT8˭Q 0 D_do z=۟ RjkLL*-pP!7SG31gjz!HӮߏ^p%MG;P{04:݃.7Dcً?OfIENDB`DarkRadiant-2.5.0/install/bitmaps/icon_colour.png000066400000000000000000000003661321750546400220440ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME&IDAT8˭Q 0 D_do z=۟ RjkLL*-pP!7SG31gjz!HӮߏ^p%MG;P{04:݃.7Dcً?OfIENDB`DarkRadiant-2.5.0/install/bitmaps/icon_entity.png000066400000000000000000000004721321750546400220530ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME&IDAT8˕ 0 ?GLGG-@ #nAG(]hx4KrOgDnXo !*`5Z ̞ fnZϡ.sXo% x BxH\EuuH%(a8,Y5Ƿn"ijV{E19{vDyj]y`6csAB#IENDB`DarkRadiant-2.5.0/install/bitmaps/icon_float.png000066400000000000000000000004731321750546400216450ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  d_tIME%oSIDAT8ұJCQ ߵp')8r(TЩ];"}7R8NB>@G7p#{CfI KHV&>0Rŷꐆ裃ʜMf9n3|YQr*;wHM<~X"kpbK.~.Fn!}S!PG̲ 'bQ|%zGH(*qhNIENDB`DarkRadiant-2.5.0/install/bitmaps/icon_int.png000066400000000000000000000005011321750546400213220ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  d_tIME 4\eIDAT8?JA_S(m,=*)ǰSZhr/^~kfx{`9oR] gr|W4-TvpU{` )e!\3{ZqB5g,b/,m\Tzqc-%m+W]A]!t96o-Y(IENDB`DarkRadiant-2.5.0/install/bitmaps/icon_model.png000066400000000000000000000012671321750546400216420ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME%oSDIDAT8}NSQ>chbhL1 #&DcG$h|}'N8Ө( abb(*ҋ&p[_GL4qm&Wg X.Ħ\X}E pʼ~8ϻoͻ<dN TЄVPK nl&"\rTD#dG3OY)jRnךU.@C~{rujM|%Ujm PQ%an9O{1W]d6#nX" R(4m@l>K>1 iӐIJB]KץX^O>k! ߶a[&"8yTN0#bw @:KXlM צ~x9UPkIENDB`DarkRadiant-2.5.0/install/bitmaps/layer.png000066400000000000000000000005661321750546400206470ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME IDAT8˥@E,P`ebiCl aL l6c"Oq-63Ɍ*K`D_**n"e4M"m;Bڛ@CS<9Vy^{,&unG$}1f~k^"Bu4M Y@UN'Dk-^#n7( <8عEQp8hoar<q!o6[.:&Yzw4n^0IENDB`DarkRadiant-2.5.0/install/bitmaps/layers.png000066400000000000000000000006561321750546400210320ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME ;IDAT8˝n@ ?'!j˔}N}1RĂZMu=:fH5X:>۟~|(=LU iy6q~ S'^lsUFLDQdBU[;ﷀ*e1tک`ݶl12$a4QUX,(˲XI"Gv"b8r`#"c0,2Nt:l|* :?c5A <4M) Eaǻdް[ͨ|>Wmig/CxAq?% '~~`[3IENDB`DarkRadiant-2.5.0/install/bitmaps/lightingMode.png000066400000000000000000000011451321750546400221370ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME슠IDAT8ˍkq_WWE/'d|PCEнekCKPcAC)al+Nj,e˕|<<7痨*ÚT`zF\XZiޢRi*e-;h*M*Ɛ꽁 ާ{àUu%v+K|,;_m[mV`W I!AD.7*mT{뤛fýG&dh u'\n1F#(Yͱ3gNs5yޓk<__\P Dgĉ `]1K;-Z&* _ t,S qv6Y۟0{xJqح5#(%ݤ~fcއ"IH+IcЁƳߵ絩öloQL kYs]`׈&$u'] +&~q9IENDB`DarkRadiant-2.5.0/install/bitmaps/logo.png000066400000000000000000001002471321750546400204700ustar00rootroot00000000000000PNG  IHDR;LbKGD pHYsgRtIME슠 IDATx[eq>>s " SXER7=8lؖa H0H鞾ޫ*3+k9=='ΜϾUY_nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnv<ɫ  7lO{6?̟p VZwwɭOn=s?c/G""JnL ! f1CD`63B4Dt+*"r_/߼ry}`b飣/ϦbAD30CJD D[߫k_*_ݐ*Ao?qqa=~],gg^x"14L11ljHhj,ffffDz2TNIEJAUPMXj)2Y)Rk!BUC@$@d1ՔYDOnD䔾|7[SroB\ C&f~("!2"!bBE @0DD4fb_jO?='?w? ϗ!3'!I LEHU쳗W\Hfp||WŤV!qR PrpyR9gffNz9s"U:ֽb\,.VWWW)e"‰J5PE^ w^]/?ut''|t_\E'>|H""Q8sBtsDI-03#@G 7|lZO_2hU$bf;_=|juyq* 41"*-/FUEL@rXD)} #L4.|?3 Ժ.8T13SarʀCNfXsbf@1q1H̦vvq)ZTTRDIJR9131.oy5?S#p||쳉 ok7Dďȷ(fdFѰ 0! d"$on{zG>췿OGV9Z%$jr#" f\J8>TjULHŕT)RTTT-qCVSZ8,3>̦Ry朆a<;;+k߻wGrh?O<ӜX#T5FfFR+!!x~zH0LDFZ Cb""i1TKs\C@D*&"8䁉RN>g>g~k_ַƗ?}px|<0As4l>^5P-߸f23CQ o89YPi 9A9_:#D1@ikfYL7no?6;o;;L)q"U55bon> ̔L挎a l@D?0 80sHfRZ-snf3@sz3䁙C."2sRĜ)8DDfn<Ɖ}#ۃ3*""UN6L_b B@$7^/?Gs}yqqÉ88".nػqԋ6#"zy k>jsFӈ4 D{GG|uzq\/?Oyޛޮ03 03PX{xU$&)! mOIMDy18)q\RJLΐ33KOBSQ{q۷NtM|?{||(UԬV!$`CX!;fN]<!%N)1LI'*"D~ʙ ))8V1$"޻[wC}'򳯾v֭}tɃWd ρ}A8DDsȆ>-'px ߺ)E1ё]<񽽊8,sohBH  +` #ᱰMRًΨ%U31?>&K9x>K?'_/^^]~//W D@M4 TR% $2"ffRTMi>CRJ9e< E-̜+`* jDÐ -qIUbRݷUMUn|=/0>q;!1 CYUkSRUb0#Bf81[ץ"s`3H #qb00@)1z@Cloݹ{:_ x^ѻ9:=#''U ƯplXYs f{s2(vVK<2C7@σO;_[LSQfd"? ! TIaȋrǜ0<Ÿ@jZK-\2$0U3/SAa+!Wk0P;w0z^{!?ˡq\³{82 ǹU+x"3P#b"f&4PQ+0"3DL(-'23Brhy6#*L)1=|7KOӟ{7 f>{|Rɰf`; "lXj-n{|o;: J3!9x#J=һ~y-trK7fE,bDM0̦ZT戳mQ(@?ݏa9&1x- !TΞwYTя}坻oWSNLD'P3,8Ki̙b1.0sq:h6t@ = fbNfx 3Ąd3X 15Cd|~prq0/襜`)V: hDdcS2 ѺZC)e?oHc8"wF^O5^+Z,{< T__Dt歋K3U@D DaHCJyR8cÜ"ZJ5S@2Z*`*U44`i~FDLٽ ǰ)qJiaȜ91y)" 33"j~͛o)ɓ05Sju~q~ԱD2:@A @+He|4ժHNU[  hNp&圪 $L;c DjT3Rp̤VNW/Gh]">g@ "MՈ3ոE㠤1շ5D53}>:v4-".4'_NcTu_Q"Bm"&Iv@NnB8+l|*ժ:'HnlfUT''p꫷/ý @DLc53jjy0Zt j2?px~ T u3!J**3`)e>1%2} 1%FR2bRH4 Lkf9wM M2D EDKÐ9e2DxV9MČJ:<T& 94#UR@!"9%5uCtQ23"o5W2N@djGKN!fH(6sHڪr d0Iwa-^ 2lUu,IԤUdyLzoOc_RֵRT0*nW酪1suG1,T0:֫0dGJgN* &lnh 'sVG}/*(T% :֕d9$RT AT <Ґ1R QrB$K0AS⏂:x$54 ϹYDf1LSCBbHWwnWjEI˞}xgC.r{S M;mA3EGWHt"9]0U`q^{| #`. -%q7GD6'"<|h=MCJ)JU0̦DHZ 0s^UxC`[FECyP߫zNWkM<u*s:)D\R굲SZ4,M)'3S1fQa9df~0!qrn)1rJDTjN&hq0>%asN6x$ '4}Շt=LլU*[/ IU Hӭ9DU2ZO 5~Fx!LuS QT3/ fh @B4U;9Q7?_[/ua&AS+HX/q<&)dRՔHcQT.<kZZ1!Qb)!5+sGg LLUDUWWWe*ɼXȫr8 zJE@ @Di B&BR[ Bb┠fʉZV uĹ hP­:LYY3 k}۫?aqx# H)u((Jo<31 &v$ad%0 C2yp^R"wP84 )%RUъD`8;HLL?*JGkG {HmkHjwoHo֩sЉz}NX]jTюam|9GE C"EPM 7XC+Ď.a4M\jAZVSJV5ĄT#!FYo*=ݲ-3)2Νouz9ޜT=:4  C `g22ZLqbylkWSq1 9qëhE"dzTKU toLm\43;"3lL߬F͉#i0Ӡ37H@Tzlq/KEU1{"hT,U`DbVUO;_3|q<9NJHð`*ruU00aJ, 3323hِAZUEdF*3nfU44a&A)L@UTj-R%"2P A*DN~4MzooD@ g"$I՘TELz VmPegR j]JhZDԦRT8"'ULM7bo@:>q$z)c89:됼sƒhj NӚIU``QxȈS(k6;\Zd x| o>ӯ鋿Ezd}q 8"W/EeH>MMeDjS)SZuFUIJJ+\9] Rb…'WTKUTGF"A7(Ujy[SU< fce (sDNp޿&fS!'V `Ɉ Nv򽉞 xȻ,!lFzqڻ!l~4{շd#"^9YI\$+7 ) oUqbg :ب+80 `(9n{hVĻ̼{ppWη//ox4Mܐ&VQ'BČVU٭ S)Ĭjfb=À*)A#?}::((U۴^:լ;qOG3qx$[Og";\144+!NqDzit̙S ڜD-%g:[t'sc9Yc8gX 7D2Yאem>jeD45m2O;>vBe#iicvp5>'{6Ionl¬WuH 5,(S~Viy!񏚉jbv;[WO8Neč8Ss©2cnZ;~՞F{5FUc^bu&q t~lkfTv8z#>2W`|Fբ,0J@b^V׀(t4LJA+`ߋMU%!82!sFs@USs-uZ,X_-Y'Ib\pƱ,֭.}~>]c˙To]^tJ1W41זt!xSlmdiZV(-hSc`:0[hf"uOuo]q4z?L ʽ{uf_=o6]k{!(p;oU۳AkqiĴsU5c Һįj]R9a3Gw=sy6ץϒoD<=mHTm['X 6I9̹fg.ޝRͷ+Ӣ\`u]Mh~v| xVFж-\p9{?HwI"ת:h\N@R7w>AÑx3iڕbJK6}|fm939l{3vG-ҵ=Ia@:ZZM|-)(t!PZݮl:=Q(~h1ΰc91Odh?%#. ؅~;oЅ3mKvuE,mp-Lag5xψjC-":zX7C.)"vu ||T#FD mNnCV7^(T֨]o /^рn__%?uin?hǓ׸vvڍ.32tvLм4UdnuclS86JWXފZZ>dDlI=DfՖs[۝a:H@^a\}nU"o%rV+a@}C /qm"9ϧ^wZH⭳e>|fRC^?x=yB\0gD m:pmЭhz!> YH`Mmj蠋3~U+Wk\I0<"Grg]Z"ŬMb%tSIoIg4U6C3^d `jF*1h_WِM͡ya>FXخF8xM'hWjMpܿ9?T$h"LZ#]?msC6$tYuOpE F°NFq68:eXWxE 2AўIG-"F@2,bQ{DZ,xυޗ!;yV;@p ^ DO<:AجNcbw޷c1<ݳ~7غ<:PCifHDOQ$ѐp3rsk3f7/βqG\=C5 o7:=6b[+wDk]MS`Clyvתz4yU:z{$"CUf ®zg9mѶGZ;x x@:hgHf3["٬_!oUF A K1‘B.T1BhY ص[;yw\s f9bvk=5396أI:zF pV6iY2EmD#Em|xvT YmrѢ|%Z*HۏثlM=3#QjZ#HWvHh0#qq ,kڀH8`\l jV* g=od:ze3ΔAfnMp^kXחo3NhĶKp-ٓ} 2͖*!%N?_F׮<03dv&TbNroܺ}!soiRݙ誛j'y_Km4yDŽNY燶 :co.=m?Q' {hFXө<(]TՑ{{ %L_}CZk-NՉ 8֘B#Mƺ)"?G`#3.jVDVYe洴ƽV7rU' {M3l{ƒLshܾ1]\\\x6/HD~К?`hD]5Nלity?u;}wd K0foRSR[GZ|SEÍcsMNW}V2+޹HcfU^^__^{m*:h'$mtg6Zt=uzH@u ޵9~^+7&GFfkm.lsjm/@4F f%8;~](NG bBw4M Z:ƚjg]q [釄 P4 A|\LP!f5:[T Vں I Z{?T8Z$s Q+lV6Ft1$s~m}so;lSl=qyiAk%}E{B πiM2.&DH4YUW:m$dIf-GoޣK6kKE֟Ԉ<Ռ,ՂLu q@MBYe.VA]vO<ӷatR30n)QsJ.o"dvoPZD;@/Dѿa(f};`kvJ"mp@[<tM<J4~.Q8n&QE) v|'qrpAPrp4SZ{#-0bԥojUDx"~x!6z0ӻNx !m[]0@Y!_ڱ#S702jaۨn!6ÓT%]yق[h}ö́z@[8"uf }pVPmKԦ1K_lbz륚UKq1VJخp@ ê5Sne]6$ t ]^Y8y|q}orNކ[u,RnQnMӥ<"hp}-ysˡmAuɖtWVk1<\_q!w0:ߨ;(Pw6z?D(QPDgvJ av4^sߞED ,S~Mv^i|L:353v[us{Db,ӳZ,BZf `c352athҰ58ڱ#Z[NQ}PJ_.z6֧ȭ+;m{f%``]hVbx-v}N\ v@bo>L{NU`@I3[d Gv}K{(b0rz[6- ykݠyH錷1Tt{{z xhثqoh7`wD'h9RaA+wՆjw{B{7N؍gHQEl35&1ߣA&vHB1qVov/5.H.]`0tk܇N)4 >0BERdd_cߕ&w@X%Ly}9jvZΎ iB}fSSTlC)' 8[{[īۆNI``(Hn^Yuw?`/wހ&6+rz>6"4v歠1umIʰAtmԅnH뜙zVVuFFRh!@ IFko-QCvAvvΔZ1݃~L4 l:8Mse_mɯENOZz3xW'RSHUu615q#9Ewkg9?0@h}G5ԋӉ;ٴ*@] lv5"!5'A78`~{Su6vBp U[}QN o PǔNgMMUiNb̖kaB|~ߘ^йD!>ɴ[3t-)a_64D&Evw 6A gtMU[Eum;D G7S` uI$Oem3rfkՋRaN^r[jS 1xr?tV0V$gڐVN -0bo=FJĢop?y8_ϟT0#3ZfGZ!a)eZO`X.04 T@)!ׅJ!&f)1!R`X*L2) s"&fÇWٳ3z6 +ZS r!I1/Ip$LͤAT%@LPPoVSN4 ƔƧ|RRxU4=XGtqrtv~T> 1VkSTL1T],&T뵪Vi]TM"rH0MJB)R~uCW'?"LH/.VᣣÓ{Y)D`q|(塔 ƼTŋߤ!I lZyX,TLR&f" 8::d&SZz_(1boʫ8uyzZT_y%$-﷦55́oW0悎>Douc!J8^C >Rզ½ٮ>Pj;wN/pxK/ >SWպ:2#Rk3@RZ$Z񍧟Qd8DTfuj=bH)/Wy@ EkZ*1#jr cJY-${{r_SN,Uݼ^MSJ"*Uj K04ӿWTrӸʣi*92 DLY)'"e^,y˽+WjrRFg~֭>wuggr9WU`(q'1nG Jclc`FOm}CΰXU?߹{l*/3/<uzgg"b-DVRk> wRFH0MB7E^]EϧiZW-RU+sSy0˕C愈ZS0bLӪJ2M -4-{QIs[ STq Y=Ϧ/~b^K"rttp_o*#t41AEI>7p!0!#!wQQ IDATZPT8<՘ Sq(abDBB&_HLt{{ѵrJ8$Zz !Ӝ2Yi>[%Jh)Tz}.9e&4RQ՚H4dE0aJ33eC BǑG~D; ~J=}| `:xC܈(['cۻQrLom혻M믦 41zB t{Sy{Rk3<5 "11-FZV"Gf&0w㉈|1 *8DH8 ^fBDTJElh9"34%"L$FLt\9 [c:[.zr !TVQx>Y,"f 30LTQ13J"բD@DRlDa"1|y:? ypxg@t8 @+٘'zuS{ <6+k Jh3kt_N@|HL h42N(Rhji.{Kyg}`8M0 O=0$)yKNJ3L>DZ \JLdfLLHR DÐaƁ qvH* `Z_^\^V?6>'sCqG")!aJl{$Grd .,rH6GfGdWveG`(۽۽͙%]7P23"LUA,,2b1F D^"]MU=Ge%<㐤!(!8F2K) R,)^ʄ/k j*XW9. #A`T"%R_ooJ1~~׿UڑF+M6A9S`j V.K,Mu9zuWDf]\fPJO?})UE!FNľPD8& D->0313n6#QLs T#w}~Lye50P0숢y: ? D0rvyzbuYSLbbV , ) bCP:8(3b  P|)"LhjQb ay9&Ue y6]^)uɤ5ɵ5Xgz_*t؆ކKٛ&UbmlWl8;U^|5O=ů8C?f,²"%  T-Qc!vٌv)raPVT| օ<&65S P LΌ o-"㱔K}(r8ͳgO MpFĢL@B L 23i2J,K13fL)q`w$a[1!Z`1z7Kn8у{_DZyҧ~m3RH S 88!aHC1"cl8)ES)9*""I)BD!rH&".ckYg"fD! z0"w"ŋ-Kb]c A0!IQ<31&K)"È0#aC9/)b U)0r*CUJ?="!?/~tǵ=W8N,@fhM0{5t=YQ'8g)@J)!RQ3Eg{dSSEђZI!1º' Aë!k VU"fVk /%D63 h_*W_ID~3"W_~8oT^;mѶCm-iI#4!~ޞc48={* +6X}\_z9Mo?g}lBRa."P1 D*2ϳNjLK.cl͕TtUJVQ+Ґ a1shb.jXJ^bbf H@8 D")Ƶ{ 1HeY\\z3|h!002߁̤a[jZ.bMļ@4R]$A@bORL_|2,Ͽy7DX!V9ݳi5IFK L>6Bc]ic1'DQ:wˢ"]w8|٧ÐE U+y)v ga6H2"JЏRB RV1(ҩQ W)1 !oH!FS H` )e2!SC!@BO=m+&6pC^SU.EID*Z* (1ZyM`\cĈ`h`R"FU0]\c>$vJ)tnnb>R̮C`泖|םۥTe]#dvڡ&OW0t֟Zk/{Nk7@yY/nA4w~531J @b )Ey5pW|C9@S@)~=T˪]7D}p4D4DHK)FL}!EDTTE4ϟ!gϮW'OL a8\/m%|\DU̘ W%TX#""\ /9QC"~+Su F<̪'f@B@ؖԜV,n#B LsiV@(`@FH:D mZ-"*˼$j,3%- s8 @!F?ɗo- GsS ~ۿi24y| b〡ZϝBv3vcfk]aMHkBm@: Y*Ī%wׯoE*߆qQ ؊/KfqL+^!ĘRj*v)j%B%DbyN"Ðdq4QH`99!b- DqU!ȀLy)"Ts{9zv͈Ucf^!p̅BQ$ @V1TyQQ@ǖAT!G xE7?vAJwCJ)Ԫ֨L"> b4=4Ezv{Ijv+n^镶TWŬjQZ;|q{{[4t! 7^J1A)b`!o!y@<"U=/9e-φN.W_iZZ鄅_RBww8q:(""_|՟>}GOahMRJ+=%΅ni1Y^@J!pw1h1S h!q!X!(Si Bbf@K^ `yY%|ys8ݎWf}9"9xJ+O P!މ8P)j\OSS( p5VPSG8׷et:/x,rŘ\V¤Rۺ56 LXMN$R.4wd8ѡS[TY@s.Y7~}/R'8T#B#f0Bl7}d벞4U/hUQ !0t 9DTΧK)_4!1s6Rh3Df"$כQU5>/~Ͽ Rtṿ:2 ZWӴbh,ѱjh}mZt9\c^`Y4zK4|uww,nYO)?/_wϞ=3%)k6Dtl}PTV0FD}0Ky~aTfRP 'N|5T1(j<bB1"2˗ȉ1<}d^_Sd"U p;ŋ+~LAkmDL,ET Y9+؄*"R;f` 8NG"a])6LoKߜ.lqdi)_t r)ay ~@31/^~W'CD'7L6x1cED f3l69=AC̾E+c:EB^MHL1D)grD$)G9r!=}vﮮ^~ǜ~?y!2}]J]t}L`^oX碬BDrhʗVFǵW_oq=dȖyd.Χ)>u8Ps z8"]0ԟrT:,_|y{{~_wI DޯdD 1mLَ&$9ɝT5Gu68",nj6)8^xӿ*-jE~c&q0F&j Bu0E!0,QUQb߬e 9 f"HT$wO̦iq@c)?~mC(M[fC.X|ٜ#pT0sVf$?Ry1~ϟ?rH\ӎYBuT ѐ6WfTJjJd 5yz1@`.b`SUU.M4,s)?Q!c^NϞ=կ~1 i۪2^Vv2뤘n288Q43-1bYq:3Px-p8|sU˹NNf0~aQml- v`rѴv;0j5,16AN˯VNZVfT &6ƨj_jj:t,AYT֭xoBϲ]*m|ZFL*rի"neָ,rw9?gO^x_ƈai*gΌ#p߆g=90sy50D8M<͏2ff"ѯvF^KTځozOiOICG.r,ww&?o0nf_ŃuavDEv+%m6]2L#EEz!gϞ04͗Pez)=']$bj|%!t|}\y1-D<ͩ@וr`o f}ҫ8[oOYpg":>^tD1>BY~}ןny7@-^`{ioΩ?p"? Sӿ"%#n|olm4M669 ޏ6DLD۷5x* ;vMy9.m'}wyA`{ūW!p>>>>>>>>>>>>>>>>>>>ޫ_ J9gx}xϯJW/ZCr^V=T vb{z0LG3/x$#'`RG#~#6 ,|R0 SٹȈ b9gbU[ <#vE'>pg*3uWboR{m~gn_}2]=>o }hFx ͪ[ϡp-&Uc MD}Lmu%92-Ҫ 80DbP_R q1UbJ6hY`Z,⫝:}#Uo~/TE$8MR "yr3t:){"0!&U,Rţ6C n⪤fz6p;B@WZD(˲R\]]tiZE֤ZC $ W]u昢W"`q0;iTJ%w=iȵ2o>0,+Ŭt4"A qH%b)HCFlKQExYo$0 iH&pn700Sk/.W4")f1t:^QOӲ, 3Sw1?zG&q?Q8q d[Whw77w߫9mU=ƘRH)]_]tHɽnLIӔ37;`VUO\r.9/󼘙!QJ%npJ) )"Qf3!m7ab!N zRLrcFN<0qa)xH*Vuq*^Q,x9<+1B]5Z=s)^)L IDAT1~S)i8=Ps^Nt:I}U6]JC\.Of-jJQcݲޢGe?bΣ73)F}=ݽ~Zč|tn7C8,ˬb",y.XRȁ 9Dq׷i4M, !~Sqtar`q=k^_.舥i/qGn~n |8@c)> "D1b fR?1w?y9O9g AGDbL؈EdU B)FoJY_j)L90!"! U9 bDdy7 Lt^TPqf)Tf?Tkl0 -ʥѸ2]<r.n_rB1k?<~p80FC֪Mʫc+SsYy^2iy }X S!6cJ{O2Mo}cksvnǫ0$܋d6DR-HnNZvqgb9B c+ޫ5XO>7BƦYO +yRJRl 0PAetRYKĜ<-%貈:G`ƪv9@D]mGTh5mJ&(rMӲ=3쓏 CJ)H Gvn{ijft\&40U$W_5DQs3)e &\UՈ`%VӒ'LϿzDw[c48l6#?yrm2?I:GMmk@5ݒ;*F X?HDzjY߄N* bU.Ԭk( @ a aѥkyZ5''CA9KPT[BL3Kc8|Ze9.KƔ8̮Oi={RI8TIX+NѬpz;oUC)f3^]b?Gg?"FfVA5͹ۻòi;#w卋m6ҁĸNt`Tu$ w2+M@9e^JyOiYR\(ɍpgϮCfw{Sjdb){f :tF͆'TΞ%zD$hul$pUӊJ'zs«'bZ\R,wIr)3]NTg?tͭ!ƫ.䭺`"둲>%zR@vaۜ~}0gG1Á0O4]LJIM}$/ŵN)p^NBIlvۍZ?8WW\?"nnnt6M| ؈̃C55{"82m|;$H+z`-j=kgY7~Vջyr^[|Ec^;]_?{jXgGw*cer)]`utu,=:pY93i9/yBL)øَXJyc2%7]aԽV#V \qifs9)&bEt\m:?Vl:6}ۄ5 U?5䨷}vAxܹ*$q3v uRqLhDxXk5j`6n~MTrD1FN E4˒>^ՄT,R!zf>6ԳwS7իW4xYTU0m#!A&]器H㧁wۂgvҩBʚS]"f|w8Z^ۛۜLvԙczڟh5%>\ܪ#vvTF+hgv J޾rq=o̺~'oD|C;("wwDՕO, U!h:ʍ8@AU3'Tw!boDC8`c6\է^0MӫW}90{Xx4tFKu3ZwL|Vn7C.혋5B^$lDMd-эQk|y^yID ,E$B,R;uh)mb}$ Ü3\Ts)YiQKs)+E̶?l6^n0k-MK9 ?s:/bR(Ni99!U9F,qj7C&.bE-lqllj_rJ`eZ1!T%DK)&bl6JLj @k%va{i0ണl.cbZ58"Bb ˲T6yt{wJUӲܞNK)ə511zZip:_|U!aL1WwSvꈓRly^[>z$DRLU^mݟ~hf2yČDicйtӶ\iNGuS. q}ϴO}jB=n8n{11ˤuIqAʰ;b&cJ8YV;l#YJ)di^륰WX/;an1 3=CB\aXYk̥nI)ŔƔDx3 2ݸy'f |!\u9QX34,&f\ *Rr<׳|mW"1%wTTm4uxi:77?p|v3ƔgUhy3f3M[0稵c  BfuM7Je) ڒKe%RTt:NyYW"N<s IaF٥gi%jwUjGX4^u2ح\"m jI1|s:-U̙ٳ'e)u[bB8Rs1N7h]ig[V;ϬZ:n0/ Hx8yK~4w3 ilYwo1EiKk\=!qPo_Zę93pZ0q@BvRcO >aeQ:'KH"j"s$R_}Y?m:mkABݾ={ Nޭ 5r&{DYun># 3S>('t:uct/Ѻn@gf4ͪiX|գdv%e^D֩[g!eLe媊 hdB4òd+R5|C|n80W _F S5C)h1Q8*ȱGԬdqe̜gQMH" a1u`*^̜*Cn"8Y*jVuLmݗ͡=7a?= {nmz79U\/_^!nqAAsέ4/SrI*= kq@ՋU{Zw+}.St!K RV!JLf_ .ܯ|G9o7<xV`oD#.L!vei![q厮e:[\Naڵ̳zk 1&D>{wI0+nZ3A@ Qe#w>9, 5q{hvT: Ľqz n=x3w}RR(vus 1 _boSw3?.r~8'_^fn+c4SsԼ azC/ bQy' ۥa7NoEy۰˴u̮4Dt5~v/q{"绯a70xp*ZYBMnG G~d趡uFjU] Cl a}:?.u`<51}y2t/-E٥ێL(^..˳VX7iTﯽNwk5Z&{ }9? _~DžDPz?-=D?yʺ>ޘ*+m>Q*Vy^lt7ŷD2>:XwIGBFFћ>>?w搷Dk3{ujr/ٳ"a'mXM_IDAT}1 IENDB`DarkRadiant-2.5.0/install/bitmaps/logo_old.bmp000066400000000000000000001354761321750546400213340ustar00rootroot00000000000000BM>6(~~  ě̛כқ⛛샃[[llዋnn\\䋋샃[[llΑhhh___???aaaKKKEEEAAA WW//j]W##bBBwWWW[UUWW]WWWAAw++hW""aWWAAwWWW\TTWW33mW _BBxW!!`ttXWWWhhh!!!!!! KKKAAA WW//jddWWzzWWMMܯ''dWW,,hW[GG|WW""aWWWWNNܮ''dWW33mW _BBxW!!` `W""atthhh篯%%%???wwwUUUKKKCCCAAA WW//j]W""aWWWWXW__""aW""aWWWWWW33mW _BBxW!!`WWWWhhh  KKK鋋"""<<<~~~WW,,hGG|WW##bW `oo\\WWWW99rW""aWW""aW!!`oo\\WW33mW_BBxW!!`WW]]hhh;;;<<<$$$KKK WWX!!`YWZiiEEz]WWWWWWW77pW""aWWDDz]WWWWW33mW[??vW!!`WW]]hhhTTTrrrKKK 333WWX!!` `YY[[䋋WWYWQQ^W""aWW䉉WW33mWX//jW))fWW]]hhh<<< ///MMMKKK̫ZZZWW,,h蘘ZW[[||rrǙ44nW^::rWX]]22lWW""aWWzzssǘ44nW^33mWW""aggKK~WWOOUUWW++h````hhhRRR 222KKKXXX...AAAZZZWW//j!!`W%%ciiXWWWW]''dWWYYY22lW""aWWggXWWWW]33mW^uu[WW!!`WWWWWWsshhh氰'''AAA###KKK222AAACCCTTTWW--i[W$$c鰰ƒ闗==tW""a쮮Ů鯯ƒͮŲՉWWDDyŮhhh!!!###### KKK--- AAAFFFTTTWWY//j//k]WWPP==tW""a⃃WW]]hhhaaaqqqPPPKKKAAAKKKNNNWWWWWW!!aVV==tW""aWW??v??v{{ƞߟ㞞ооопiiII}TTII}II}MMM ,,,xx@CCCCCCCCCCCCCCCE,,, MMM444tt NYYYYYYYYYYYYYYYYYJ444444wwLYYYYYYYYYYYYYYYYYYXJ444444uu NYYYYYYYYYYYYYYYYYYYYXK444444vvLYYYYYYYYYYYYYYYYYYYYYYXJ444444ww NYYYYYYYYYYYYYYYYYYYYYYYYXK444444uuLYYYYYYYYYYYYYYYYYYYYYYYYYYXJ444444yy NYYYYYYYYYYYYYYYYYYYYYYYYYYYYXJ444444ssMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXI444444zzNYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXJ444444rrMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYI444444zzMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXI444CCCssMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYJCCC;;;zzMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXI;;;砠uu NYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXJyyLYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXJ΃ IYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYX//NllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&LMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&LMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&LEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEZZMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&LZZEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllllYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY""kMYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&&L""kYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYllEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEZZ GYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW;;VZZEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE HYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW((NKYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW//RkkkgggGYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW%%Kgggkkkhhh```KYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW//R```hhhhhhgggGYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW&&Lggghhhhhh```KYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW..R```hhhhhhggg HYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW))Nggghhhhhhggg KYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW--Qggghhhhhh IYYYYYYYYYYYYYYYYYYYYYYYYYYYYW++Phhhhhh JYYYYYYYYYYYYYYYYYYYYYYYYYYW,,Qhhhhhh JYYYYYYYYYYYYYYYYYYYYYYYYW,,Q hhhhhh IYYYYYYYYYYYYYYYYYYYYYYW**Ohhhhhh KYYYYYYYYYYYYYYYYYYYYW--Q hhhhhh HYYYYYYYYYYYYYYYYYYW((NhhhhhhKYYYYYYYYYYYYYYYYW..Qhhhhhh3555555555555555##DhhhDarkRadiant-2.5.0/install/bitmaps/loop.png000066400000000000000000000003721321750546400204770ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME/ sIDAT8œQ 0 CBlKC@f1BQ Jt Zk؞I$fEUQU,}䁫j<ϒ z @Q9+/Aچ^ FiE'7hݿiwEIENDB`DarkRadiant-2.5.0/install/bitmaps/make_visportal.png000066400000000000000000000014071321750546400225460ustar00rootroot00000000000000PNG  IHDRh6bKGD pHYs  tIME6IDAT(MoGwޙެ8 )PB[Jp墳K8H*ikwgvfgC^]՗?yqv2o]`Ώo6cNq}8`X7 T iy$ܮ$ф΅(M4Epw6u'|Mt^ [(>|Y#?>qL([Iw>RN$X7)-1r?.VGQoxr@u*{Gsu`xVhNHb?gNV\?tsq.'|u# [VQ6cZ/%0ryT"1uUtȎVd,FxQx/Lyri7(w~sO-E|m"k -T#~ް.ӝ{vMVVn>pqFj/ 8jYdaogNyPK+ǀaٚüv+Ҁ^  D cmc)>"Xh)"X\%F ݵH\KٙfV-,`k5 ;5cS>6}qp}^/O/9V֯!-i^22բ1tӈ-,N+ALzi pF%TPwW2KI@ "?I (/aJPA('!EktZ V7z梨VwMypsyg> }ܫI{qZFƚKIENDB`DarkRadiant-2.5.0/install/bitmaps/media-playback-start-ltr.png000066400000000000000000000010731321750546400243220ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME 3sϸIDAT8͒KTaƟ罯8]F-?(…HqJF_U.B0h.>W-Κ  3Nwf"83Eg>9Wb`ޱX Ow%~((/8/gd]N<7&b3mg׆nu>BBDBDI+k47e&Z^,SS ,zЭNE{zƣ??kt] 4yP\Ѿl5;peeh pxy_#1]RѫD$ uԨ}lozyydm9*8pCJCtX+d^[:7j7?9 RQp9D`nfVݣ͡غm9l!0S5?i{_n>L=Oٖ6:j(zOlU7ֻ;/D?sl)W_uo~IENDB`DarkRadiant-2.5.0/install/bitmaps/media-playback-stop.png000066400000000000000000000005661321750546400233610ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME  IDAT8͒AJAE#(r\Bporw^;nYgY&4=.L\hmT_oGu//?\zBnFˮm&y@HJ3ɽ~b@$mnINu`*$&KQ4nGy(K퐉9z=^1}3'hFPIENDB`DarkRadiant-2.5.0/install/bitmaps/model16green.png000066400000000000000000000012671321750546400220220ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME6DIDAT8}NSQ>chbhL1 #&DcG$h|}'N8Ө( abb(*ҋ&p[_GL4qm&Wg X.Ħ\X}E pʼ~8ϻoͻ<dN TЄVPK nl&"\rTD#dG3OY)jRnךU.@C~{rujM|%Ujm PQ%an9O{1W]d6#nX" R(4m@l>KR[y/q |XDmls]+ǫ+F K\Ҙqзmt A {4V[}:D{Yl\%7H*"TD+I8r&qKϻd9s7#*F|b٠$eЋ x؃%b߸œfT^IENDB`DarkRadiant-2.5.0/install/bitmaps/modify_faces.png000066400000000000000000000003701321750546400221540ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIMEGIDAT(S w/QN [h=$]D8"BI2;o^ĭd9Skir3TRc[g2ihm VapǤmA1~7xzGDIENDB`DarkRadiant-2.5.0/install/bitmaps/modify_vertices.png000066400000000000000000000003501321750546400227150ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME0uIDAT(S :} !#=.z'e&p+$yzv+dwV-wnZKTrI6^2),bl?mp4ɠ6d7 f^%IENDB`DarkRadiant-2.5.0/install/bitmaps/monsterclip16.png000066400000000000000000000004011321750546400222250ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME0IDAT8˭S &:08B?jZ#}!EVȈ5C=dĵ+TO1EW.P4y̬S)y9% Hw ,k F0swMFm{l-|h3wj#FHYIENDB`DarkRadiant-2.5.0/install/bitmaps/noFalloff.bmp000066400000000000000000000030701321750546400214240ustar00rootroot00000000000000BM86(@  DarkRadiant-2.5.0/install/bitmaps/notex.bmp000066400000000000000000000103301321750546400206500ustar00rootroot00000000000000BM(@@  (( 00@@PPXXaaqquuutttqqqaaaPPP@@@===000 #%#%#%   "!&&'  "%     ##&#   ###   ###      #'& "&###$#####$####% %   ## #  &&#    &##   ####%'###&&##&##%'###   # #&&&     !!##''     ##   !##& #    &####    "##&!!     &##'   ##&#'&###'###%###%DarkRadiant-2.5.0/install/bitmaps/objectives16.png000066400000000000000000000007121321750546400220300ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIMENWIDAT8˥?KBaϹ&5 BKߡ)ڄ ̴DMA-B j 47 h7= qE3T/s=<%^ _?&x\n7~,D5W>$ r]}5W~Uk 9|7 0eQ<5O?t3|֟bik' XX0jwMkx3-U횑!5'hϊt; ulny@rT n)M,'d[I-4lhY %3sP1kGU":7&mˤnV-%mR'yp{"IENDB`DarkRadiant-2.5.0/install/bitmaps/offset_clone.png000066400000000000000000000003761321750546400222000ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIMENIDAT8˝ CesxH !@)Rvn# bXrL;X5eQZՁNP0W{avb"4Rm'1R;~*.ܳ)& B@vJ I a'-ppF|c5a6΁7W6t\PQIENDB`DarkRadiant-2.5.0/install/bitmaps/patch_bend.bmp000066400000000000000000000003561321750546400216110ustar00rootroot00000000000000BMv("""""""""""""""""3"#"""3332""#"#""#""""#"" "32" "#""""" """ """"""" """""""DarkRadiant-2.5.0/install/bitmaps/patch_drilldown.bmp000066400000000000000000000003561321750546400226770ustar00rootroot00000000000000BMv(#"##"#21"!21DarkRadiant-2.5.0/install/bitmaps/patch_insdel.bmp000066400000000000000000000003561321750546400221570ustar00rootroot00000000000000BMv("" "#""""""33""" "##"""""""#""#3"""32"331"#3"#"32""""32"""""#33""""""#"""""""#""""3333"""""""""""""""""""""""""""DarkRadiant-2.5.0/install/bitmaps/patch_showboundingbox.bmp000066400000000000000000000003561321750546400241200ustar00rootroot00000000000000BMv(""""""""  111111  1  1  1  1  111111 """"""""DarkRadiant-2.5.0/install/bitmaps/patch_weld.bmp000066400000000000000000000003561321750546400216340ustar00rootroot00000000000000BMv(""""""""!""""""""""""!!""" """"""""!$""""""!"!2""""$#3$""!"#3""""!"2"""""$"B""""""""""!""""""!"""""""DarkRadiant-2.5.0/install/bitmaps/patch_wireframe.bmp000066400000000000000000000003561321750546400226620ustar00rootroot00000000000000BMv(""""""""" """"""""" """" """ """""""" """""""""""" """""""""""""""""""""""""""DarkRadiant-2.5.0/install/bitmaps/player_start16.png000066400000000000000000000005361321750546400224100ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIMEن~/IDAT8ˍ0 ?q! 5,D Хt.;P(9Hϲ*Sߋ^TQW"I-u%+L5`wyM7K=P4iMX y[sAX#-C`k*F R)&#*1$U]GĖiՌ5x LoIf#Ig;@14.<1ԌyRIENDB`DarkRadiant-2.5.0/install/bitmaps/pointLight32.png000066400000000000000000000013171321750546400220140ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  ~tIME@/\IDATXMhAYöqĘv#X(V{ APB MT/z̧"/1*͑(z+HC99ī,ӅuȼY +}Dْd0z‰>`cۚ5wg;Gmh NqQ[wHں^A5Q  5aw2ai`\:E©= _ɍcR۶5pvsodso/޼_mOsQ( ձ,ZX](*&ruIENDB`DarkRadiant-2.5.0/install/bitmaps/pointfile16.png000066400000000000000000000010341321750546400216620ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME@/IDAT8ˍJQ7Q.@.)tэ.J6]u'tս[Ap 7n Q=$\s3d?.r>zYhIYԕf!IT<]TW2E/y@5+4N{TuC| \] /h$hɥ cZv~㏅m YԜQ@n ll-岰m| &S) !L h/ t狏_4pUが@z"d^,M P7"^g\lC;A6Bg`&te(Q-T{3]t*nxW B k2zQ``d:4D9lw`(\| qpYY%,w="ix]UÔ@u! {\BH?z[/Ls[m@pv}fYnQ%˛t)T>;,o-RL"[+wPVއ_1IENDB`DarkRadiant-2.5.0/install/bitmaps/recprop.png000066400000000000000000000003561321750546400212020ustar00rootroot00000000000000PNG  IHDR Vu\bKGD pHYs  tIME7!iTXtCommentCreated with The GIMPmNIDAT(ϵA @)qzbʄ0T&:CDZ ~^%:t_K` "eLIENDB`DarkRadiant-2.5.0/install/bitmaps/refresh.png000066400000000000000000000014431321750546400211640ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME*RiTXtCommentCreated with GIMPd.eIDAT8˥YHTqwQ1" (h$!"Q02 %0C$)26rLELTpmv/cs8||gny@a?}$?aռΑ"鎗Mg8 ZIbW"kpwؕ#b D(@ٙ{1;? ~Çݑhk{ݾTXt>gO7 pIIГjMs#+2!=$IԖ360Q"ri^iK8JE)3#|N͵ZSœ^&Jf4g @=1mf}Xe@PbEpLJZ#-}%,Tūۻi{Ūu 0. 9kvvcU \s_P'F&,ՙʴ2:Pbr<9;IB!mב~z\\=4j`v]YlXFL?jܟ}]ݷfla>S(~IDAT(ϭRA fF_mˌ˚#FbUb@cPJOI~*p(>ضh$}~˘ž0|_,HjZ8.+!Tg`qTNw03N(+SI`4:/IENDB`DarkRadiant-2.5.0/install/bitmaps/selection_csgmerge.png000066400000000000000000000003731321750546400233700ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIMEZƋIDAT(ϥ 0 CYf̜@M?QN*q <,UVls,8[qks%iY>mbU >mtk\E% i$"]. [ypfckNTbIENDB`DarkRadiant-2.5.0/install/bitmaps/selection_csgsubtract.png000066400000000000000000000003711321750546400241160ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs+tIMEZƋIDAT(œ!CO7/f/ >؄it@T&u|7$ e; T"~D%Z^zM27jF) ݰu,PUB: } gq=e)JڌQ&2_;*ףIENDB`DarkRadiant-2.5.0/install/bitmaps/selection_makehollow.png000066400000000000000000000003171321750546400237340ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs+tIME]\IDAT( 0 TduJhK [6[o%U(`-xi24lG!rGg=)Q"5;`V@IENDB`DarkRadiant-2.5.0/install/bitmaps/selection_makeroom.png000066400000000000000000000003071321750546400234030ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs+tIME9S1TIDAT(퓱 0O O]( >y6M&H 4 ߀\ jfB]'~۽"tIENDB`DarkRadiant-2.5.0/install/bitmaps/selection_selectcompletetall.png000066400000000000000000000003371321750546400254610ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME[lIDAT(ϵQ ! K:ڍf/ qBAm4)HPQ5 'f-@)廻{XsOˆu #KTD &n$[jlZ]!I:A1\@ ^NIENDB`DarkRadiant-2.5.0/install/bitmaps/selection_selectinside.png000066400000000000000000000003461321750546400242470ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME[sIDAT(ϥ D85ί6r})VuDk@KH= 1޵B.}j""2Y](AʀcZVMzNzFU.i\ENIENDB`DarkRadiant-2.5.0/install/bitmaps/selection_selectpartialtall.png000066400000000000000000000004201321750546400252760ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME\RIDAT(ύQ GӃq4ԄRxdS5H"J9fg1/ncf , (kI#+?*&{L< Qkm)/O}͕R<B: M]"f~@1wOsy-OIENDB`DarkRadiant-2.5.0/install/bitmaps/solidMode16.png000066400000000000000000000010541321750546400216120ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME -R~IDAT8c?% $##+##+^Tyy8II~ñAZ4<,=lߚ{2's7U`&; [x$P\{%_v.>?bd͈;L~#5?k[nϯ_~6ajzlEycX:kCV7+n^ة[͉֘b 6*q1lcYzךaBw9Xdł L||'Ly>~ik=O?=~uނc9/022sp0U210g}ρO{_1t? %e>LLf``P_ndddtZJ38yIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_communication.png000066400000000000000000000004631321750546400241100ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME 6^IDAT8˥ 0EE4iP :̃<(+@2N"p-=Xk9S_@i3(K}+r$ΣߩUͭ \"$B`pdO }IPհ쬴k&]XiJGۺ7`^)`{ J]-DLIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_damage.png000066400000000000000000000006701321750546400224610ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME ]8?EIDAT8}1j@EA t .|cI&8)ܤt!m|)Ʃ\MH !ZɳaFg>_H⤏+#9WS& A5_306KdEi?CXH{L" +c\ƁfҦ+R ;-=~bwL Hm\p+Kw/mu crBPt$ܐv Ɔ,VXaPcM14^XaT0B6 |@H![%$Xk DZ0G/t JkZZϲ lVST 44FՖEL*N&z`)^Y3[[׊[ J ̅(-6|J@mN%o o+2SN7px%a!L 1ymx-GDhatMwœ$IENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_healing.png000066400000000000000000000005711321750546400226520ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME͇"`IDAT8˽N@=)P,  AޡH|۱ngvnt&.W[D2rELrcj@e'oE"0 (/Yݱ51_Acf#\faTnܚ+UDR L;ol.U_IvU?u[VRdssV ޛ#zu[gquVC?q b0c^dM,e Y8HC| ^RIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_holy.png000066400000000000000000000004541321750546400222160ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIMETsIDAT8= @F߬AV=,sCyj+ai!Kq: o]7IzCamB o‘d ]-|K` c9=IENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_invite.png000066400000000000000000000004041321750546400225340ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME#CLIDAT8 0 Dݠ+d`V`2B WqRs,?ىDgt.Iuiw Q!E{V.`6]YTG(5d~WjQKY\/~UIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_kill.png000066400000000000000000000016331321750546400221760ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME#CL(IDAT8}S=Hsg={o~nALoᶽ~PLqhK7vh(ꐩ|P tB-Z, X͐k4Cgz8TUkpΡ:Z\*l4:Fn'pJO`0!puuu;<(7?rD&y$ŏT"zz[%|{qppkZ~F4h0(w&pTD" ",y/ 1i1c |{{;;;H BBឮBr|{{{XYY(HZz83vd2|zzX 1Zbrr۶?B4l8h45{""۶$E[a|jZ$U{PR$I/ ͑,M˲ !tc8'K9op o]OcmUU;$ImIΒ' @$9?+8ӍPP(bwɲd T Db:W Ø DRt~~>onnar6/ @Gs)N/2hD"|}aaWVUuRUl~WӴ$~"޶r.u*c?v@Y7mu]8˲eavv[[[p]w4_Ɛf!8<x( "Rǿ}*LHIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_knockout.png000066400000000000000000000005131321750546400230740ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME;_0IDAT8˽ @ `@}d@G  pA}]B/Ӵѿ*sւk6` "`\7P४$< @n2U%-d@cz= g59e>;@렅{/'Gc<&t9A].V[#8R+,VG .ݛXLr~vkr NIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_light.png000066400000000000000000000013611321750546400223500ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME:o~IDAT8˥[HQ?.:ּ,fV- FEUEaD>cЫ/F 1؍FBDNk2˞>9=Bvu 6oaߧQϮ6PX,깏];O<0 Bê ` s isژ .Gҟ$b7B2cz7`R2o_'͙#Bvu 10{;~UwtMw=gXh#o)[X~j0 ޟDH%\٩'o+,CRtriKX6#d^,v/Krj4ր0ĕZTFR>ALϐ3#'T-(t U@s*Tӧ>dzxu4jmyukRY6!.4&~U=?(cnyMEA"ѴoΕ8~>fGJ`&G i.+u ]e/˒ly(_I,{vas/J (q@oU?!j}EJ)>`JIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_magic.png000066400000000000000000000017671321750546400223330ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME9b>IDAT8M=leﻳ>?qlvq4uBQ!A`@TT  3bbAR$*JHڤU%UHH:]}x?!zH.{e!A>)i|iɛH0o l o׋F,E-']#SWuaP C"fChc F =C>8*2/>y2 K, $`X)RŪKX𺆪(A6D][♑μe]vn_} :6R>4IBr]W4DIRh%SKUViwıyXO G/Xp$KhU^0.Ԇ)h(gۇKy>4 ƒeگ2S7x#A+BCtmxyZ Eʰe' ”e;Ƚx$RBty!e! [KǹW[{agwɟCފ%9@MipIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_random.png000066400000000000000000000017441321750546400225260ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME8qIDAT8UOw-UJ ԉ$K `𰐘prz1vi!Xb̃\fܐ& #b 0‡-ҷv{< #R$LuHfGG4f3 Ȳ(uɅ#{:h[M_iVn+Wz=g 5h|ggAi2ߪDGŘ%O!6!B~W}sׯ_woRQQdȚFA)noFuwS\^|֭/ mrp.?~RWg/nf ٌ9!H|K$j:;+^ܵoa-C!7oH4^%!D!TU:$''tX%%ixT*"j}=nTLI܉NJx,'gx׮{3Ҁ!I~xEd2;v) _SAB@S8Ǥ(D,+S%gKK=X X%P#Lr9RUtFbq@^_܌ S6K6҂ 耖ˑZq{a'Y7ntdY 9BK&ݻwC Fv}}W E}4>c?Xvg:ܹslMOSgRM{Ƨ;Ȳ7ijζ76mܔiR]=Eſ 0CAIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_readable.png000066400000000000000000000005701321750546400230010ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME8IDAT8˕1r0E3i\Ґj]JC8 JqrC vF3jK.!3TU+B u7MeMs%,Ndi>U+]%ȩHy)|n,{Q4$DHFހf\FF;] 7Q~xHzS&vɺ"xH7> :;F;DfypyG&ঐiu~a'UIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_restore.png000066400000000000000000000005561321750546400227310ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME7BIDAT8˥ӱQ1w C.*e /Tf t;u t7lFhkwT\+IֱfIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_shield.png000066400000000000000000000006571321750546400225200ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME6E#r8.AUGC ~ˆB9\\\`ii mmm( zE(f`P7럶߹-Vpyy ^$1?? q&&& ~gL& A P.aZR܌.(JJ# 9|v {!> uh >~?=FZ/Y90!:...◽=z}[;(`n H,:?ǠC0F$ƃmuw ǧ?:יzM]\a^ % ya) N=ryil٥s!$|u v+^_%7sUj4ﵖQ,w{h'SphqcehNKQ.|#v(%NFcoacY;İPTxp++iu>yaxWot].Yb4BG-K.&45\wtN>|hux;vE4MK!RJ Aa<8NZ6'ady֝(Ųc+P9'/?(Y+4a/NN(⌟t:rF\o4iLD H)!O3)j+=q>^@9_sz#̍M%C[X˨@W?l393֯rXkL nBt1ypjK+ (ub~}ˎV -`L85p̟S1}1<%L1\+84C@u0;=ݵ,`' A\gzH|FY[ 8Y?h&x"H/r"JS0MF)`1 2byGN{{OaH ە{Ǣ8鴭>kf[}uJLlІ!O)N^xkrv| cNμd:ˇbhBy.Z?ˉI[FT >5z/ -6Z'H)9|3)-?9r__oɦYX,3Xc?ڿyFױ VD@OW>/~fT36OHۜfFiiHbxxt\-'0jWH2'aB-^CC׊Zآ" $z8׳&LnjX^ǶL\6{ 鲽UlrlG9{Wݣn߽W>x|xa<}Q5--_c~ڼo3Eh-۷Q+*k\lVo54!8:4L*e137ß JPDsIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_trigger.png000066400000000000000000000003501321750546400227010ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME2(uIDAT8 D_S2[!I3^qmZm!DHO X 7!wpĪ\A"NntUqz Bv"cIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_visual.png000066400000000000000000000017621321750546400225510ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME1l!.IDAT8˥Mk"g3f2!D&Agc4Xaa =B/K!BB4"lquq^ P2mY1gggzH$+EQV)^b!<$2M E"^|cFEa= IRr|_Mff&y>>>~h4qnzuQWA6nxuuC*kFLfc|ajv_41 8n ?///^A"`}:.bl`0x||IQ4|>ea$Ma~F ('''˲ӓ4\. bEAA(zJRc,q;ߖJD,@QNPVC:aid2J^Ks\y8::V<(eEQ "P!$28.//p$@ 4Mض E p88XZZ.kaXd<7`IUչ$IdvR) @|>LBEKQ\TfX}noo`/5n{kXz8db2<;4M35MS~ DOTTCZuB!`04Ma>\.W4k4نa ~:˲Y4BEfGI>J%54M{h4{˝Nl6|ww kYքt::KY&L:ߒe$9j(d]TL&`۶aXdm۶`HcY1n$ @c?ot+IIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_icon_water.png000066400000000000000000000010761321750546400223660ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME IyD7IDAT8˕OA?o;;ݲK%\4LثFk=w/41 ^8phA Jڭv˲C B}73̼U墰 " gP"UZ]a3(7 pH*@ <@ʧX׼Ù!YmcRſ8XJCs{;\N+PDc`b SXbcHGQy ҆bZq T^oGC@6 c0 .cIK.e[~3e8!4x B[p2EnS,|ޜ9NER_v7 MqUآk<1! A̙K r R3>a?Q=^oe%CH";q~g9 ` Y 9aNtXC(boBHflshx+jWr;sUgIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_response.png000066400000000000000000000007601321750546400220710ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME0&}IDAT8˝KBQ?&`!)4C(6ʖj[Zt4p MBo@܂Ԩ9=s%m@HQ $r] ԁEx>p =גlOMt`d?QSY(ɢu*aˍJc`XАzk W:F<#)^ )`L*E9iQU#P G29Xc`pRsBqxawާ>ƓI&mpeoh]I8l":^ԖsmIس?SG*s"jweݲp ,+ {)*`o"*=6k(dCY VIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_response_inactive.png000066400000000000000000000014211321750546400237460ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME/.MIDAT8}[HTQ}̙:1C22* zD#)@P$ƼDVPDC`Q RST"C LK2 fDg:g^6-wx[b`-3@KSh b* %ZېKg,7"icvs]twɂ35ٻq==$L2ۦ-]5`aI^H,\GLy2X`;?j<$M*K&qnDeb8DgU MF&6a=m[d D%W) `_˅]\)m0rQ͑q.bHlX{gd[2;:o*hz5;Yֆ==?(N L5GMma`T2azC}3j%z>VWc B4j*6!Z=8 W/C:(i؎9K:4 {s>1$&X veT)1TBA^NQgvej9Ͼ8,yd5ABab!%%}p6Rΰ&"oR3U3'JI#`,h@ /^wIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_response_inherited.png000066400000000000000000000007361321750546400241270ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME/.MkIDAT8˕1HQpp.5DMuB.;kSKAm $-9 4ymK gB;9S}s|2 D4P&˞<nu)c1b1'@%(jDESUPEYwq݃?c8# 偺$ zs̔nbځ7?$$ @4S)(a^%X?-%/2_Wogr$!'V:(% ڼ~ ̍q5%!<`=`gm۫!P /\-K(C ,Q\5/t{s}v\ _ǏOIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_response_inherited_inactive.png000066400000000000000000000014501321750546400260030ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME-x aIDAT8}[HTAgκ=n遵r3IB/iAAUvÇ_)eBa%QTEEv3Ֆ5n֖)ճjf0fjyQytX d׽@Ph7c ՀpGqlw@jA U_L,J5 XFS~9bX w}af;5yDd`:݉QAWJN%Ù1f!"8+PA©z \mu[gƶd Yn7&[L!n-_o&B $t`1Tqη:Sx#+.$H0_ PW3%?[[<}Ə&5l6W*E2A*vdףh w>E( Yn7}fV4۶-N'o }1e%% f^/NG _ uC#7o XkP4pg'i@-X~K3d9L('ow.}Bfťoϳ~>ZFSkmti!V_ \gA3!hYJ?l$s֑պL5ߚ-`sQbSE_/IDhb}h-&ń@h$aCg:JzTƒw=J9>IDAT8˝/aw_sJJ䐐HHX_t70H,1u)itrz AУ|߹^DĻ|ɛ}yvL2WNC( B FU ts@FxXր>`TOtxL;k"5`ݐP^:FݷU*ra\;(>8vp&BhXC&L2Jgѐ&B4pySt'FH*n_䦺Mu῾c; +uzeH%'b:֠\eB%cȅj\!4Rk4KP,=*hY b~6X;BhvA=Q{ O id3IdVԽ"]А.Hqrv5l2#lDU(( ,^E۲&vǤKJ>OM( fIENDB`DarkRadiant-2.5.0/install/bitmaps/sr_stim_inherited_inactive.png000066400000000000000000000014301321750546400251170ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME >9>IDAT8ˍ]HSa9s4t"mY'}Ѕa(H" `aA]`QACe3pH+Jt73wօfxs{W }`ry;O`ƁvR$ :u3ԩ/u`[Lla!?g/"ndC\v|FKIXEE4r y0Lh~)fg6KV=Ӆ<FXF/M`¦u*֩jm%`xOJcÊR}ileVu<Ɛ>&󪴔QF5۝pXJSL4rnziG19h5 v'IN^<޳(/nfo%,c. vO*RS`[g'fx_7]$-X֬Gy:B$WEϒ]_bdIS=Ԁ$#2L [R$y+l)ٰ?2z>&Hd-)!m<5%A@0=r|쩽c(v3 "!9ݾ)*c1B=]فVz ^l5Ǒƿgn`'"gD.ZeileK,=Gl\_|Ʀ|tu ! aSMY@,#E,/S8IENDB`DarkRadiant-2.5.0/install/bitmaps/stimresponse.png000066400000000000000000000007231321750546400222610ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME\`IDAT8˥NAh3 Fyh4&&*Ac$`<-lF bC19!!M&?~~_V`O/&@ϷO9UjDAU'"NGtL{Ngz_IDAT8 0 C_o/ ba͛ڄH⭀՗sɊf<$P{|qjemg;f{,0'6r$ewIENDB`DarkRadiant-2.5.0/install/bitmaps/textool_facescale_pivot.png000066400000000000000000000004121321750546400244260ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME ZIDAT8˵R #a>KGw`H$@`3ǔP݉z`^.-af]"M^RRT;Nb̷\rsK21v:Aj`f IkFi&M"F43^2pIENDB`DarkRadiant-2.5.0/install/bitmaps/textool_merge.png000066400000000000000000000005461321750546400224060ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIMEA5IDAT8cŌH@"@7 `? ?,H5`5jo8Kb5w/_v2T'_JzT/ 3 48 /Tbg,`PM '`Y;L׈2?;r -|lT у"2G$Ks&o R)" >m[\ .ZZw+I*kPUv/!,*Lm\7߁sp3ɟx}ò=vIENDB`DarkRadiant-2.5.0/install/bitmaps/textureApplyToSelection16.png000066400000000000000000000005011321750546400245460ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIMEqIDAT8˥ 0BY%50E` zmꄖ2"Kv%N;CC8Uq  VJG).8sf96G'9ƒe\A1@B|caV뗑&%[N|f̸`gL}/l$EPM- FH Lñ( ~jDIENDB`DarkRadiant-2.5.0/install/bitmaps/textureLoadInTexWindow16.png000066400000000000000000000004601321750546400243330ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIMEqIDAT8˕10w#W_"ǢN TyY],R &{) FOR3ϵ\@._pnw%b,=xX`!V: xAWd]'pm{lbq?^i~/xg3 r\5B2#4\H }GIENDB`DarkRadiant-2.5.0/install/bitmaps/textureMode16.png000066400000000000000000000003051321750546400221760ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME 9TRIDAT8c?%Bc`c;\R ęEbFX}?![=~bnc+8ba4 C0xnu@qh*IENDB`DarkRadiant-2.5.0/install/bitmaps/texture_lock.png000066400000000000000000000003661321750546400222410ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME ]OIDAT(ϥ[ gO~T`& nm߹U.*__kSm]ܳ>L Hė- k7ue|R46B)&OMwkd`Nw ;#~FީTIENDB`DarkRadiant-2.5.0/install/bitmaps/texture_pick.png000066400000000000000000000006331321750546400222340ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME37S(IDAT8˥?N0?! *Ëށ ba "'%RNP. bEL1DHV}ғl=ǶQUѮ@jQcDu]kVUT_V\DTU 4}?3^ gz D$t!EN1K&!k: xA""t(.byǓ>^BZ`9)80IGUW+ewᜣ{ݕYy.^7$׳?SYo̾7/IENDB`DarkRadiant-2.5.0/install/bitmaps/textures_popup.bmp000066400000000000000000000003561321750546400226300ustar00rootroot00000000000000BMv("""""" 3"""#" 3""310" #33""1AAA30""433"AAA330"4330"SAAA30"CA30"4U0"5C30"SSQ0"#C3""#R 2"""DarkRadiant-2.5.0/install/bitmaps/texwindow_findandreplace.png000066400000000000000000000003621321750546400245740ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME 9TIDAT8c?%BMYg$FF`biU$.  G )?G j`"Ov"IENDB`DarkRadiant-2.5.0/install/bitmaps/texwindow_flushandreload.png000066400000000000000000000006431321750546400246320ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME 0IDAT8˝AJ@)dSЅG, =GP,x v5Fc3M'$j޼FlNqV,U T_`*<ݷ$Ee |?/S,K<CeSU}X "(s*MH&~=8X1f''1a?nJul7l&:%R;ڑt^kzzpoA[oWn7d#Iֺc)/~esy7GIENDB`DarkRadiant-2.5.0/install/bitmaps/texwindow_hideunused.png000066400000000000000000000004211321750546400237660ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIMEIDAT8˥б 0ȡsR`5<SgCDg+>ED:}\~i,m |.Z wfAI43`{0ЃϦ@ +5gƛ,Rx@X$8ؙp n"1Lc݋[ĩ=(g_tFI2Y#I-\H!+eUIENDB`DarkRadiant-2.5.0/install/bitmaps/unrecprop.png000066400000000000000000000004131321750546400215370ustar00rootroot00000000000000PNG  IHDR Vu\bKGD pHYs  tIME!iTXtCommentCreated with The GIMPmkIDAT(ϵѱ @DrF#J p(El\=@#KBfw4C`B #oNOlG.4=Ys=g<o "x} 1IENDB`DarkRadiant-2.5.0/install/bitmaps/view_cameratoggle.bmp000066400000000000000000000003561321750546400232060ustar00rootroot00000000000000BMv(""5S""""" US""""0GUU2"""GfeS"""DEfU2""DDGVeS""DDtufU"#DGGwVU2 DtwwvUR0DwwwwuRDwwwwt2DtwwwwD"GwDwww2"wwws""""ws"""""DarkRadiant-2.5.0/install/bitmaps/view_cameraupdate.bmp000066400000000000000000000003561321750546400232070ustar00rootroot00000000000000BMv(""""" 2"""""" 2"""""#033""""""""" 2""%R"" 2"#US" "" fU""""$DEfS"""$DwVU"""$DwwU"""4Gwwu"""GGwwD"""Gtww2"""s""""""DarkRadiant-2.5.0/install/bitmaps/view_change.png000066400000000000000000000003321321750546400220010ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs+tIME,gIDAT(ϭ Cw'9 -U$]'es(KI  ITx@E;]+I1o%DR 8>1Rj5IENDB`DarkRadiant-2.5.0/install/bitmaps/view_clipper.png000066400000000000000000000003451321750546400222160ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIMEGHrIDAT(ϥRA K-<Fu[HIPNaݶ!GGJb`~# rx $TL#6 ʪ-)1dcgX L+"$n=0&N5IENDB`DarkRadiant-2.5.0/install/bitmaps/view_cubicclipping.png000066400000000000000000000004051321750546400233700ustar00rootroot00000000000000PNG  IHDRsO/bKGD pHYs  tIME$|IDAT(ϥS[ Yzܬfd?D]2D) ;fl@I8}\7ieyB:,"YDkƫ)T|ݔ/$Dgwicc`bítT7XC:Kj]'|wgF}]%^9sE(g):ZѶY V n 6XCp&D&ݷ>x~*'IobnG ߫U0A2*'ۻE0NJ9ΉZ#X*SX*û"gCu@8ׅ"Y;Er$kdwLك~;a}GIENDB`DarkRadiant-2.5.0/install/bitmaps/white.bmp000066400000000000000000000003701321750546400206360ustar00rootroot00000000000000BM6(  DarkRadiant-2.5.0/install/bitmaps/wireframe.png000066400000000000000000000010441321750546400215040ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIMEǵIDAT8˕NQLpRlHJ6 +$D°c)p.Pԅ @Mю|.Ӵ~sw$TݶV$H<DKQr;R>זx:|rOVQ.nn̩|ˀuz ,+&V8;0 Ō,(^:dB$^~B#C`&ݰq?0_w.#07g:_Z*\hoa8Voo!&ImY-0&:&Rsxq?pyů[q8K Q<{D7ɀa2dЏձ 2ޥTǘsށ3F +O0{X]@>N19;k;eS%XL8 $g4oR{AAR𼠈ap|v0?$yN@IENDB`DarkRadiant-2.5.0/install/bitmaps/wireframeMode16.png000066400000000000000000000007751321750546400224720ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME#3#IDAT8˥=kPWG+ЂII2Is4KCnj!Ch=4thId.ԡ]Сɧ(q }{y8Gf}sۡHRt] `?Yr ]v8>X,DQ4k3wIm;xjfZ7- MNg= *|>fWB"N /pzqYgfeF98N$+uJ$]I3 {38I뺕RqKjKjaxeYeYQy2j4K4υuuY2i|&5`'E[f6] qlla+9Qsq2aiIENDB`DarkRadiant-2.5.0/install/colours.xml000066400000000000000000000237701321750546400176000ustar00rootroot00000000000000 DarkRadiant-2.5.0/install/commandsystem.xml000066400000000000000000000054151321750546400207710ustar00rootroot00000000000000 DarkRadiant-2.5.0/install/darkradiant.desktop.in000066400000000000000000000010501321750546400216370ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Name=DarkRadiant GenericName=Level editor Keywords=doom;doom3;idtech;level;map;the dark mod;game development; Comment=3D level editor for Doom 3 and the Dark Mod Comment[de]=3D Level Editor für Doom 3 und The Dark Mod Icon=@prefix@/share/darkradiant/bitmaps/darkradiant_icon_64x64.png # The %f means a single, local, absolute file. Remote files will be # first copied in a local temp location and then given to the program: Exec=@prefix@/bin/darkradiant %f Terminal=false Type=Application Categories=Game;3DGraphics; DarkRadiant-2.5.0/install/debug.xml000066400000000000000000000052541321750546400171750ustar00rootroot00000000000000 DarkRadiant-2.5.0/install/games/000077500000000000000000000000001321750546400164535ustar00rootroot00000000000000DarkRadiant-2.5.0/install/games/darkmod.game000066400000000000000000000334541321750546400207400ustar00rootroot00000000000000 tga jpg dds materials mtr fonts dat 2 The origin of this object in 3D space The classname of this entity. This must be a recognised class defined in one of the DEF files, and governs the overall behaviour of the entity The displayed name of this entity. This can be arbitrary, but must be unique across the entire map The direction this entity initially faces. Generally applied to AI or player_start entities. The displayed model or particle system for this entity. For a func_static or other entity type which can have brushes as children, this key must be set to the same value as "name" if brushes are used The model or particle system to be used when this entity is lit (i.e. for torches). The model or particle system to be used when this torch entity is extinguished. lights fogs DarkRadiant-2.5.0/install/games/doom3-demo.game000066400000000000000000000323721321750546400212600ustar00rootroot00000000000000 tga jpg dds materials mtr fonts dat 2 The origin of this object in 3D space The classname of this entity. This must be a recognised class defined in one of the DEF files, and governs the overall behaviour of the entity The displayed name of this entity. This can be arbitrary, but must be unique across the entire map The displayed model or particle system for this entity. For a func_static or other entity type which can have brushes as children, this key must be set to the same value as "name" if brushes are used The model or particle system to be used when this entity is lit (i.e. for torches). The model or particle system to be used when this torch entity is extinguished. lights fogs DarkRadiant-2.5.0/install/games/doom3.game000066400000000000000000000331451321750546400203350ustar00rootroot00000000000000 tga jpg dds materials mtr fonts dat 2 The origin of this object in 3D space The classname of this entity. This must be a recognised class defined in one of the DEF files, and governs the overall behaviour of the entity The displayed name of this entity. This can be arbitrary, but must be unique across the entire map The direction this entity initially faces. Generally applied to AI or player_start entities. The displayed model or particle system for this entity. For a func_static or other entity type which can have brushes as children, this key must be set to the same value as "name" if brushes are used The model or particle system to be used when this entity is lit (i.e. for torches). The model or particle system to be used when this torch entity is extinguished. lights fogs DarkRadiant-2.5.0/install/games/prey.game000066400000000000000000000231611321750546400202700ustar00rootroot00000000000000 tga jpg dds materials mtr fonts dat 2 The origin of this object in 3D space The classname of this entity. This must be a recognised class defined in one of the DEF files, and governs the overall behaviour of the entity The displayed name of this entity. This can be arbitrary, but must be unique across the entire map The direction this entity initially faces. Generally applied to AI or player_start entities. The displayed model or particle system for this entity. For a func_static or other entity type which can have brushes as children, this key must be set to the same value as "name" if brushes are used The model or particle system to be used when this entity is lit (i.e. for torches). The model or particle system to be used when this torch entity is extinguished. lights fogs DarkRadiant-2.5.0/install/games/quake3.game000066400000000000000000000230541321750546400205030ustar00rootroot00000000000000 tga jpg dds materials mtr fonts dat 2 The origin of this object in 3D space The classname of this entity. This must be a recognised class defined in one of the DEF files, and governs the overall behaviour of the entity The displayed name of this entity. This can be arbitrary, but must be unique across the entire map The direction this entity initially faces. Generally applied to AI or player_start entities. The displayed model or particle system for this entity. For a func_static or other entity type which can have brushes as children, this key must be set to the same value as "name" if brushes are used The model or particle system to be used when this entity is lit (i.e. for torches). The model or particle system to be used when this torch entity is extinguished. lights fogs DarkRadiant-2.5.0/install/games/quake4.game000066400000000000000000000232071321750546400205040ustar00rootroot00000000000000 tga jpg dds materials mtr fonts dat 2 The origin of this object in 3D space The classname of this entity. This must be a recognised class defined in one of the DEF files, and governs the overall behaviour of the entity The displayed name of this entity. This can be arbitrary, but must be unique across the entire map The direction this entity initially faces. Generally applied to AI or player_start entities. The displayed model or particle system for this entity. For a func_static or other entity type which can have brushes as children, this key must be set to the same value as "name" if brushes are used The model or particle system to be used when this entity is lit (i.e. for torches). The model or particle system to be used when this torch entity is extinguished. lights fogs DarkRadiant-2.5.0/install/games/xreal.game000066400000000000000000000330261321750546400204250ustar00rootroot00000000000000 tga png jpg dds materials mtr fonts dat 1 The origin of this object in 3D space The classname of this entity. This must be a recognised class defined in one of the DEF files, and governs the overall behaviour of the entity The displayed name of this entity. This can be arbitrary, but must be unique across the entire map The direction this entity initially faces. Generally applied to AI or player_start entities. The displayed model or particle system for this entity. For a func_static or other entity type which can have brushes as children, this key must be set to the same value as "name" if brushes are used The model or particle system to be used when this entity is lit (i.e. for torches). The model or particle system to be used when this torch entity is extinguished. lights fogs DarkRadiant-2.5.0/install/gl/000077500000000000000000000000001321750546400157615ustar00rootroot00000000000000DarkRadiant-2.5.0/install/gl/Makefile.shaders000066400000000000000000000004761321750546400210600ustar00rootroot00000000000000CGC = cgc CGCFLAGS_FP = -profile arbfp1 CGCFLAGS_VP = -profile arbvp1 all: interaction_fp.arb interaction_vp.arb interaction_fp.arb: interaction_fp.cg $(CGC) $(CGCFLAGS_FP) -o interaction_fp.arb interaction_fp.cg interaction_vp.arb: interaction_vp.cg $(CGC) $(CGCFLAGS_VP) -o interaction_vp.arb interaction_vp.cg DarkRadiant-2.5.0/install/gl/interaction_fp.arb000066400000000000000000000057441321750546400214650ustar00rootroot00000000000000!!ARBfp1.0 # cgc version 2.0.0015, build date May 15 2008 # command line args: -profile arbfp1 # source file: interaction_fp.cg #vendor NVIDIA Corporation #version 2.0.0.15 #profile arbfp1 #program main #semantic main.diffusemap : TEXUNIT0 #semantic main.bumpmap : TEXUNIT1 #semantic main.specularmap : TEXUNIT2 #semantic main.attenuationmap_xy : TEXUNIT3 #semantic main.attenuationmap_z : TEXUNIT4 #semantic main.view_origin : C4 #semantic main.light_origin : C2 #semantic main.light_color : C3 #semantic main.light_scale : C6 #semantic main.ambientFactor : C7 #var float4 IN.colour : $vin.COLOR : COL0 : 0 : 1 #var float4 IN.position : $vin.TEXCOORD0 : TEX0 : 0 : 1 #var float4 IN.tex_diffuse_bump : $vin.TEXCOORD1 : TEX1 : 0 : 1 #var float4 IN.tex_specular : $vin.TEXCOORD2 : TEX2 : 0 : 1 #var float4 IN.tex_atten_xy_z : $vin.TEXCOORD3 : TEX3 : 0 : 1 #var float3 IN.tangent : $vin.TEXCOORD4 : TEX4 : 0 : 1 #var float3 IN.binormal : $vin.TEXCOORD5 : TEX5 : 0 : 1 #var float3 IN.normal : $vin.TEXCOORD6 : TEX6 : 0 : 1 #var sampler2D diffusemap : TEXUNIT0 : texunit 0 : 1 : 1 #var sampler2D bumpmap : TEXUNIT1 : texunit 1 : 2 : 1 #var sampler2D specularmap : TEXUNIT2 : texunit 2 : 3 : 1 #var sampler2D attenuationmap_xy : TEXUNIT3 : texunit 3 : 4 : 1 #var sampler2D attenuationmap_z : TEXUNIT4 : texunit 4 : 5 : 1 #var float3 view_origin : C4 : c[4] : 6 : 1 #var float3 light_origin : C2 : c[2] : 7 : 1 #var float3 light_color : C3 : c[3] : 8 : 1 #var float3 light_scale : C6 : c[6] : 9 : 1 #var float ambientFactor : C7 : c[7] : 10 : 1 #var float4 main.color : $vout.COLOR : COL : -1 : 1 #const c[0] = 0.5 2 32 1 #const c[1] = 0 PARAM c[8] = { { 0.5, 2, 32, 1 }, { 0 }, program.local[2..7] }; TEMP R0; TEMP R1; TEMP R2; ADD R0.xyz, -fragment.texcoord[0], c[2]; DP3 R1.z, fragment.texcoord[6], R0; DP3 R1.x, fragment.texcoord[4], R0; DP3 R1.y, fragment.texcoord[5], R0; DP3 R0.x, R1, R1; RSQ R0.w, R0.x; MUL R2.xyz, R0.w, R1; ADD R0.xyz, -fragment.texcoord[0], c[4]; DP3 R1.z, fragment.texcoord[6], R0; DP3 R1.x, R0, fragment.texcoord[4]; DP3 R1.y, R0, fragment.texcoord[5]; DP3 R0.w, R1, R1; RSQ R0.w, R0.w; MAD R1.xyz, R0.w, R1, R2; DP3 R1.w, R1, R1; RSQ R1.w, R1.w; TEX R0.xyz, fragment.texcoord[1].zwzw, texture[1], 2D; ADD R0.xyz, R0, -c[0].x; MUL R0.xyz, R0, c[0].y; DP3 R0.w, R0, R0; RSQ R0.w, R0.w; MUL R0.xyz, R0.w, R0; MUL R1.xyz, R1.w, R1; DP3_SAT R0.w, R0, R1; DP3 R0.x, R0, R2; MOV R1.x, c[0].w; ADD R0.y, R1.x, -c[7].x; MAD_SAT R2.x, R0.y, R0, c[7]; MOV R0.xyz, c[6]; MUL R0.xyz, R0, c[3]; MUL R0.xyz, R0, R2.x; TEX R1, fragment.texcoord[1], texture[0], 2D; MUL R1.xyz, R1, R0; MUL R1, R1, fragment.color.primary; TEX R0.xyz, fragment.texcoord[2], texture[2], 2D; TXP R2.xyz, fragment.texcoord[3], texture[3], 2D; POW R0.w, R0.w, c[0].z; MUL R0.xyz, R0, c[3]; MAD R0.xyz, R0, R0.w, R1; CMP R2.xyz, -fragment.texcoord[3].w, R2, c[1].x; MOV R1.y, c[0].x; MOV R1.x, fragment.texcoord[3].z; TEX R1.xyz, R1, texture[4], 2D; MUL R0.xyz, R0, R2; MUL result.color.xyz, R0, R1; MOV result.color.w, R1; END # 46 instructions, 3 R-regs DarkRadiant-2.5.0/install/gl/interaction_fp.cg000066400000000000000000000065631321750546400213120ustar00rootroot00000000000000/// ============================================================================ /* Copyright (C) 2004 Robert Beckebans Please see the file "AUTHORS" for a list of contributors 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /// ============================================================================ #include "utils.cg" struct cg_vertex2fragment { float4 colour : COLOR; float4 position : TEXCOORD0; float4 tex_diffuse_bump : TEXCOORD1; float4 tex_specular : TEXCOORD2; float4 tex_atten_xy_z : TEXCOORD3; float3 tangent : TEXCOORD4; float3 binormal : TEXCOORD5; float3 normal : TEXCOORD6; }; struct cg_fragment2final { float4 color : COLOR; }; cg_fragment2final main(cg_vertex2fragment IN, uniform sampler2D diffusemap : TEXUNIT0, uniform sampler2D bumpmap : TEXUNIT1, uniform sampler2D specularmap : TEXUNIT2, uniform sampler2D attenuationmap_xy : TEXUNIT3, uniform sampler2D attenuationmap_z : TEXUNIT4, uniform float3 view_origin : C4, uniform float3 light_origin : C2, uniform float3 light_color : C3, uniform float3 light_scale: C6, uniform float ambientFactor: C7) { cg_fragment2final OUT; // construct object-space-to-tangent-space 3x3 matrix float3x3 rotation = float3x3(IN.tangent, IN.binormal, IN.normal); // compute view direction in tangent space float3 V = normalize(mul(rotation, view_origin - IN.position.xyz)); // compute light direction in tangent space float3 L = normalize(mul(rotation, (light_origin - IN.position.xyz))); // compute half angle in tangent space float3 H = normalize(L + V); // compute normal in tangent space from bumpmap float3 T = CG_Expand(tex2D(bumpmap, IN.tex_diffuse_bump.zw).xyz); float3 N = normalize(T); // compute the diffuse term float4 diffuse = tex2D(diffusemap, IN.tex_diffuse_bump.xy); diffuse.rgb *= light_color * light_scale * saturate((1 - ambientFactor) * dot(N, L) + ambientFactor); // compute the specular term float3 specular = tex2D(specularmap, IN.tex_specular.xy).rgb * light_color * pow(saturate(dot(N, H)), 32); // Lookup values from attenuation textures float3 attenuation_xy = tex2Dproj( attenuationmap_xy, IN.tex_atten_xy_z.xyw ).rgb; float3 attenuation_z = tex2D( attenuationmap_z, float2(IN.tex_atten_xy_z.z, 0.5) ).rgb; // Set colour to black if the w value is negative, this means we are behind // the origin of a projected light if (IN.tex_atten_xy_z.w <= 0) attenuation_xy.rgb = 0; // multiply in vertex colour diffuse *= IN.colour; // compute final color OUT.color.rgba = diffuse; OUT.color.rgb += specular; OUT.color.rgb *= attenuation_xy; OUT.color.rgb *= attenuation_z; return OUT; } DarkRadiant-2.5.0/install/gl/interaction_fp.glsl000066400000000000000000000062721321750546400216570ustar00rootroot00000000000000/// ============================================================================ /* Copyright (C) 2004 Robert Beckebans Please see the file "CONTRIBUTORS" for a list of contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /// ============================================================================ uniform sampler2D u_diffusemap; uniform sampler2D u_bumpmap; uniform sampler2D u_specularmap; uniform sampler2D u_attenuationmap_xy; uniform sampler2D u_attenuationmap_z; uniform vec3 u_view_origin; uniform vec3 u_light_origin; uniform vec3 u_light_color; uniform float u_light_scale; // Vertex colour parameters. Vertex colour is calculated as // (colour * scale + offset), so for normal vertex colouring scale should be 1.0 // and offset 0.0, and for inverse colouring scale should be -1.0 and offset // 1.0. uniform float u_vcol_scale; uniform float u_vcol_offset; varying vec3 var_vertex; varying vec4 var_tex_diffuse_bump; varying vec2 var_tex_specular; varying vec4 var_tex_atten_xy_z; varying mat3 var_mat_os2ts; void main() { // compute view direction in tangent space vec3 V = normalize(var_mat_os2ts * (u_view_origin - var_vertex)); // compute light direction in tangent space vec3 L = normalize(var_mat_os2ts * (u_light_origin - var_vertex)); // compute half angle in tangent space vec3 H = normalize(L + V); // compute normal in tangent space from bumpmap vec3 N = 2.0 * (texture2D(u_bumpmap, var_tex_diffuse_bump.pq).xyz - 0.5); N = normalize(N); // compute the diffuse term vec4 diffuse = texture2D(u_diffusemap, var_tex_diffuse_bump.st); diffuse.rgb *= u_light_color * u_light_scale * clamp(dot(N, L), 0.0, 1.0); // compute the specular term float specIntensity = clamp(dot(N, H), 0.0, 1.0); specIntensity = pow(specIntensity, 32.0); vec3 specular = texture2D(u_specularmap, var_tex_specular.xy).rgb * u_light_color * specIntensity; // compute attenuation vec3 attenuation_xy = vec3(0.0, 0.0, 0.0); if (var_tex_atten_xy_z.w > 0.0) attenuation_xy = texture2DProj( u_attenuationmap_xy, var_tex_atten_xy_z.xyw ).rgb; vec3 attenuation_z = texture2D( u_attenuationmap_z, vec2(var_tex_atten_xy_z.z, 0.5) ).rgb; // compute final color gl_FragColor = diffuse * (gl_Color * u_vcol_scale + u_vcol_offset); gl_FragColor.rgb *= 2.0; // replacement for RENDER_SCREEN light scaling gl_FragColor.rgb += specular; gl_FragColor.rgb *= attenuation_xy; gl_FragColor.rgb *= attenuation_z; } DarkRadiant-2.5.0/install/gl/interaction_vp.arb000066400000000000000000001007251321750546400215000ustar00rootroot00000000000000!!ARBvp1.0 # cgc version 2.0.0015, build date May 15 2008 # command line args: -profile arbvp1 # source file: interaction_vp.cg #vendor NVIDIA Corporation #version 2.0.0.15 #profile arbvp1 #program main #semantic glstate : state #var float4 IN.colour : $vin.COLOR : COLOR0 : 0 : 1 #var float4 IN.position : $vin.POSITION : POSITION : 0 : 1 #var float4 IN.tex0 : $vin.ATTR8 : ATTR8 : 0 : 1 #var float3 IN.tangent : $vin.ATTR9 : ATTR9 : 0 : 1 #var float3 IN.binormal : $vin.ATTR10 : ATTR10 : 0 : 1 #var float3 IN.normal : $vin.ATTR11 : ATTR11 : 0 : 1 #var float4 glstate.material.ambient : state.material.ambient : : -1 : 0 #var float4 glstate.material.diffuse : state.material.diffuse : : -1 : 0 #var float4 glstate.material.specular : state.material.specular : : -1 : 0 #var float4 glstate.material.emission : state.material.emission : : -1 : 0 #var float4 glstate.material.shininess : state.material.shininess : : -1 : 0 #var float4 glstate.material.front.ambient : state.material.front.ambient : : -1 : 0 #var float4 glstate.material.front.diffuse : state.material.front.diffuse : : -1 : 0 #var float4 glstate.material.front.specular : state.material.front.specular : : -1 : 0 #var float4 glstate.material.front.emission : state.material.front.emission : : -1 : 0 #var float4 glstate.material.front.shininess : state.material.front.shininess : : -1 : 0 #var float4 glstate.material.back.ambient : state.material.back.ambient : : -1 : 0 #var float4 glstate.material.back.diffuse : state.material.back.diffuse : : -1 : 0 #var float4 glstate.material.back.specular : state.material.back.specular : : -1 : 0 #var float4 glstate.material.back.emission : state.material.back.emission : : -1 : 0 #var float4 glstate.material.back.shininess : state.material.back.shininess : : -1 : 0 #var float4 glstate.light[0].ambient : state.light[0].ambient : : -1 : 0 #var float4 glstate.light[0].diffuse : state.light[0].diffuse : : -1 : 0 #var float4 glstate.light[0].specular : state.light[0].specular : : -1 : 0 #var float4 glstate.light[0].position : state.light[0].position : : -1 : 0 #var float4 glstate.light[0].attenuation : state.light[0].attenuation : : -1 : 0 #var float4 glstate.light[0].spot.direction : state.light[0].spot.direction : : -1 : 0 #var float4 glstate.light[0].half : state.light[0].half : : -1 : 0 #var float4 glstate.light[1].ambient : state.light[1].ambient : : -1 : 0 #var float4 glstate.light[1].diffuse : state.light[1].diffuse : : -1 : 0 #var float4 glstate.light[1].specular : state.light[1].specular : : -1 : 0 #var float4 glstate.light[1].position : state.light[1].position : : -1 : 0 #var float4 glstate.light[1].attenuation : state.light[1].attenuation : : -1 : 0 #var float4 glstate.light[1].spot.direction : state.light[1].spot.direction : : -1 : 0 #var float4 glstate.light[1].half : state.light[1].half : : -1 : 0 #var float4 glstate.light[2].ambient : state.light[2].ambient : : -1 : 0 #var float4 glstate.light[2].diffuse : state.light[2].diffuse : : -1 : 0 #var float4 glstate.light[2].specular : state.light[2].specular : : -1 : 0 #var float4 glstate.light[2].position : state.light[2].position : : -1 : 0 #var float4 glstate.light[2].attenuation : state.light[2].attenuation : : -1 : 0 #var float4 glstate.light[2].spot.direction : state.light[2].spot.direction : : -1 : 0 #var float4 glstate.light[2].half : state.light[2].half : : -1 : 0 #var float4 glstate.light[3].ambient : state.light[3].ambient : : -1 : 0 #var float4 glstate.light[3].diffuse : state.light[3].diffuse : : -1 : 0 #var float4 glstate.light[3].specular : state.light[3].specular : : -1 : 0 #var float4 glstate.light[3].position : state.light[3].position : : -1 : 0 #var float4 glstate.light[3].attenuation : state.light[3].attenuation : : -1 : 0 #var float4 glstate.light[3].spot.direction : state.light[3].spot.direction : : -1 : 0 #var float4 glstate.light[3].half : state.light[3].half : : -1 : 0 #var float4 glstate.light[4].ambient : state.light[4].ambient : : -1 : 0 #var float4 glstate.light[4].diffuse : state.light[4].diffuse : : -1 : 0 #var float4 glstate.light[4].specular : state.light[4].specular : : -1 : 0 #var float4 glstate.light[4].position : state.light[4].position : : -1 : 0 #var float4 glstate.light[4].attenuation : state.light[4].attenuation : : -1 : 0 #var float4 glstate.light[4].spot.direction : state.light[4].spot.direction : : -1 : 0 #var float4 glstate.light[4].half : state.light[4].half : : -1 : 0 #var float4 glstate.light[5].ambient : state.light[5].ambient : : -1 : 0 #var float4 glstate.light[5].diffuse : state.light[5].diffuse : : -1 : 0 #var float4 glstate.light[5].specular : state.light[5].specular : : -1 : 0 #var float4 glstate.light[5].position : state.light[5].position : : -1 : 0 #var float4 glstate.light[5].attenuation : state.light[5].attenuation : : -1 : 0 #var float4 glstate.light[5].spot.direction : state.light[5].spot.direction : : -1 : 0 #var float4 glstate.light[5].half : state.light[5].half : : -1 : 0 #var float4 glstate.light[6].ambient : state.light[6].ambient : : -1 : 0 #var float4 glstate.light[6].diffuse : state.light[6].diffuse : : -1 : 0 #var float4 glstate.light[6].specular : state.light[6].specular : : -1 : 0 #var float4 glstate.light[6].position : state.light[6].position : : -1 : 0 #var float4 glstate.light[6].attenuation : state.light[6].attenuation : : -1 : 0 #var float4 glstate.light[6].spot.direction : state.light[6].spot.direction : : -1 : 0 #var float4 glstate.light[6].half : state.light[6].half : : -1 : 0 #var float4 glstate.light[7].ambient : state.light[7].ambient : : -1 : 0 #var float4 glstate.light[7].diffuse : state.light[7].diffuse : : -1 : 0 #var float4 glstate.light[7].specular : state.light[7].specular : : -1 : 0 #var float4 glstate.light[7].position : state.light[7].position : : -1 : 0 #var float4 glstate.light[7].attenuation : state.light[7].attenuation : : -1 : 0 #var float4 glstate.light[7].spot.direction : state.light[7].spot.direction : : -1 : 0 #var float4 glstate.light[7].half : state.light[7].half : : -1 : 0 #var float4 glstate.lightmodel.ambient : state.lightmodel.ambient : : -1 : 0 #var float4 glstate.lightmodel.scenecolor : state.lightmodel.scenecolor : : -1 : 0 #var float4 glstate.lightmodel.front.scenecolor : state.lightmodel.front.scenecolor : : -1 : 0 #var float4 glstate.lightmodel.back.scenecolor : state.lightmodel.back.scenecolor : : -1 : 0 #var float4 glstate.lightprod[0].ambient : state.lightprod[0].ambient : : -1 : 0 #var float4 glstate.lightprod[0].diffuse : state.lightprod[0].diffuse : : -1 : 0 #var float4 glstate.lightprod[0].specular : state.lightprod[0].specular : : -1 : 0 #var float4 glstate.lightprod[0].front.ambient : state.lightprod[0].front.ambient : : -1 : 0 #var float4 glstate.lightprod[0].front.diffuse : state.lightprod[0].front.diffuse : : -1 : 0 #var float4 glstate.lightprod[0].front.specular : state.lightprod[0].front.specular : : -1 : 0 #var float4 glstate.lightprod[0].back.ambient : state.lightprod[0].back.ambient : : -1 : 0 #var float4 glstate.lightprod[0].back.diffuse : state.lightprod[0].back.diffuse : : -1 : 0 #var float4 glstate.lightprod[0].back.specular : state.lightprod[0].back.specular : : -1 : 0 #var float4 glstate.lightprod[1].ambient : state.lightprod[1].ambient : : -1 : 0 #var float4 glstate.lightprod[1].diffuse : state.lightprod[1].diffuse : : -1 : 0 #var float4 glstate.lightprod[1].specular : state.lightprod[1].specular : : -1 : 0 #var float4 glstate.lightprod[1].front.ambient : state.lightprod[1].front.ambient : : -1 : 0 #var float4 glstate.lightprod[1].front.diffuse : state.lightprod[1].front.diffuse : : -1 : 0 #var float4 glstate.lightprod[1].front.specular : state.lightprod[1].front.specular : : -1 : 0 #var float4 glstate.lightprod[1].back.ambient : state.lightprod[1].back.ambient : : -1 : 0 #var float4 glstate.lightprod[1].back.diffuse : state.lightprod[1].back.diffuse : : -1 : 0 #var float4 glstate.lightprod[1].back.specular : state.lightprod[1].back.specular : : -1 : 0 #var float4 glstate.lightprod[2].ambient : state.lightprod[2].ambient : : -1 : 0 #var float4 glstate.lightprod[2].diffuse : state.lightprod[2].diffuse : : -1 : 0 #var float4 glstate.lightprod[2].specular : state.lightprod[2].specular : : -1 : 0 #var float4 glstate.lightprod[2].front.ambient : state.lightprod[2].front.ambient : : -1 : 0 #var float4 glstate.lightprod[2].front.diffuse : state.lightprod[2].front.diffuse : : -1 : 0 #var float4 glstate.lightprod[2].front.specular : state.lightprod[2].front.specular : : -1 : 0 #var float4 glstate.lightprod[2].back.ambient : state.lightprod[2].back.ambient : : -1 : 0 #var float4 glstate.lightprod[2].back.diffuse : state.lightprod[2].back.diffuse : : -1 : 0 #var float4 glstate.lightprod[2].back.specular : state.lightprod[2].back.specular : : -1 : 0 #var float4 glstate.lightprod[3].ambient : state.lightprod[3].ambient : : -1 : 0 #var float4 glstate.lightprod[3].diffuse : state.lightprod[3].diffuse : : -1 : 0 #var float4 glstate.lightprod[3].specular : state.lightprod[3].specular : : -1 : 0 #var float4 glstate.lightprod[3].front.ambient : state.lightprod[3].front.ambient : : -1 : 0 #var float4 glstate.lightprod[3].front.diffuse : state.lightprod[3].front.diffuse : : -1 : 0 #var float4 glstate.lightprod[3].front.specular : state.lightprod[3].front.specular : : -1 : 0 #var float4 glstate.lightprod[3].back.ambient : state.lightprod[3].back.ambient : : -1 : 0 #var float4 glstate.lightprod[3].back.diffuse : state.lightprod[3].back.diffuse : : -1 : 0 #var float4 glstate.lightprod[3].back.specular : state.lightprod[3].back.specular : : -1 : 0 #var float4 glstate.lightprod[4].ambient : state.lightprod[4].ambient : : -1 : 0 #var float4 glstate.lightprod[4].diffuse : state.lightprod[4].diffuse : : -1 : 0 #var float4 glstate.lightprod[4].specular : state.lightprod[4].specular : : -1 : 0 #var float4 glstate.lightprod[4].front.ambient : state.lightprod[4].front.ambient : : -1 : 0 #var float4 glstate.lightprod[4].front.diffuse : state.lightprod[4].front.diffuse : : -1 : 0 #var float4 glstate.lightprod[4].front.specular : state.lightprod[4].front.specular : : -1 : 0 #var float4 glstate.lightprod[4].back.ambient : state.lightprod[4].back.ambient : : -1 : 0 #var float4 glstate.lightprod[4].back.diffuse : state.lightprod[4].back.diffuse : : -1 : 0 #var float4 glstate.lightprod[4].back.specular : state.lightprod[4].back.specular : : -1 : 0 #var float4 glstate.lightprod[5].ambient : state.lightprod[5].ambient : : -1 : 0 #var float4 glstate.lightprod[5].diffuse : state.lightprod[5].diffuse : : -1 : 0 #var float4 glstate.lightprod[5].specular : state.lightprod[5].specular : : -1 : 0 #var float4 glstate.lightprod[5].front.ambient : state.lightprod[5].front.ambient : : -1 : 0 #var float4 glstate.lightprod[5].front.diffuse : state.lightprod[5].front.diffuse : : -1 : 0 #var float4 glstate.lightprod[5].front.specular : state.lightprod[5].front.specular : : -1 : 0 #var float4 glstate.lightprod[5].back.ambient : state.lightprod[5].back.ambient : : -1 : 0 #var float4 glstate.lightprod[5].back.diffuse : state.lightprod[5].back.diffuse : : -1 : 0 #var float4 glstate.lightprod[5].back.specular : state.lightprod[5].back.specular : : -1 : 0 #var float4 glstate.lightprod[6].ambient : state.lightprod[6].ambient : : -1 : 0 #var float4 glstate.lightprod[6].diffuse : state.lightprod[6].diffuse : : -1 : 0 #var float4 glstate.lightprod[6].specular : state.lightprod[6].specular : : -1 : 0 #var float4 glstate.lightprod[6].front.ambient : state.lightprod[6].front.ambient : : -1 : 0 #var float4 glstate.lightprod[6].front.diffuse : state.lightprod[6].front.diffuse : : -1 : 0 #var float4 glstate.lightprod[6].front.specular : state.lightprod[6].front.specular : : -1 : 0 #var float4 glstate.lightprod[6].back.ambient : state.lightprod[6].back.ambient : : -1 : 0 #var float4 glstate.lightprod[6].back.diffuse : state.lightprod[6].back.diffuse : : -1 : 0 #var float4 glstate.lightprod[6].back.specular : state.lightprod[6].back.specular : : -1 : 0 #var float4 glstate.lightprod[7].ambient : state.lightprod[7].ambient : : -1 : 0 #var float4 glstate.lightprod[7].diffuse : state.lightprod[7].diffuse : : -1 : 0 #var float4 glstate.lightprod[7].specular : state.lightprod[7].specular : : -1 : 0 #var float4 glstate.lightprod[7].front.ambient : state.lightprod[7].front.ambient : : -1 : 0 #var float4 glstate.lightprod[7].front.diffuse : state.lightprod[7].front.diffuse : : -1 : 0 #var float4 glstate.lightprod[7].front.specular : state.lightprod[7].front.specular : : -1 : 0 #var float4 glstate.lightprod[7].back.ambient : state.lightprod[7].back.ambient : : -1 : 0 #var float4 glstate.lightprod[7].back.diffuse : state.lightprod[7].back.diffuse : : -1 : 0 #var float4 glstate.lightprod[7].back.specular : state.lightprod[7].back.specular : : -1 : 0 #var float4 glstate.texgen[0].eye.s : state.texgen[0].eye.s : : -1 : 0 #var float4 glstate.texgen[0].eye.t : state.texgen[0].eye.t : : -1 : 0 #var float4 glstate.texgen[0].eye.r : state.texgen[0].eye.r : : -1 : 0 #var float4 glstate.texgen[0].eye.q : state.texgen[0].eye.q : : -1 : 0 #var float4 glstate.texgen[0].object.s : state.texgen[0].object.s : : -1 : 0 #var float4 glstate.texgen[0].object.t : state.texgen[0].object.t : : -1 : 0 #var float4 glstate.texgen[0].object.r : state.texgen[0].object.r : : -1 : 0 #var float4 glstate.texgen[0].object.q : state.texgen[0].object.q : : -1 : 0 #var float4 glstate.texgen[1].eye.s : state.texgen[1].eye.s : : -1 : 0 #var float4 glstate.texgen[1].eye.t : state.texgen[1].eye.t : : -1 : 0 #var float4 glstate.texgen[1].eye.r : state.texgen[1].eye.r : : -1 : 0 #var float4 glstate.texgen[1].eye.q : state.texgen[1].eye.q : : -1 : 0 #var float4 glstate.texgen[1].object.s : state.texgen[1].object.s : : -1 : 0 #var float4 glstate.texgen[1].object.t : state.texgen[1].object.t : : -1 : 0 #var float4 glstate.texgen[1].object.r : state.texgen[1].object.r : : -1 : 0 #var float4 glstate.texgen[1].object.q : state.texgen[1].object.q : : -1 : 0 #var float4 glstate.texgen[2].eye.s : state.texgen[2].eye.s : : -1 : 0 #var float4 glstate.texgen[2].eye.t : state.texgen[2].eye.t : : -1 : 0 #var float4 glstate.texgen[2].eye.r : state.texgen[2].eye.r : : -1 : 0 #var float4 glstate.texgen[2].eye.q : state.texgen[2].eye.q : : -1 : 0 #var float4 glstate.texgen[2].object.s : state.texgen[2].object.s : : -1 : 0 #var float4 glstate.texgen[2].object.t : state.texgen[2].object.t : : -1 : 0 #var float4 glstate.texgen[2].object.r : state.texgen[2].object.r : : -1 : 0 #var float4 glstate.texgen[2].object.q : state.texgen[2].object.q : : -1 : 0 #var float4 glstate.texgen[3].eye.s : state.texgen[3].eye.s : : -1 : 0 #var float4 glstate.texgen[3].eye.t : state.texgen[3].eye.t : : -1 : 0 #var float4 glstate.texgen[3].eye.r : state.texgen[3].eye.r : : -1 : 0 #var float4 glstate.texgen[3].eye.q : state.texgen[3].eye.q : : -1 : 0 #var float4 glstate.texgen[3].object.s : state.texgen[3].object.s : : -1 : 0 #var float4 glstate.texgen[3].object.t : state.texgen[3].object.t : : -1 : 0 #var float4 glstate.texgen[3].object.r : state.texgen[3].object.r : : -1 : 0 #var float4 glstate.texgen[3].object.q : state.texgen[3].object.q : : -1 : 0 #var float4 glstate.texgen[4].eye.s : state.texgen[4].eye.s : : -1 : 0 #var float4 glstate.texgen[4].eye.t : state.texgen[4].eye.t : : -1 : 0 #var float4 glstate.texgen[4].eye.r : state.texgen[4].eye.r : : -1 : 0 #var float4 glstate.texgen[4].eye.q : state.texgen[4].eye.q : : -1 : 0 #var float4 glstate.texgen[4].object.s : state.texgen[4].object.s : : -1 : 0 #var float4 glstate.texgen[4].object.t : state.texgen[4].object.t : : -1 : 0 #var float4 glstate.texgen[4].object.r : state.texgen[4].object.r : : -1 : 0 #var float4 glstate.texgen[4].object.q : state.texgen[4].object.q : : -1 : 0 #var float4 glstate.texgen[5].eye.s : state.texgen[5].eye.s : : -1 : 0 #var float4 glstate.texgen[5].eye.t : state.texgen[5].eye.t : : -1 : 0 #var float4 glstate.texgen[5].eye.r : state.texgen[5].eye.r : : -1 : 0 #var float4 glstate.texgen[5].eye.q : state.texgen[5].eye.q : : -1 : 0 #var float4 glstate.texgen[5].object.s : state.texgen[5].object.s : : -1 : 0 #var float4 glstate.texgen[5].object.t : state.texgen[5].object.t : : -1 : 0 #var float4 glstate.texgen[5].object.r : state.texgen[5].object.r : : -1 : 0 #var float4 glstate.texgen[5].object.q : state.texgen[5].object.q : : -1 : 0 #var float4 glstate.texgen[6].eye.s : state.texgen[6].eye.s : : -1 : 0 #var float4 glstate.texgen[6].eye.t : state.texgen[6].eye.t : : -1 : 0 #var float4 glstate.texgen[6].eye.r : state.texgen[6].eye.r : : -1 : 0 #var float4 glstate.texgen[6].eye.q : state.texgen[6].eye.q : : -1 : 0 #var float4 glstate.texgen[6].object.s : state.texgen[6].object.s : : -1 : 0 #var float4 glstate.texgen[6].object.t : state.texgen[6].object.t : : -1 : 0 #var float4 glstate.texgen[6].object.r : state.texgen[6].object.r : : -1 : 0 #var float4 glstate.texgen[6].object.q : state.texgen[6].object.q : : -1 : 0 #var float4 glstate.texgen[7].eye.s : state.texgen[7].eye.s : : -1 : 0 #var float4 glstate.texgen[7].eye.t : state.texgen[7].eye.t : : -1 : 0 #var float4 glstate.texgen[7].eye.r : state.texgen[7].eye.r : : -1 : 0 #var float4 glstate.texgen[7].eye.q : state.texgen[7].eye.q : : -1 : 0 #var float4 glstate.texgen[7].object.s : state.texgen[7].object.s : : -1 : 0 #var float4 glstate.texgen[7].object.t : state.texgen[7].object.t : : -1 : 0 #var float4 glstate.texgen[7].object.r : state.texgen[7].object.r : : -1 : 0 #var float4 glstate.texgen[7].object.q : state.texgen[7].object.q : : -1 : 0 #var float4 glstate.fog.color : state.fog.color : : -1 : 0 #var float4 glstate.fog.params : state.fog.params : : -1 : 0 #var float4 glstate.clip[0].plane : state.clip[0].plane : : -1 : 0 #var float4 glstate.clip[1].plane : state.clip[1].plane : : -1 : 0 #var float4 glstate.clip[2].plane : state.clip[2].plane : : -1 : 0 #var float4 glstate.clip[3].plane : state.clip[3].plane : : -1 : 0 #var float4 glstate.clip[4].plane : state.clip[4].plane : : -1 : 0 #var float4 glstate.clip[5].plane : state.clip[5].plane : : -1 : 0 #var float4 glstate.clip[6].plane : state.clip[6].plane : : -1 : 0 #var float4 glstate.clip[7].plane : state.clip[7].plane : : -1 : 0 #var float glstate.point.size : state.point.size : : -1 : 0 #var float glstate.point.attenuation : state.point.attenuation : : -1 : 0 #var float4x4 glstate.matrix.modelview[0] : state.matrix.modelview[0] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[1] : state.matrix.modelview[1] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[2] : state.matrix.modelview[2] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[3] : state.matrix.modelview[3] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[4] : state.matrix.modelview[4] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[5] : state.matrix.modelview[5] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[6] : state.matrix.modelview[6] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[7] : state.matrix.modelview[7] : , 4 : -1 : 0 #var float4x4 glstate.matrix.projection : state.matrix.projection : , 4 : -1 : 0 #var float4x4 glstate.matrix.mvp : state.matrix.mvp : c[1], 4 : -1 : 1 #var float4x4 glstate.matrix.texture[0] : state.matrix.texture[0] : c[5], 4 : -1 : 1 #var float4x4 glstate.matrix.texture[1] : state.matrix.texture[1] : c[9], 4 : -1 : 1 #var float4x4 glstate.matrix.texture[2] : state.matrix.texture[2] : c[13], 4 : -1 : 1 #var float4x4 glstate.matrix.texture[3] : state.matrix.texture[3] : c[17], 4 : -1 : 1 #var float4x4 glstate.matrix.texture[4] : state.matrix.texture[4] : , 4 : -1 : 0 #var float4x4 glstate.matrix.texture[5] : state.matrix.texture[5] : , 4 : -1 : 0 #var float4x4 glstate.matrix.texture[6] : state.matrix.texture[6] : , 4 : -1 : 0 #var float4x4 glstate.matrix.texture[7] : state.matrix.texture[7] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[0] : state.matrix.palette[0] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[1] : state.matrix.palette[1] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[2] : state.matrix.palette[2] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[3] : state.matrix.palette[3] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[4] : state.matrix.palette[4] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[5] : state.matrix.palette[5] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[6] : state.matrix.palette[6] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[7] : state.matrix.palette[7] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[0] : state.matrix.program[0] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[1] : state.matrix.program[1] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[2] : state.matrix.program[2] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[3] : state.matrix.program[3] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[4] : state.matrix.program[4] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[5] : state.matrix.program[5] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[6] : state.matrix.program[6] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[7] : state.matrix.program[7] : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[0] : state.matrix.modelview[0].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[1] : state.matrix.modelview[1].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[2] : state.matrix.modelview[2].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[3] : state.matrix.modelview[3].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[4] : state.matrix.modelview[4].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[5] : state.matrix.modelview[5].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[6] : state.matrix.modelview[6].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[7] : state.matrix.modelview[7].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.projection : state.matrix.projection.inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.mvp : state.matrix.mvp.inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[0] : state.matrix.texture[0].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[1] : state.matrix.texture[1].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[2] : state.matrix.texture[2].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[3] : state.matrix.texture[3].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[4] : state.matrix.texture[4].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[5] : state.matrix.texture[5].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[6] : state.matrix.texture[6].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[7] : state.matrix.texture[7].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[0] : state.matrix.palette[0].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[1] : state.matrix.palette[1].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[2] : state.matrix.palette[2].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[3] : state.matrix.palette[3].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[4] : state.matrix.palette[4].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[5] : state.matrix.palette[5].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[6] : state.matrix.palette[6].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[7] : state.matrix.palette[7].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[0] : state.matrix.program[0].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[1] : state.matrix.program[1].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[2] : state.matrix.program[2].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[3] : state.matrix.program[3].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[4] : state.matrix.program[4].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[5] : state.matrix.program[5].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[6] : state.matrix.program[6].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[7] : state.matrix.program[7].inverse : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[0] : state.matrix.modelview[0].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[1] : state.matrix.modelview[1].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[2] : state.matrix.modelview[2].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[3] : state.matrix.modelview[3].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[4] : state.matrix.modelview[4].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[5] : state.matrix.modelview[5].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[6] : state.matrix.modelview[6].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[7] : state.matrix.modelview[7].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.projection : state.matrix.projection.transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.mvp : state.matrix.mvp.transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[0] : state.matrix.texture[0].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[1] : state.matrix.texture[1].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[2] : state.matrix.texture[2].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[3] : state.matrix.texture[3].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[4] : state.matrix.texture[4].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[5] : state.matrix.texture[5].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[6] : state.matrix.texture[6].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[7] : state.matrix.texture[7].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[0] : state.matrix.palette[0].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[1] : state.matrix.palette[1].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[2] : state.matrix.palette[2].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[3] : state.matrix.palette[3].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[4] : state.matrix.palette[4].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[5] : state.matrix.palette[5].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[6] : state.matrix.palette[6].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[7] : state.matrix.palette[7].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[0] : state.matrix.program[0].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[1] : state.matrix.program[1].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[2] : state.matrix.program[2].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[3] : state.matrix.program[3].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[4] : state.matrix.program[4].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[5] : state.matrix.program[5].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[6] : state.matrix.program[6].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[7] : state.matrix.program[7].transpose : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[0] : state.matrix.modelview[0].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[1] : state.matrix.modelview[1].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[2] : state.matrix.modelview[2].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[3] : state.matrix.modelview[3].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[4] : state.matrix.modelview[4].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[5] : state.matrix.modelview[5].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[6] : state.matrix.modelview[6].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[7] : state.matrix.modelview[7].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.projection : state.matrix.projection.invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.mvp : state.matrix.mvp.invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[0] : state.matrix.texture[0].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[1] : state.matrix.texture[1].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[2] : state.matrix.texture[2].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[3] : state.matrix.texture[3].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[4] : state.matrix.texture[4].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[5] : state.matrix.texture[5].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[6] : state.matrix.texture[6].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[7] : state.matrix.texture[7].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[0] : state.matrix.palette[0].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[1] : state.matrix.palette[1].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[2] : state.matrix.palette[2].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[3] : state.matrix.palette[3].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[4] : state.matrix.palette[4].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[5] : state.matrix.palette[5].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[6] : state.matrix.palette[6].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[7] : state.matrix.palette[7].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[0] : state.matrix.program[0].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[1] : state.matrix.program[1].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[2] : state.matrix.program[2].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[3] : state.matrix.program[3].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[4] : state.matrix.program[4].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[5] : state.matrix.program[5].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[6] : state.matrix.program[6].invtrans : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[7] : state.matrix.program[7].invtrans : , 4 : -1 : 0 #var float4 main.colour : $vout.COLOR : COL0 : -1 : 1 #var float4 main.hposition : $vout.POSITION : HPOS : -1 : 1 #var float4 main.position : $vout.TEXCOORD0 : TEX0 : -1 : 1 #var float4 main.tex_diffuse_bump : $vout.TEXCOORD1 : TEX1 : -1 : 1 #var float4 main.tex_specular : $vout.TEXCOORD2 : TEX2 : -1 : 1 #var float4 main.tex_atten_xy_z : $vout.TEXCOORD3 : TEX3 : -1 : 1 #var float3 main.tangent : $vout.TEXCOORD4 : TEX4 : -1 : 1 #var float3 main.binormal : $vout.TEXCOORD5 : TEX5 : -1 : 1 #var float3 main.normal : $vout.TEXCOORD6 : TEX6 : -1 : 1 PARAM c[21] = { program.local[0], state.matrix.mvp, state.matrix.texture[0], state.matrix.texture[1], state.matrix.texture[2], state.matrix.texture[3] }; TEMP R0; DP4 R0.x, vertex.attrib[8], c[9]; DP4 R0.y, vertex.attrib[8], c[10]; MOV result.color, vertex.color; MOV result.texcoord[0], vertex.position; MOV result.texcoord[4].xyz, vertex.attrib[9]; MOV result.texcoord[5].xyz, vertex.attrib[10]; MOV result.texcoord[6].xyz, vertex.attrib[11]; MOV result.texcoord[1].zw, R0.xyxy; DP4 result.position.w, vertex.position, c[4]; DP4 result.position.z, vertex.position, c[3]; DP4 result.position.y, vertex.position, c[2]; DP4 result.position.x, vertex.position, c[1]; DP4 result.texcoord[1].y, vertex.attrib[8], c[6]; DP4 result.texcoord[1].x, vertex.attrib[8], c[5]; DP4 result.texcoord[2].w, vertex.attrib[8], c[16]; DP4 result.texcoord[2].z, vertex.attrib[8], c[15]; DP4 result.texcoord[2].y, vertex.attrib[8], c[14]; DP4 result.texcoord[2].x, vertex.attrib[8], c[13]; DP4 result.texcoord[3].w, vertex.position, c[20]; DP4 result.texcoord[3].z, vertex.position, c[19]; DP4 result.texcoord[3].y, vertex.position, c[18]; DP4 result.texcoord[3].x, vertex.position, c[17]; END # 22 instructions, 1 R-regs DarkRadiant-2.5.0/install/gl/interaction_vp.cg000066400000000000000000000044311321750546400213220ustar00rootroot00000000000000/// ============================================================================ /* Copyright (C) 2004 Robert Beckebans Please see the file "AUTHORS" for a list of contributors 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /// ============================================================================ struct cg_app2vertex { float4 colour : COLOR; float4 position : POSITION; float4 tex0 : ATTR8; float3 tangent : ATTR9; float3 binormal : ATTR10; float3 normal : ATTR11; }; struct cg_vertex2fragment { float4 colour : COLOR; float4 hposition : POSITION; float4 position : TEXCOORD0; float4 tex_diffuse_bump : TEXCOORD1; float4 tex_specular : TEXCOORD2; float4 tex_atten_xy_z : TEXCOORD3; float3 tangent : TEXCOORD4; float3 binormal : TEXCOORD5; float3 normal : TEXCOORD6; }; cg_vertex2fragment main(cg_app2vertex IN) { cg_vertex2fragment OUT; // transform vertex position into homogenous clip-space OUT.hposition = mul(glstate.matrix.mvp, IN.position); // assign position in object space OUT.position = IN.position; // transform texcoords OUT.tex_diffuse_bump.xy = mul(glstate.matrix.texture[0], IN.tex0).xy; // transform texcoords OUT.tex_diffuse_bump.zw = mul(glstate.matrix.texture[1], IN.tex0).xy; // transform texcoords OUT.tex_specular = mul(glstate.matrix.texture[2], IN.tex0); // transform vertex position into light space OUT.tex_atten_xy_z = mul(glstate.matrix.texture[3], IN.position); // assign tangent space vectors OUT.tangent = IN.tangent; OUT.binormal = IN.binormal; OUT.normal = IN.normal; // Pass-thru vertex colour OUT.colour = IN.colour; return OUT; } DarkRadiant-2.5.0/install/gl/interaction_vp.glsl000066400000000000000000000043101321750546400216660ustar00rootroot00000000000000/// ============================================================================ /* Copyright (C) 2004 Robert Beckebans Please see the file "CONTRIBUTORS" for a list of contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /// ============================================================================ attribute vec4 attr_TexCoord0; attribute vec3 attr_Tangent; attribute vec3 attr_Bitangent; attribute vec3 attr_Normal; varying vec3 var_vertex; varying vec4 var_tex_diffuse_bump; varying vec2 var_tex_specular; varying vec4 var_tex_atten_xy_z; varying mat3 var_mat_os2ts; void main() { // transform vertex position into homogenous clip-space gl_Position = ftransform(); // assign position in object space var_vertex = gl_Vertex.xyz; // transform texcoords into diffusemap texture space var_tex_diffuse_bump.st = (gl_TextureMatrix[0] * attr_TexCoord0).st; // transform texcoords into bumpmap texture space var_tex_diffuse_bump.pq = (gl_TextureMatrix[1] * attr_TexCoord0).st; // transform texcoords into specularmap texture space var_tex_specular = (gl_TextureMatrix[2] * attr_TexCoord0).st; // calc light xy,z attenuation in light space var_tex_atten_xy_z = gl_TextureMatrix[3] * gl_Vertex; // construct object-space-to-tangent-space 3x3 matrix var_mat_os2ts = mat3( attr_Tangent.x, attr_Bitangent.x, attr_Normal.x, attr_Tangent.y, attr_Bitangent.y, attr_Normal.y, attr_Tangent.z, attr_Bitangent.z, attr_Normal.z ); // Pass through vertex colour gl_FrontColor = gl_Color; } DarkRadiant-2.5.0/install/gl/utils.cg000066400000000000000000000024341321750546400174370ustar00rootroot00000000000000/// ============================================================================ /* Copyright (C) 2004 Robert Beckebans Please see the file "AUTHORS" for a list of contributors 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /// ============================================================================ // fresnel approximation float fast_fresnel(float3 I, float3 N, float3 fresnel_values) { float power = fresnel_values.x; float scale = fresnel_values.y; float bias = fresnel_values.z; return bias + pow(1.0 - dot(I, N), power) * scale; } float3 CG_Expand(float3 v) { return (v - 0.5) * 2; // expand a range-compressed vector } DarkRadiant-2.5.0/install/gl/zfill_arbfp1.cg000066400000000000000000000025331321750546400206520ustar00rootroot00000000000000/// ============================================================================ /* Copyright (C) 2003 Robert Beckebans Copyright (C) 2003, 2004 contributors of the XreaL project Please see the file "AUTHORS" for a list of contributors 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /// ============================================================================ struct cg_vertex2fragment { float4 position : POSITION; float4 tex0 : TEXCOORD0; }; struct cg_fragment2final { float4 color : COLOR; }; cg_fragment2final main(in cg_vertex2fragment IN, uniform sampler2D colormap) { cg_fragment2final OUT; OUT.color.w = tex2D(colormap, IN.tex0.xy).a; OUT.color.xyz = 0; return OUT; } DarkRadiant-2.5.0/install/gl/zfill_arbvp1.cg000066400000000000000000000027451321750546400206770ustar00rootroot00000000000000/// ============================================================================ /* Copyright (C) 2003 Robert Beckebans Copyright (C) 2003, 2004 contributors of the XreaL project Please see the file "AUTHORS" for a list of contributors 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /// ============================================================================ struct cg_app2vertex { float4 position : ATTR0; float4 texcoord0 : ATTR8; }; struct cg_vertex2fragment { float4 position : POSITION; float4 tex0 : TEXCOORD0; }; cg_vertex2fragment main(cg_app2vertex IN) { cg_vertex2fragment OUT; // transform vertex position into homogenous clip-space OUT.position = mul(glstate.matrix.mvp, IN.position); // transform texcoords into 1st texture space OUT.tex0 = mul(glstate.matrix.texture[0], IN.texcoord0); return OUT; } DarkRadiant-2.5.0/install/gl/zfill_fp.glp000066400000000000000000000011271321750546400202730ustar00rootroot00000000000000!!ARBfp1.0 # cgc version 1.3.0001, build date Aug 4 2004 10:01:10 # command line args: -profile arbfp1 # source file: ..\..\setup\data\tools\gl\zfill_arbfp1.cg #vendor NVIDIA Corporation #version 1.0.02 #profile arbfp1 #program main #semantic main.colormap #var float4 IN.position : : : 0 : 0 #var float4 IN.tex0 : $vin.TEX0 : TEX0 : 0 : 1 #var sampler2D colormap : : texunit 0 : 1 : 1 #var float4 main.color : $vout.COL : COL : -1 : 1 #const c[0] = 0 PARAM c[1] = { { 0 } }; MOV result.color.xyz, c[0].x; TEX result.color.w, fragment.texcoord[0], texture[0], 2D; END # 2 instructions, 0 R-regs DarkRadiant-2.5.0/install/gl/zfill_fp.glsl000066400000000000000000000020701321750546400204500ustar00rootroot00000000000000/// ============================================================================ /* Copyright (C) 2004 Robert Beckebans Please see the file "CONTRIBUTORS" for a list of contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /// ============================================================================ void main() { gl_FragColor.a = 1.0; gl_FragColor.rgb = vec3(1.0, 1.0, 0.0); } DarkRadiant-2.5.0/install/gl/zfill_vp.glp000066400000000000000000000761741321750546400203310ustar00rootroot00000000000000!!ARBvp1.0 # cgc version 1.3.0001, build date Aug 4 2004 10:01:10 # command line args: -profile arbvp1 # source file: ..\..\setup\data\tools\gl\zfill_arbvp1.cg #vendor NVIDIA Corporation #version 1.0.02 #profile arbvp1 #program main #semantic glstate : STATE #var float4 glstate.material.ambient : STATE.MATERIAL.AMBIENT : : -1 : 0 #var float4 glstate.material.diffuse : STATE.MATERIAL.DIFFUSE : : -1 : 0 #var float4 glstate.material.specular : STATE.MATERIAL.SPECULAR : : -1 : 0 #var float4 glstate.material.emission : STATE.MATERIAL.EMISSION : : -1 : 0 #var float4 glstate.material.shininess : STATE.MATERIAL.SHININESS : : -1 : 0 #var float4 glstate.material.front.ambient : STATE.MATERIAL.FRONT.AMBIENT : : -1 : 0 #var float4 glstate.material.front.diffuse : STATE.MATERIAL.FRONT.DIFFUSE : : -1 : 0 #var float4 glstate.material.front.specular : STATE.MATERIAL.FRONT.SPECULAR : : -1 : 0 #var float4 glstate.material.front.emission : STATE.MATERIAL.FRONT.EMISSION : : -1 : 0 #var float4 glstate.material.front.shininess : STATE.MATERIAL.FRONT.SHININESS : : -1 : 0 #var float4 glstate.material.back.ambient : STATE.MATERIAL.BACK.AMBIENT : : -1 : 0 #var float4 glstate.material.back.diffuse : STATE.MATERIAL.BACK.DIFFUSE : : -1 : 0 #var float4 glstate.material.back.specular : STATE.MATERIAL.BACK.SPECULAR : : -1 : 0 #var float4 glstate.material.back.emission : STATE.MATERIAL.BACK.EMISSION : : -1 : 0 #var float4 glstate.material.back.shininess : STATE.MATERIAL.BACK.SHININESS : : -1 : 0 #var float4 glstate.light[0].ambient : STATE.LIGHT[0].AMBIENT : : -1 : 0 #var float4 glstate.light[0].diffuse : STATE.LIGHT[0].DIFFUSE : : -1 : 0 #var float4 glstate.light[0].specular : STATE.LIGHT[0].SPECULAR : : -1 : 0 #var float4 glstate.light[0].position : STATE.LIGHT[0].POSITION : : -1 : 0 #var float4 glstate.light[0].attenuation : STATE.LIGHT[0].ATTENUATION : : -1 : 0 #var float4 glstate.light[0].spot.direction : STATE.LIGHT[0].SPOT.DIRECTION : : -1 : 0 #var float4 glstate.light[0].half : STATE.LIGHT[0].HALF : : -1 : 0 #var float4 glstate.light[1].ambient : STATE.LIGHT[1].AMBIENT : : -1 : 0 #var float4 glstate.light[1].diffuse : STATE.LIGHT[1].DIFFUSE : : -1 : 0 #var float4 glstate.light[1].specular : STATE.LIGHT[1].SPECULAR : : -1 : 0 #var float4 glstate.light[1].position : STATE.LIGHT[1].POSITION : : -1 : 0 #var float4 glstate.light[1].attenuation : STATE.LIGHT[1].ATTENUATION : : -1 : 0 #var float4 glstate.light[1].spot.direction : STATE.LIGHT[1].SPOT.DIRECTION : : -1 : 0 #var float4 glstate.light[1].half : STATE.LIGHT[1].HALF : : -1 : 0 #var float4 glstate.light[2].ambient : STATE.LIGHT[2].AMBIENT : : -1 : 0 #var float4 glstate.light[2].diffuse : STATE.LIGHT[2].DIFFUSE : : -1 : 0 #var float4 glstate.light[2].specular : STATE.LIGHT[2].SPECULAR : : -1 : 0 #var float4 glstate.light[2].position : STATE.LIGHT[2].POSITION : : -1 : 0 #var float4 glstate.light[2].attenuation : STATE.LIGHT[2].ATTENUATION : : -1 : 0 #var float4 glstate.light[2].spot.direction : STATE.LIGHT[2].SPOT.DIRECTION : : -1 : 0 #var float4 glstate.light[2].half : STATE.LIGHT[2].HALF : : -1 : 0 #var float4 glstate.light[3].ambient : STATE.LIGHT[3].AMBIENT : : -1 : 0 #var float4 glstate.light[3].diffuse : STATE.LIGHT[3].DIFFUSE : : -1 : 0 #var float4 glstate.light[3].specular : STATE.LIGHT[3].SPECULAR : : -1 : 0 #var float4 glstate.light[3].position : STATE.LIGHT[3].POSITION : : -1 : 0 #var float4 glstate.light[3].attenuation : STATE.LIGHT[3].ATTENUATION : : -1 : 0 #var float4 glstate.light[3].spot.direction : STATE.LIGHT[3].SPOT.DIRECTION : : -1 : 0 #var float4 glstate.light[3].half : STATE.LIGHT[3].HALF : : -1 : 0 #var float4 glstate.light[4].ambient : STATE.LIGHT[4].AMBIENT : : -1 : 0 #var float4 glstate.light[4].diffuse : STATE.LIGHT[4].DIFFUSE : : -1 : 0 #var float4 glstate.light[4].specular : STATE.LIGHT[4].SPECULAR : : -1 : 0 #var float4 glstate.light[4].position : STATE.LIGHT[4].POSITION : : -1 : 0 #var float4 glstate.light[4].attenuation : STATE.LIGHT[4].ATTENUATION : : -1 : 0 #var float4 glstate.light[4].spot.direction : STATE.LIGHT[4].SPOT.DIRECTION : : -1 : 0 #var float4 glstate.light[4].half : STATE.LIGHT[4].HALF : : -1 : 0 #var float4 glstate.light[5].ambient : STATE.LIGHT[5].AMBIENT : : -1 : 0 #var float4 glstate.light[5].diffuse : STATE.LIGHT[5].DIFFUSE : : -1 : 0 #var float4 glstate.light[5].specular : STATE.LIGHT[5].SPECULAR : : -1 : 0 #var float4 glstate.light[5].position : STATE.LIGHT[5].POSITION : : -1 : 0 #var float4 glstate.light[5].attenuation : STATE.LIGHT[5].ATTENUATION : : -1 : 0 #var float4 glstate.light[5].spot.direction : STATE.LIGHT[5].SPOT.DIRECTION : : -1 : 0 #var float4 glstate.light[5].half : STATE.LIGHT[5].HALF : : -1 : 0 #var float4 glstate.light[6].ambient : STATE.LIGHT[6].AMBIENT : : -1 : 0 #var float4 glstate.light[6].diffuse : STATE.LIGHT[6].DIFFUSE : : -1 : 0 #var float4 glstate.light[6].specular : STATE.LIGHT[6].SPECULAR : : -1 : 0 #var float4 glstate.light[6].position : STATE.LIGHT[6].POSITION : : -1 : 0 #var float4 glstate.light[6].attenuation : STATE.LIGHT[6].ATTENUATION : : -1 : 0 #var float4 glstate.light[6].spot.direction : STATE.LIGHT[6].SPOT.DIRECTION : : -1 : 0 #var float4 glstate.light[6].half : STATE.LIGHT[6].HALF : : -1 : 0 #var float4 glstate.light[7].ambient : STATE.LIGHT[7].AMBIENT : : -1 : 0 #var float4 glstate.light[7].diffuse : STATE.LIGHT[7].DIFFUSE : : -1 : 0 #var float4 glstate.light[7].specular : STATE.LIGHT[7].SPECULAR : : -1 : 0 #var float4 glstate.light[7].position : STATE.LIGHT[7].POSITION : : -1 : 0 #var float4 glstate.light[7].attenuation : STATE.LIGHT[7].ATTENUATION : : -1 : 0 #var float4 glstate.light[7].spot.direction : STATE.LIGHT[7].SPOT.DIRECTION : : -1 : 0 #var float4 glstate.light[7].half : STATE.LIGHT[7].HALF : : -1 : 0 #var float4 glstate.lightmodel.ambient : STATE.LIGHTMODEL.AMBIENT : : -1 : 0 #var float4 glstate.lightmodel.scenecolor : STATE.LIGHTMODEL.SCENECOLOR : : -1 : 0 #var float4 glstate.lightmodel.front.scenecolor : STATE.LIGHTMODEL.FRONT.SCENECOLOR : : -1 : 0 #var float4 glstate.lightmodel.back.scenecolor : STATE.LIGHTMODEL.BACK.SCENECOLOR : : -1 : 0 #var float4 glstate.lightprod[0].ambient : STATE.LIGHTPROD[0].AMBIENT : : -1 : 0 #var float4 glstate.lightprod[0].diffuse : STATE.LIGHTPROD[0].DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[0].specular : STATE.LIGHTPROD[0].SPECULAR : : -1 : 0 #var float4 glstate.lightprod[0].front.ambient : STATE.LIGHTPROD[0].FRONT.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[0].front.diffuse : STATE.LIGHTPROD[0].FRONT.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[0].front.specular : STATE.LIGHTPROD[0].FRONT.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[0].back.ambient : STATE.LIGHTPROD[0].BACK.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[0].back.diffuse : STATE.LIGHTPROD[0].BACK.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[0].back.specular : STATE.LIGHTPROD[0].BACK.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[1].ambient : STATE.LIGHTPROD[1].AMBIENT : : -1 : 0 #var float4 glstate.lightprod[1].diffuse : STATE.LIGHTPROD[1].DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[1].specular : STATE.LIGHTPROD[1].SPECULAR : : -1 : 0 #var float4 glstate.lightprod[1].front.ambient : STATE.LIGHTPROD[1].FRONT.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[1].front.diffuse : STATE.LIGHTPROD[1].FRONT.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[1].front.specular : STATE.LIGHTPROD[1].FRONT.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[1].back.ambient : STATE.LIGHTPROD[1].BACK.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[1].back.diffuse : STATE.LIGHTPROD[1].BACK.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[1].back.specular : STATE.LIGHTPROD[1].BACK.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[2].ambient : STATE.LIGHTPROD[2].AMBIENT : : -1 : 0 #var float4 glstate.lightprod[2].diffuse : STATE.LIGHTPROD[2].DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[2].specular : STATE.LIGHTPROD[2].SPECULAR : : -1 : 0 #var float4 glstate.lightprod[2].front.ambient : STATE.LIGHTPROD[2].FRONT.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[2].front.diffuse : STATE.LIGHTPROD[2].FRONT.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[2].front.specular : STATE.LIGHTPROD[2].FRONT.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[2].back.ambient : STATE.LIGHTPROD[2].BACK.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[2].back.diffuse : STATE.LIGHTPROD[2].BACK.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[2].back.specular : STATE.LIGHTPROD[2].BACK.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[3].ambient : STATE.LIGHTPROD[3].AMBIENT : : -1 : 0 #var float4 glstate.lightprod[3].diffuse : STATE.LIGHTPROD[3].DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[3].specular : STATE.LIGHTPROD[3].SPECULAR : : -1 : 0 #var float4 glstate.lightprod[3].front.ambient : STATE.LIGHTPROD[3].FRONT.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[3].front.diffuse : STATE.LIGHTPROD[3].FRONT.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[3].front.specular : STATE.LIGHTPROD[3].FRONT.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[3].back.ambient : STATE.LIGHTPROD[3].BACK.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[3].back.diffuse : STATE.LIGHTPROD[3].BACK.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[3].back.specular : STATE.LIGHTPROD[3].BACK.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[4].ambient : STATE.LIGHTPROD[4].AMBIENT : : -1 : 0 #var float4 glstate.lightprod[4].diffuse : STATE.LIGHTPROD[4].DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[4].specular : STATE.LIGHTPROD[4].SPECULAR : : -1 : 0 #var float4 glstate.lightprod[4].front.ambient : STATE.LIGHTPROD[4].FRONT.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[4].front.diffuse : STATE.LIGHTPROD[4].FRONT.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[4].front.specular : STATE.LIGHTPROD[4].FRONT.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[4].back.ambient : STATE.LIGHTPROD[4].BACK.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[4].back.diffuse : STATE.LIGHTPROD[4].BACK.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[4].back.specular : STATE.LIGHTPROD[4].BACK.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[5].ambient : STATE.LIGHTPROD[5].AMBIENT : : -1 : 0 #var float4 glstate.lightprod[5].diffuse : STATE.LIGHTPROD[5].DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[5].specular : STATE.LIGHTPROD[5].SPECULAR : : -1 : 0 #var float4 glstate.lightprod[5].front.ambient : STATE.LIGHTPROD[5].FRONT.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[5].front.diffuse : STATE.LIGHTPROD[5].FRONT.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[5].front.specular : STATE.LIGHTPROD[5].FRONT.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[5].back.ambient : STATE.LIGHTPROD[5].BACK.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[5].back.diffuse : STATE.LIGHTPROD[5].BACK.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[5].back.specular : STATE.LIGHTPROD[5].BACK.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[6].ambient : STATE.LIGHTPROD[6].AMBIENT : : -1 : 0 #var float4 glstate.lightprod[6].diffuse : STATE.LIGHTPROD[6].DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[6].specular : STATE.LIGHTPROD[6].SPECULAR : : -1 : 0 #var float4 glstate.lightprod[6].front.ambient : STATE.LIGHTPROD[6].FRONT.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[6].front.diffuse : STATE.LIGHTPROD[6].FRONT.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[6].front.specular : STATE.LIGHTPROD[6].FRONT.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[6].back.ambient : STATE.LIGHTPROD[6].BACK.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[6].back.diffuse : STATE.LIGHTPROD[6].BACK.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[6].back.specular : STATE.LIGHTPROD[6].BACK.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[7].ambient : STATE.LIGHTPROD[7].AMBIENT : : -1 : 0 #var float4 glstate.lightprod[7].diffuse : STATE.LIGHTPROD[7].DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[7].specular : STATE.LIGHTPROD[7].SPECULAR : : -1 : 0 #var float4 glstate.lightprod[7].front.ambient : STATE.LIGHTPROD[7].FRONT.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[7].front.diffuse : STATE.LIGHTPROD[7].FRONT.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[7].front.specular : STATE.LIGHTPROD[7].FRONT.SPECULAR : : -1 : 0 #var float4 glstate.lightprod[7].back.ambient : STATE.LIGHTPROD[7].BACK.AMBIENT : : -1 : 0 #var float4 glstate.lightprod[7].back.diffuse : STATE.LIGHTPROD[7].BACK.DIFFUSE : : -1 : 0 #var float4 glstate.lightprod[7].back.specular : STATE.LIGHTPROD[7].BACK.SPECULAR : : -1 : 0 #var float4 glstate.texgen[0].eye.s : STATE.TEXGEN[0].EYE.S : : -1 : 0 #var float4 glstate.texgen[0].eye.t : STATE.TEXGEN[0].EYE.T : : -1 : 0 #var float4 glstate.texgen[0].eye.r : STATE.TEXGEN[0].EYE.R : : -1 : 0 #var float4 glstate.texgen[0].eye.q : STATE.TEXGEN[0].EYE.Q : : -1 : 0 #var float4 glstate.texgen[0].object.s : STATE.TEXGEN[0].OBJECT.S : : -1 : 0 #var float4 glstate.texgen[0].object.t : STATE.TEXGEN[0].OBJECT.T : : -1 : 0 #var float4 glstate.texgen[0].object.r : STATE.TEXGEN[0].OBJECT.R : : -1 : 0 #var float4 glstate.texgen[0].object.q : STATE.TEXGEN[0].OBJECT.Q : : -1 : 0 #var float4 glstate.texgen[1].eye.s : STATE.TEXGEN[1].EYE.S : : -1 : 0 #var float4 glstate.texgen[1].eye.t : STATE.TEXGEN[1].EYE.T : : -1 : 0 #var float4 glstate.texgen[1].eye.r : STATE.TEXGEN[1].EYE.R : : -1 : 0 #var float4 glstate.texgen[1].eye.q : STATE.TEXGEN[1].EYE.Q : : -1 : 0 #var float4 glstate.texgen[1].object.s : STATE.TEXGEN[1].OBJECT.S : : -1 : 0 #var float4 glstate.texgen[1].object.t : STATE.TEXGEN[1].OBJECT.T : : -1 : 0 #var float4 glstate.texgen[1].object.r : STATE.TEXGEN[1].OBJECT.R : : -1 : 0 #var float4 glstate.texgen[1].object.q : STATE.TEXGEN[1].OBJECT.Q : : -1 : 0 #var float4 glstate.texgen[2].eye.s : STATE.TEXGEN[2].EYE.S : : -1 : 0 #var float4 glstate.texgen[2].eye.t : STATE.TEXGEN[2].EYE.T : : -1 : 0 #var float4 glstate.texgen[2].eye.r : STATE.TEXGEN[2].EYE.R : : -1 : 0 #var float4 glstate.texgen[2].eye.q : STATE.TEXGEN[2].EYE.Q : : -1 : 0 #var float4 glstate.texgen[2].object.s : STATE.TEXGEN[2].OBJECT.S : : -1 : 0 #var float4 glstate.texgen[2].object.t : STATE.TEXGEN[2].OBJECT.T : : -1 : 0 #var float4 glstate.texgen[2].object.r : STATE.TEXGEN[2].OBJECT.R : : -1 : 0 #var float4 glstate.texgen[2].object.q : STATE.TEXGEN[2].OBJECT.Q : : -1 : 0 #var float4 glstate.texgen[3].eye.s : STATE.TEXGEN[3].EYE.S : : -1 : 0 #var float4 glstate.texgen[3].eye.t : STATE.TEXGEN[3].EYE.T : : -1 : 0 #var float4 glstate.texgen[3].eye.r : STATE.TEXGEN[3].EYE.R : : -1 : 0 #var float4 glstate.texgen[3].eye.q : STATE.TEXGEN[3].EYE.Q : : -1 : 0 #var float4 glstate.texgen[3].object.s : STATE.TEXGEN[3].OBJECT.S : : -1 : 0 #var float4 glstate.texgen[3].object.t : STATE.TEXGEN[3].OBJECT.T : : -1 : 0 #var float4 glstate.texgen[3].object.r : STATE.TEXGEN[3].OBJECT.R : : -1 : 0 #var float4 glstate.texgen[3].object.q : STATE.TEXGEN[3].OBJECT.Q : : -1 : 0 #var float4 glstate.texgen[4].eye.s : STATE.TEXGEN[4].EYE.S : : -1 : 0 #var float4 glstate.texgen[4].eye.t : STATE.TEXGEN[4].EYE.T : : -1 : 0 #var float4 glstate.texgen[4].eye.r : STATE.TEXGEN[4].EYE.R : : -1 : 0 #var float4 glstate.texgen[4].eye.q : STATE.TEXGEN[4].EYE.Q : : -1 : 0 #var float4 glstate.texgen[4].object.s : STATE.TEXGEN[4].OBJECT.S : : -1 : 0 #var float4 glstate.texgen[4].object.t : STATE.TEXGEN[4].OBJECT.T : : -1 : 0 #var float4 glstate.texgen[4].object.r : STATE.TEXGEN[4].OBJECT.R : : -1 : 0 #var float4 glstate.texgen[4].object.q : STATE.TEXGEN[4].OBJECT.Q : : -1 : 0 #var float4 glstate.texgen[5].eye.s : STATE.TEXGEN[5].EYE.S : : -1 : 0 #var float4 glstate.texgen[5].eye.t : STATE.TEXGEN[5].EYE.T : : -1 : 0 #var float4 glstate.texgen[5].eye.r : STATE.TEXGEN[5].EYE.R : : -1 : 0 #var float4 glstate.texgen[5].eye.q : STATE.TEXGEN[5].EYE.Q : : -1 : 0 #var float4 glstate.texgen[5].object.s : STATE.TEXGEN[5].OBJECT.S : : -1 : 0 #var float4 glstate.texgen[5].object.t : STATE.TEXGEN[5].OBJECT.T : : -1 : 0 #var float4 glstate.texgen[5].object.r : STATE.TEXGEN[5].OBJECT.R : : -1 : 0 #var float4 glstate.texgen[5].object.q : STATE.TEXGEN[5].OBJECT.Q : : -1 : 0 #var float4 glstate.texgen[6].eye.s : STATE.TEXGEN[6].EYE.S : : -1 : 0 #var float4 glstate.texgen[6].eye.t : STATE.TEXGEN[6].EYE.T : : -1 : 0 #var float4 glstate.texgen[6].eye.r : STATE.TEXGEN[6].EYE.R : : -1 : 0 #var float4 glstate.texgen[6].eye.q : STATE.TEXGEN[6].EYE.Q : : -1 : 0 #var float4 glstate.texgen[6].object.s : STATE.TEXGEN[6].OBJECT.S : : -1 : 0 #var float4 glstate.texgen[6].object.t : STATE.TEXGEN[6].OBJECT.T : : -1 : 0 #var float4 glstate.texgen[6].object.r : STATE.TEXGEN[6].OBJECT.R : : -1 : 0 #var float4 glstate.texgen[6].object.q : STATE.TEXGEN[6].OBJECT.Q : : -1 : 0 #var float4 glstate.texgen[7].eye.s : STATE.TEXGEN[7].EYE.S : : -1 : 0 #var float4 glstate.texgen[7].eye.t : STATE.TEXGEN[7].EYE.T : : -1 : 0 #var float4 glstate.texgen[7].eye.r : STATE.TEXGEN[7].EYE.R : : -1 : 0 #var float4 glstate.texgen[7].eye.q : STATE.TEXGEN[7].EYE.Q : : -1 : 0 #var float4 glstate.texgen[7].object.s : STATE.TEXGEN[7].OBJECT.S : : -1 : 0 #var float4 glstate.texgen[7].object.t : STATE.TEXGEN[7].OBJECT.T : : -1 : 0 #var float4 glstate.texgen[7].object.r : STATE.TEXGEN[7].OBJECT.R : : -1 : 0 #var float4 glstate.texgen[7].object.q : STATE.TEXGEN[7].OBJECT.Q : : -1 : 0 #var float4 glstate.fog.color : STATE.FOG.COLOR : : -1 : 0 #var float4 glstate.fog.params : STATE.FOG.PARAMS : : -1 : 0 #var float4 glstate.clip[0].plane : STATE.CLIP[0].PLANE : : -1 : 0 #var float4 glstate.clip[1].plane : STATE.CLIP[1].PLANE : : -1 : 0 #var float4 glstate.clip[2].plane : STATE.CLIP[2].PLANE : : -1 : 0 #var float4 glstate.clip[3].plane : STATE.CLIP[3].PLANE : : -1 : 0 #var float4 glstate.clip[4].plane : STATE.CLIP[4].PLANE : : -1 : 0 #var float4 glstate.clip[5].plane : STATE.CLIP[5].PLANE : : -1 : 0 #var float4 glstate.clip[6].plane : STATE.CLIP[6].PLANE : : -1 : 0 #var float4 glstate.clip[7].plane : STATE.CLIP[7].PLANE : : -1 : 0 #var float glstate.point.size : STATE.POINT.SIZE : : -1 : 0 #var float glstate.point.attenuation : STATE.POINT.ATTENUATION : : -1 : 0 #var float4x4 glstate.matrix.modelview[0] : STATE.MATRIX.MODELVIEW[0] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[1] : STATE.MATRIX.MODELVIEW[1] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[2] : STATE.MATRIX.MODELVIEW[2] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[3] : STATE.MATRIX.MODELVIEW[3] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[4] : STATE.MATRIX.MODELVIEW[4] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[5] : STATE.MATRIX.MODELVIEW[5] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[6] : STATE.MATRIX.MODELVIEW[6] : , 4 : -1 : 0 #var float4x4 glstate.matrix.modelview[7] : STATE.MATRIX.MODELVIEW[7] : , 4 : -1 : 0 #var float4x4 glstate.matrix.projection : STATE.MATRIX.PROJECTION : , 4 : -1 : 0 #var float4x4 glstate.matrix.mvp : STATE.MATRIX.MVP : c[0], 4 : -1 : 1 #var float4x4 glstate.matrix.texture[0] : STATE.MATRIX.TEXTURE[0] : c[4], 4 : -1 : 1 #var float4x4 glstate.matrix.texture[1] : STATE.MATRIX.TEXTURE[1] : , 4 : -1 : 0 #var float4x4 glstate.matrix.texture[2] : STATE.MATRIX.TEXTURE[2] : , 4 : -1 : 0 #var float4x4 glstate.matrix.texture[3] : STATE.MATRIX.TEXTURE[3] : , 4 : -1 : 0 #var float4x4 glstate.matrix.texture[4] : STATE.MATRIX.TEXTURE[4] : , 4 : -1 : 0 #var float4x4 glstate.matrix.texture[5] : STATE.MATRIX.TEXTURE[5] : , 4 : -1 : 0 #var float4x4 glstate.matrix.texture[6] : STATE.MATRIX.TEXTURE[6] : , 4 : -1 : 0 #var float4x4 glstate.matrix.texture[7] : STATE.MATRIX.TEXTURE[7] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[0] : STATE.MATRIX.PALETTE[0] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[1] : STATE.MATRIX.PALETTE[1] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[2] : STATE.MATRIX.PALETTE[2] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[3] : STATE.MATRIX.PALETTE[3] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[4] : STATE.MATRIX.PALETTE[4] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[5] : STATE.MATRIX.PALETTE[5] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[6] : STATE.MATRIX.PALETTE[6] : , 4 : -1 : 0 #var float4x4 glstate.matrix.palette[7] : STATE.MATRIX.PALETTE[7] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[0] : STATE.MATRIX.PROGRAM[0] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[1] : STATE.MATRIX.PROGRAM[1] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[2] : STATE.MATRIX.PROGRAM[2] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[3] : STATE.MATRIX.PROGRAM[3] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[4] : STATE.MATRIX.PROGRAM[4] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[5] : STATE.MATRIX.PROGRAM[5] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[6] : STATE.MATRIX.PROGRAM[6] : , 4 : -1 : 0 #var float4x4 glstate.matrix.program[7] : STATE.MATRIX.PROGRAM[7] : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[0] : STATE.MATRIX.MODELVIEW[0].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[1] : STATE.MATRIX.MODELVIEW[1].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[2] : STATE.MATRIX.MODELVIEW[2].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[3] : STATE.MATRIX.MODELVIEW[3].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[4] : STATE.MATRIX.MODELVIEW[4].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[5] : STATE.MATRIX.MODELVIEW[5].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[6] : STATE.MATRIX.MODELVIEW[6].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.modelview[7] : STATE.MATRIX.MODELVIEW[7].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.projection : STATE.MATRIX.PROJECTION.INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.mvp : STATE.MATRIX.MVP.INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[0] : STATE.MATRIX.TEXTURE[0].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[1] : STATE.MATRIX.TEXTURE[1].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[2] : STATE.MATRIX.TEXTURE[2].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[3] : STATE.MATRIX.TEXTURE[3].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[4] : STATE.MATRIX.TEXTURE[4].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[5] : STATE.MATRIX.TEXTURE[5].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[6] : STATE.MATRIX.TEXTURE[6].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.texture[7] : STATE.MATRIX.TEXTURE[7].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[0] : STATE.MATRIX.PALETTE[0].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[1] : STATE.MATRIX.PALETTE[1].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[2] : STATE.MATRIX.PALETTE[2].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[3] : STATE.MATRIX.PALETTE[3].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[4] : STATE.MATRIX.PALETTE[4].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[5] : STATE.MATRIX.PALETTE[5].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[6] : STATE.MATRIX.PALETTE[6].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.palette[7] : STATE.MATRIX.PALETTE[7].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[0] : STATE.MATRIX.PROGRAM[0].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[1] : STATE.MATRIX.PROGRAM[1].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[2] : STATE.MATRIX.PROGRAM[2].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[3] : STATE.MATRIX.PROGRAM[3].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[4] : STATE.MATRIX.PROGRAM[4].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[5] : STATE.MATRIX.PROGRAM[5].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[6] : STATE.MATRIX.PROGRAM[6].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.inverse.program[7] : STATE.MATRIX.PROGRAM[7].INVERSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[0] : STATE.MATRIX.MODELVIEW[0].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[1] : STATE.MATRIX.MODELVIEW[1].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[2] : STATE.MATRIX.MODELVIEW[2].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[3] : STATE.MATRIX.MODELVIEW[3].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[4] : STATE.MATRIX.MODELVIEW[4].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[5] : STATE.MATRIX.MODELVIEW[5].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[6] : STATE.MATRIX.MODELVIEW[6].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.modelview[7] : STATE.MATRIX.MODELVIEW[7].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.projection : STATE.MATRIX.PROJECTION.TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.mvp : STATE.MATRIX.MVP.TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[0] : STATE.MATRIX.TEXTURE[0].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[1] : STATE.MATRIX.TEXTURE[1].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[2] : STATE.MATRIX.TEXTURE[2].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[3] : STATE.MATRIX.TEXTURE[3].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[4] : STATE.MATRIX.TEXTURE[4].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[5] : STATE.MATRIX.TEXTURE[5].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[6] : STATE.MATRIX.TEXTURE[6].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.texture[7] : STATE.MATRIX.TEXTURE[7].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[0] : STATE.MATRIX.PALETTE[0].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[1] : STATE.MATRIX.PALETTE[1].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[2] : STATE.MATRIX.PALETTE[2].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[3] : STATE.MATRIX.PALETTE[3].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[4] : STATE.MATRIX.PALETTE[4].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[5] : STATE.MATRIX.PALETTE[5].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[6] : STATE.MATRIX.PALETTE[6].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.palette[7] : STATE.MATRIX.PALETTE[7].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[0] : STATE.MATRIX.PROGRAM[0].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[1] : STATE.MATRIX.PROGRAM[1].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[2] : STATE.MATRIX.PROGRAM[2].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[3] : STATE.MATRIX.PROGRAM[3].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[4] : STATE.MATRIX.PROGRAM[4].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[5] : STATE.MATRIX.PROGRAM[5].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[6] : STATE.MATRIX.PROGRAM[6].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.transpose.program[7] : STATE.MATRIX.PROGRAM[7].TRANSPOSE : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[0] : STATE.MATRIX.MODELVIEW[0].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[1] : STATE.MATRIX.MODELVIEW[1].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[2] : STATE.MATRIX.MODELVIEW[2].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[3] : STATE.MATRIX.MODELVIEW[3].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[4] : STATE.MATRIX.MODELVIEW[4].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[5] : STATE.MATRIX.MODELVIEW[5].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[6] : STATE.MATRIX.MODELVIEW[6].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.modelview[7] : STATE.MATRIX.MODELVIEW[7].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.projection : STATE.MATRIX.PROJECTION.INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.mvp : STATE.MATRIX.MVP.INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[0] : STATE.MATRIX.TEXTURE[0].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[1] : STATE.MATRIX.TEXTURE[1].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[2] : STATE.MATRIX.TEXTURE[2].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[3] : STATE.MATRIX.TEXTURE[3].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[4] : STATE.MATRIX.TEXTURE[4].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[5] : STATE.MATRIX.TEXTURE[5].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[6] : STATE.MATRIX.TEXTURE[6].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.texture[7] : STATE.MATRIX.TEXTURE[7].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[0] : STATE.MATRIX.PALETTE[0].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[1] : STATE.MATRIX.PALETTE[1].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[2] : STATE.MATRIX.PALETTE[2].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[3] : STATE.MATRIX.PALETTE[3].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[4] : STATE.MATRIX.PALETTE[4].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[5] : STATE.MATRIX.PALETTE[5].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[6] : STATE.MATRIX.PALETTE[6].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.palette[7] : STATE.MATRIX.PALETTE[7].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[0] : STATE.MATRIX.PROGRAM[0].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[1] : STATE.MATRIX.PROGRAM[1].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[2] : STATE.MATRIX.PROGRAM[2].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[3] : STATE.MATRIX.PROGRAM[3].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[4] : STATE.MATRIX.PROGRAM[4].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[5] : STATE.MATRIX.PROGRAM[5].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[6] : STATE.MATRIX.PROGRAM[6].INVTRANS : , 4 : -1 : 0 #var float4x4 glstate.matrix.invtrans.program[7] : STATE.MATRIX.PROGRAM[7].INVTRANS : , 4 : -1 : 0 #var float4 IN.position : $vin.ATTR0 : ATTR0 : 0 : 1 #var float4 IN.texcoord0 : $vin.ATTR8 : ATTR8 : 0 : 1 #var float4 main.position : $vout.HPOS : HPOS : -1 : 1 #var float4 main.tex0 : $vout.TEX0 : TEX0 : -1 : 1 PARAM c[8] = { state.matrix.mvp, state.matrix.texture[0] }; DP4 result.position.w, vertex.attrib[0], c[3]; DP4 result.position.z, vertex.attrib[0], c[2]; DP4 result.position.y, vertex.attrib[0], c[1]; DP4 result.position.x, vertex.attrib[0], c[0]; DP4 result.texcoord[0].w, vertex.attrib[8], c[7]; DP4 result.texcoord[0].z, vertex.attrib[8], c[6]; DP4 result.texcoord[0].y, vertex.attrib[8], c[5]; DP4 result.texcoord[0].x, vertex.attrib[8], c[4]; END # 8 instructions, 0 R-regs DarkRadiant-2.5.0/install/gl/zfill_vp.glsl000066400000000000000000000023601321750546400204720ustar00rootroot00000000000000/// ============================================================================ /* Copyright (C) 2004 Robert Beckebans Please see the file "CONTRIBUTORS" for a list of contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /// ============================================================================ attribute vec4 attr_TexCoord0; void main() { // transform vertex position into homogenous clip-space gl_Position = ftransform(); // transform texcoords gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; // assign color gl_FrontColor = gl_Color; } DarkRadiant-2.5.0/install/i18n/000077500000000000000000000000001321750546400161365ustar00rootroot00000000000000DarkRadiant-2.5.0/install/i18n/LINGUAS000066400000000000000000000000031321750546400171540ustar00rootroot00000000000000de DarkRadiant-2.5.0/install/i18n/Makefile.in.in000066400000000000000000000426151321750546400206200ustar00rootroot00000000000000# Makefile for PO directory in any package using GNU gettext. # Copyright (C) 1995-1997, 2000-2007, 2009-2010 by Ulrich Drepper # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without any warranty. # # Origin: gettext-0.19.8 GETTEXT_MACRO_VERSION = 0.19 PACKAGE = @PACKAGE@ VERSION = @VERSION@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ SED = @SED@ SHELL = /bin/sh @SET_MAKE@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ datarootdir = @datarootdir@ datadir = @datadir@ localedir = @localedir@ gettextsrcdir = $(datadir)/gettext/po INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ # We use $(mkdir_p). # In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as # "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions, # @install_sh@ does not start with $(SHELL), so we add it. # In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined # either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake # versions, $(mkinstalldirs) and $(install_sh) are unused. mkinstalldirs = $(SHELL) @install_sh@ -d install_sh = $(SHELL) @install_sh@ MKDIR_P = @MKDIR_P@ mkdir_p = @mkdir_p@ # When building gettext-tools, we prefer to use the built programs # rather than installed programs. However, we can't do that when we # are cross compiling. CROSS_COMPILING = @CROSS_COMPILING@ GMSGFMT_ = @GMSGFMT@ GMSGFMT_no = @GMSGFMT@ GMSGFMT_yes = @GMSGFMT_015@ GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT)) MSGFMT_ = @MSGFMT@ MSGFMT_no = @MSGFMT@ MSGFMT_yes = @MSGFMT_015@ MSGFMT = $(MSGFMT_$(USE_MSGCTXT)) XGETTEXT_ = @XGETTEXT@ XGETTEXT_no = @XGETTEXT@ XGETTEXT_yes = @XGETTEXT_015@ XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT)) MSGMERGE = msgmerge MSGMERGE_UPDATE = @MSGMERGE@ --update MSGINIT = msginit MSGCONV = msgconv MSGFILTER = msgfilter POFILES = @POFILES@ GMOFILES = @GMOFILES@ UPDATEPOFILES = @UPDATEPOFILES@ DUMMYPOFILES = @DUMMYPOFILES@ DISTFILES.common = Makefile.in.in \ $(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3) DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \ $(POFILES) $(GMOFILES) \ $(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3) POTFILES = \ CATALOGS = @CATALOGS@ POFILESDEPS_ = $(srcdir)/$(DOMAIN).pot POFILESDEPS_yes = $(POFILESDEPS_) POFILESDEPS_no = POFILESDEPS = $(POFILESDEPS_$(PO_DEPENDS_ON_POT)) DISTFILESDEPS_ = update-po DISTFILESDEPS_yes = $(DISTFILESDEPS_) DISTFILESDEPS_no = DISTFILESDEPS = $(DISTFILESDEPS_$(DIST_DEPENDS_ON_UPDATE_PO)) # Makevars gets inserted here. (Don't remove this line!) .SUFFIXES: .SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update .po.mo: @echo "$(MSGFMT) -c -o $@ $<"; \ $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@ .po.gmo: @lang=`echo $* | sed -e 's,.*/,,'`; \ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o $${lang}.gmo $${lang}.po"; \ cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo .sin.sed: sed -e '/^#/d' $< > t-$@ mv t-$@ $@ all: all-@USE_NLS@ all-yes: stamp-po all-no: # Ensure that the gettext macros and this Makefile.in.in are in sync. CHECK_MACRO_VERSION = \ test "$(GETTEXT_MACRO_VERSION)" = "@GETTEXT_MACRO_VERSION@" \ || { echo "*** error: gettext infrastructure mismatch: using a Makefile.in.in from gettext version $(GETTEXT_MACRO_VERSION) but the autoconf macros are from gettext version @GETTEXT_MACRO_VERSION@" 1>&2; \ exit 1; \ } # $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no # internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because # we don't want to bother translators with empty POT files). We assume that # LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty. # In this case, stamp-po is a nop (i.e. a phony target). # stamp-po is a timestamp denoting the last time at which the CATALOGS have # been loosely updated. Its purpose is that when a developer or translator # checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS, # "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent # invocations of "make" will do nothing. This timestamp would not be necessary # if updating the $(CATALOGS) would always touch them; however, the rule for # $(POFILES) has been designed to not touch files that don't need to be # changed. stamp-po: $(srcdir)/$(DOMAIN).pot @$(CHECK_MACRO_VERSION) test ! -f $(srcdir)/$(DOMAIN).pot || \ test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES) @test ! -f $(srcdir)/$(DOMAIN).pot || { \ echo "touch stamp-po" && \ echo timestamp > stamp-poT && \ mv stamp-poT stamp-po; \ } # Note: Target 'all' must not depend on target '$(DOMAIN).pot-update', # otherwise packages like GCC can not be built if only parts of the source # have been downloaded. # This target rebuilds $(DOMAIN).pot; it is an expensive operation. # Note that $(DOMAIN).pot is not touched if it doesn't need to be changed. # The determination of whether the package xyz is a GNU one is based on the # heuristic whether some file in the top level directory mentions "GNU xyz". # If GNU 'find' is available, we avoid grepping through monster files. $(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in package_gnu="$(PACKAGE_GNU)"; \ test -n "$$package_gnu" || { \ if { if (LC_ALL=C find --version) 2>/dev/null | grep GNU >/dev/null; then \ LC_ALL=C find -L $(top_srcdir) -maxdepth 1 -type f \ -size -10000000c -exec grep 'GNU @PACKAGE@' \ /dev/null '{}' ';' 2>/dev/null; \ else \ LC_ALL=C grep 'GNU @PACKAGE@' $(top_srcdir)/* 2>/dev/null; \ fi; \ } | grep -v 'libtool:' >/dev/null; then \ package_gnu=yes; \ else \ package_gnu=no; \ fi; \ }; \ if test "$$package_gnu" = "yes"; then \ package_prefix='GNU '; \ else \ package_prefix=''; \ fi; \ if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \ msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \ else \ msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \ fi; \ case `$(XGETTEXT) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-5] | 0.1[0-5].* | 0.16 | 0.16.[0-1]*) \ $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ --files-from=$(srcdir)/POTFILES.in \ --copyright-holder='$(COPYRIGHT_HOLDER)' \ --msgid-bugs-address="$$msgid_bugs_address" \ ;; \ *) \ $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ --files-from=$(srcdir)/POTFILES.in \ --copyright-holder='$(COPYRIGHT_HOLDER)' \ --package-name="$${package_prefix}@PACKAGE@" \ --package-version='@VERSION@' \ --msgid-bugs-address="$$msgid_bugs_address" \ ;; \ esac test ! -f $(DOMAIN).po || { \ if test -f $(srcdir)/$(DOMAIN).pot-header; then \ sed -e '1,/^#$$/d' < $(DOMAIN).po > $(DOMAIN).1po && \ cat $(srcdir)/$(DOMAIN).pot-header $(DOMAIN).1po > $(DOMAIN).po; \ rm -f $(DOMAIN).1po; \ fi; \ if test -f $(srcdir)/$(DOMAIN).pot; then \ cat < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ cat < $(DOMAIN).po > $(DOMAIN).2po && \ if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \ rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \ else \ rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \ mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ fi; \ else \ mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ fi; \ } # This rule has no dependencies: we don't need to update $(DOMAIN).pot at # every "make" invocation, only create it when it is missing. # Only "make $(DOMAIN).pot-update" or "make dist" will force an update. $(srcdir)/$(DOMAIN).pot: $(MAKE) $(DOMAIN).pot-update # This target rebuilds a PO file if $(DOMAIN).pot has changed. # Note that a PO file is not touched if it doesn't need to be changed. $(POFILES): $(POFILESDEPS) gather-po-files @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \ if test -f "$(srcdir)/$${lang}.po"; then \ test -f $(srcdir)/$(DOMAIN).pot || $(MAKE) $(srcdir)/$(DOMAIN).pot; \ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ echo "$${cdcmd}$(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot"; \ cd $(srcdir) \ && { case `$(MSGMERGE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \ $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) $${lang}.po $(DOMAIN).pot;; \ *) \ $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot;; \ esac; \ }; \ else \ $(MAKE) $${lang}.po-create; \ fi install: install-exec install-data install-exec: install-data: install-data-@USE_NLS@ if test "$(PACKAGE)" = "gettext-tools"; then \ $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ for file in $(DISTFILES.common) Makevars.template; do \ $(INSTALL_DATA) $(srcdir)/$$file \ $(DESTDIR)$(gettextsrcdir)/$$file; \ done; \ for file in Makevars; do \ rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ done; \ else \ : ; \ fi install-data-no: all install-data-yes: all @catalogs='$(CATALOGS)'; \ for cat in $$catalogs; do \ cat=`basename $$cat`; \ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ dir=$(localedir)/$$lang/LC_MESSAGES; \ $(mkdir_p) $(DESTDIR)$$dir; \ if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \ $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \ echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \ for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ if test -n "$$lc"; then \ if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ for file in *; do \ if test -f $$file; then \ ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ fi; \ done); \ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ else \ if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ :; \ else \ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ fi; \ fi; \ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \ fi; \ done; \ done install-strip: install installdirs: installdirs-exec installdirs-data installdirs-exec: installdirs-data: installdirs-data-@USE_NLS@ if test "$(PACKAGE)" = "gettext-tools"; then \ $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ else \ : ; \ fi installdirs-data-no: installdirs-data-yes: @catalogs='$(CATALOGS)'; \ for cat in $$catalogs; do \ cat=`basename $$cat`; \ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ dir=$(localedir)/$$lang/LC_MESSAGES; \ $(mkdir_p) $(DESTDIR)$$dir; \ for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ if test -n "$$lc"; then \ if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ for file in *; do \ if test -f $$file; then \ ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ fi; \ done); \ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ else \ if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ :; \ else \ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ fi; \ fi; \ fi; \ done; \ done # Define this as empty until I found a useful application. installcheck: uninstall: uninstall-exec uninstall-data uninstall-exec: uninstall-data: uninstall-data-@USE_NLS@ if test "$(PACKAGE)" = "gettext-tools"; then \ for file in $(DISTFILES.common) Makevars.template; do \ rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ done; \ else \ : ; \ fi uninstall-data-no: uninstall-data-yes: catalogs='$(CATALOGS)'; \ for cat in $$catalogs; do \ cat=`basename $$cat`; \ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ done; \ done check: all info dvi ps pdf html tags TAGS ctags CTAGS ID: mostlyclean: rm -f stamp-poT rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po rm -fr *.o clean: mostlyclean distclean: clean rm -f Makefile Makefile.in POTFILES *.mo maintainer-clean: distclean @echo "This command is intended for maintainers to use;" @echo "it deletes files that may require special tools to rebuild." rm -f stamp-po $(GMOFILES) distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) dist distdir: test -z "$(DISTFILESDEPS)" || $(MAKE) $(DISTFILESDEPS) @$(MAKE) dist2 # This is a separate target because 'update-po' must be executed before. dist2: stamp-po $(DISTFILES) dists="$(DISTFILES)"; \ if test "$(PACKAGE)" = "gettext-tools"; then \ dists="$$dists Makevars.template"; \ fi; \ if test -f $(srcdir)/$(DOMAIN).pot; then \ dists="$$dists $(DOMAIN).pot stamp-po"; \ fi; \ if test -f $(srcdir)/ChangeLog; then \ dists="$$dists ChangeLog"; \ fi; \ for i in 0 1 2 3 4 5 6 7 8 9; do \ if test -f $(srcdir)/ChangeLog.$$i; then \ dists="$$dists ChangeLog.$$i"; \ fi; \ done; \ if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \ for file in $$dists; do \ if test -f $$file; then \ cp -p $$file $(distdir) || exit 1; \ else \ cp -p $(srcdir)/$$file $(distdir) || exit 1; \ fi; \ done # Custom target added to gather the .po files in the top-level directory and # with the correct names. The Win32 gettext implementation keeps them next to # the .mo files. gather-po-files: for dir in *; do \ if test -d $${dir}; then \ cp $${dir}/LC_MESSAGES/darkradiant.po $${dir}.po; \ fi; \ done update-po: Makefile $(MAKE) $(DOMAIN).pot-update test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES) $(MAKE) update-gmo # General rule for creating PO files. .nop.po-create: @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \ echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \ exit 1 # General rule for updating PO files. .nop.po-update: @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \ if test "$(PACKAGE)" = "gettext-tools" && test "$(CROSS_COMPILING)" != "yes"; then PATH=`pwd`/../src:$$PATH; fi; \ tmpdir=`pwd`; \ echo "$$lang:"; \ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ echo "$${cdcmd}$(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \ cd $(srcdir); \ if { case `$(MSGMERGE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \ $(MSGMERGE) $(MSGMERGE_OPTIONS) -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \ *) \ $(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \ esac; \ }; then \ if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ rm -f $$tmpdir/$$lang.new.po; \ else \ if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ :; \ else \ echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ exit 1; \ fi; \ fi; \ else \ echo "msgmerge for $$lang.po failed!" 1>&2; \ rm -f $$tmpdir/$$lang.new.po; \ fi $(DUMMYPOFILES): update-gmo: Makefile gather-po-files $(GMOFILES) @: # Recreate Makefile by invoking config.status. Explicitly invoke the shell, # because execution permission bits may not work on the current file system. # Use @SHELL@, which is the shell determined by autoconf for the use by its # scripts, not $(SHELL) which is hardwired to /bin/sh and may be deficient. Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@ cd $(top_builddir) \ && @SHELL@ ./config.status $(subdir)/$@.in po-directories force: # Tell versions [3.59,3.63) of GNU make not to export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: DarkRadiant-2.5.0/install/i18n/Makevars000066400000000000000000000034721321750546400176400ustar00rootroot00000000000000# Makefile variables for PO directory in any package using GNU gettext. # Usually the message domain is the same as the package name. DOMAIN = $(PACKAGE) # These two variables depend on the location of this directory. subdir = install/i18n top_builddir = ../.. # These options get passed to xgettext. XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --qt # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding # package. (Note that the msgstr strings, extracted from the package's # sources, belong to the copyright holder of the package.) Translators are # expected to transfer the copyright for their translations to this person # or entity, or to disclaim their copyright. The empty string stands for # the public domain; in this case the translators are expected to disclaim # their copyright. COPYRIGHT_HOLDER = The Dark Mod Team and contributors # This is the email address or URL to which the translators shall report # bugs in the untranslated strings: # - Strings which are not entire sentences, see the maintainer guidelines # in the GNU gettext documentation, section 'Preparing Strings'. # - Strings which use unclear terms or require additional context to be # understood. # - Strings which make invalid assumptions about notation of date, time or # money. # - Pluralisation problems. # - Incorrect English spelling. # - Incorrect formatting. # It can be your email address, or a mailing list address where translators # can write to without being subscribed, or the URL of a web page through # which the translators can contact you. MSGID_BUGS_ADDRESS = http://thedarkmod.com # This is the list of locale categories, beyond LC_MESSAGES, for which the # message catalogs shall be used. It is usually empty. EXTRA_LOCALE_CATEGORIES = DarkRadiant-2.5.0/install/i18n/POTFILES.in000066400000000000000000000171021321750546400177140ustar00rootroot00000000000000radiant/textool/TexTool.cpp radiant/settings/GameManager.cpp radiant/settings/LanguageManager.cpp radiant/modulesystem/ModuleRegistry.cpp radiant/brushmanip.cpp radiant/patchmanip.cpp radiant/selection/shaderclipboard/ShaderClipboard.cpp radiant/selection/algorithm/Shader.cpp radiant/selection/algorithm/Curves.cpp radiant/selection/algorithm/Entity.cpp radiant/selection/algorithm/Group.cpp radiant/selection/algorithm/Primitives.cpp radiant/selection/algorithm/Transformation.cpp radiant/selection/selectionset/SelectionSetToolmenu.cpp radiant/clipper/Clipper.cpp radiant/entity.cpp radiant/mainframe_old.cpp radiant/layers/LayerSystem.cpp radiant/map/FindMapElements.cpp radiant/map/MapResource.cpp radiant/map/CounterManager.cpp radiant/map/RegionManager.cpp radiant/map/PointFile.cpp radiant/map/AutoSaver.cpp radiant/map/MapFileChooserPreview.cpp radiant/map/Map.cpp radiant/camera/CameraSettings.cpp radiant/camera/FloatingCamWnd.cpp radiant/xyview/GlobalXYWnd.cpp radiant/xyview/XYWnd.cpp radiant/referencecache/ModelCache.cpp radiant/ui/about/AboutDialog.cpp radiant/ui/overlay/OverlayDialog.cpp radiant/ui/mru/MRU.cpp radiant/ui/ortho/OrthoContextMenu.cpp radiant/ui/common/ShaderChooser.cpp radiant/ui/common/EntityChooser.cpp radiant/ui/common/SoundShaderPreview.cpp radiant/ui/common/TexturePreviewCombo.cpp radiant/ui/common/SoundChooser.cpp radiant/ui/common/CommandEntry.cpp radiant/ui/common/ShaderSelector.cpp radiant/ui/common/ShaderDefinitionView.cpp radiant/ui/commandlist/ShortcutChooser.cpp radiant/ui/commandlist/CommandList.cpp radiant/ui/einspector/SoundPropertyEditor.cpp radiant/ui/einspector/ClassnamePropertyEditor.cpp radiant/ui/einspector/AddPropertyDialog.cpp radiant/ui/einspector/ModelPropertyEditor.cpp radiant/ui/einspector/SkinChooser.cpp radiant/ui/einspector/TexturePropertyEditor.cpp radiant/ui/einspector/EntityPropertyEditor.cpp radiant/ui/einspector/Vector3PropertyEditor.cpp radiant/ui/einspector/EntityInspector.cpp radiant/ui/einspector/SkinPropertyEditor.cpp radiant/ui/einspector/LightTextureChooser.cpp radiant/ui/mapinfo/ShaderInfoTab.cpp radiant/ui/mapinfo/MapInfoDialog.cpp radiant/ui/mapinfo/EntityInfoTab.cpp radiant/ui/mapinfo/ModelInfoTab.cpp radiant/ui/mainframe/EmbeddedLayout.cpp radiant/ui/mainframe/SplitPaneLayout.cpp radiant/ui/mainframe/LayoutCommand.h radiant/ui/mainframe/FloatingLayout.cpp radiant/ui/mainframe/MainFrame.cpp radiant/ui/entitychooser/EntityClassChooser.cpp radiant/ui/menu/FiltersMenu.cpp radiant/ui/prefdialog/PrefDialog.cpp radiant/ui/lightinspector/LightInspector.cpp radiant/ui/findshader/FindShader.cpp radiant/ui/particles/ParticlesChooser.cpp radiant/ui/layers/LayerControl.cpp radiant/ui/layers/LayerControlDialog.cpp radiant/ui/mediabrowser/MediaBrowser.cpp radiant/ui/mediabrowser/TextureDirectoryLoader.h radiant/ui/texturebrowser/TextureBrowser.cpp radiant/ui/surfaceinspector/SurfaceInspector.cpp radiant/ui/modelselector/ModelSelector.cpp radiant/ui/modelselector/ModelFileFunctor.h radiant/ui/brush/QuerySidesDialog.cpp radiant/ui/transform/TransformDialog.cpp radiant/ui/patch/PatchThickenDialog.cpp radiant/ui/patch/BulgePatchDialog.cpp radiant/ui/patch/PatchInspector.cpp radiant/ui/patch/PatchCreateDialog.cpp radiant/ui/patch/CapDialog.cpp radiant/ui/filterdialog/FilterEditor.cpp radiant/ui/filterdialog/FilterDialog.cpp radiant/main.cpp radiant/brush/BrushModule.cpp radiant/brush/csg/CSG.cpp radiant/patch/PatchCreators.h radiant/patch/Patch.cpp include/i18n.h libs/gtkutil/ModalProgressDialog.cpp libs/gtkutil/IConv.h libs/gtkutil/FileChooser.cpp libs/gtkutil/dialog/MessageBox.cpp libs/gtkutil/ConsoleView.cpp libs/gtkutil/PathEntry.cpp libs/pivot.h libs/radiant_jpeglib.h libs/picomodel/pm_lwo.c libs/picomodel/lwo/lwo2.h libs/picomodel/lwo/lwob.c plugins/eclasstree/plugin.cpp plugins/eclasstree/EClassTree.cpp plugins/entity/EntityCreator.cpp plugins/dm.conversation/plugin.cpp plugins/dm.conversation/ConversationEntityFinder.h plugins/dm.conversation/CommandEditor.cpp plugins/dm.conversation/ConversationDialog.cpp plugins/dm.conversation/ConversationEntity.cpp plugins/dm.conversation/ConversationKeyExtractor.cpp plugins/dm.conversation/ConversationEditor.cpp plugins/fonts/FontLoader.cpp plugins/eventmanager/MouseEvents.cpp plugins/eventmanager/Modifiers.cpp plugins/mapdoom3/NodeImporter.cpp plugins/mapdoom3/Doom3PrefabFormat.cpp plugins/mapdoom3/primitiveparsers/BrushDef.cpp plugins/mapdoom3/primitiveparsers/BrushDef3.cpp plugins/mapdoom3/InfoFile.cpp plugins/mapdoom3/Doom3MapFormat.cpp plugins/dm.gui/plugin.cpp plugins/dm.gui/XDataSelector.cpp plugins/dm.gui/ReadableReloader.h plugins/dm.gui/GuiSelector.cpp plugins/dm.gui/XData.h plugins/dm.gui/ReadablePopulator.h plugins/dm.gui/ReadableEditorDialog.cpp plugins/dm.gui/XData.cpp plugins/dm.gui/XdFileChooserDialog.cpp plugins/dm.stimresponse/ResponseEffect.cpp plugins/dm.stimresponse/EffectEditor.cpp plugins/dm.stimresponse/plugin.cpp plugins/dm.stimresponse/StimEditor.cpp plugins/dm.stimresponse/SRPropertyRemover.cpp plugins/dm.stimresponse/CustomStimEditor.cpp plugins/dm.stimresponse/StimResponseEditor.cpp plugins/dm.stimresponse/StimTypes.cpp plugins/dm.stimresponse/ResponseEditor.cpp plugins/dm.stimresponse/SRPropertyLoader.cpp plugins/dm.stimresponse/ClassEditor.cpp plugins/dm.difficulty/plugin.cpp plugins/dm.difficulty/DifficultyDialog.cpp plugins/dm.difficulty/Setting.cpp plugins/dm.difficulty/DifficultyEditor.cpp plugins/filetypes/FileTypeRegistry.cpp plugins/entitylist/EntityList.cpp plugins/dm.objectives/ObjectivesEditor.cpp plugins/dm.objectives/Specifier.cpp plugins/dm.objectives/ObjectiveEntityFinder.h plugins/dm.objectives/LogicEditor.cpp plugins/dm.objectives/ComponentType.cpp plugins/dm.objectives/ObjectiveKeyExtractor.cpp plugins/dm.objectives/objectives.cpp plugins/dm.objectives/MissionLogicDialog.cpp plugins/dm.objectives/SpecifierType.cpp plugins/dm.objectives/ce/LocationComponentEditor.cpp plugins/dm.objectives/ce/DistanceComponentEditor.cpp plugins/dm.objectives/ce/ItemComponentEditor.cpp plugins/dm.objectives/ce/InfoLocationComponentEditor.cpp plugins/dm.objectives/ce/AIFindItemComponentEditor.cpp plugins/dm.objectives/ce/ReadableOpenedComponentEditor.cpp plugins/dm.objectives/ce/CustomComponentEditor.cpp plugins/dm.objectives/ce/DestroyComponentEditor.cpp plugins/dm.objectives/ce/ReadableClosedComponentEditor.cpp plugins/dm.objectives/ce/ReadablePageReachedComponentEditor.cpp plugins/dm.objectives/ce/AlertComponentEditor.cpp plugins/dm.objectives/ce/PickpocketComponentEditor.cpp plugins/dm.objectives/ce/CustomClockedComponentEditor.cpp plugins/dm.objectives/ce/KillComponentEditor.cpp plugins/dm.objectives/ce/KnockoutComponentEditor.cpp plugins/dm.objectives/ce/AIFindBodyComponentEditor.cpp plugins/dm.objectives/ComponentsDialog.cpp plugins/dm.objectives/ObjectiveEntity.cpp plugins/dm.objectives/DifficultyPanel.cpp plugins/dm.editing/plugin.cpp plugins/dm.editing/AIVocalSetPropertyEditor.cpp plugins/dm.editing/AIHeadPropertyEditor.cpp plugins/dm.editing/AIHeadChooserDialog.cpp plugins/dm.editing/FixupMapDialog.cpp plugins/dm.editing/AIVocalSetPreview.cpp plugins/dm.editing/AIVocalSetChooserDialog.cpp plugins/dm.editing/FixupMap.cpp plugins/image/jpeg.cpp plugins/script/ScriptWindow.cpp plugins/script/ScriptMenu.cpp plugins/script/ScriptingSystem.cpp plugins/uimanager/colourscheme/ColourSchemeEditor.cpp plugins/uimanager/FilterMenu.cpp plugins/uimanager/ParticlePreview.cpp plugins/uimanager/ToolbarManager.cpp plugins/uimanager/GroupDialog.cpp plugins/uimanager/MenuItem.cpp plugins/undo/UndoSystem.cpp plugins/grid/Grid.cpp plugins/wavefront/WaveFrontModule.cpp install/ui/ObjectivesEditor.glade install/ui/EntityClassChooser.glade install/ui/CamWnd.glade install/ui/AddPropertyDialog.glade DarkRadiant-2.5.0/install/i18n/darkradiant.pot000066400000000000000000004352371321750546400211640ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: greebo@angua.at\n" "POT-Creation-Date: 2017-12-17 17:20+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ..\..\radiant\brush\BrushModule.cpp:27 msgid "Settings/Primitives" msgstr "" #: ..\..\radiant\brush\BrushModule.cpp:31 msgid "Default texture scale" msgstr "" #: ..\..\radiant\brush\BrushModule.cpp:34 msgid "Enable Texture Lock (for Brushes)" msgstr "" #: ..\..\radiant\brush\csg\CSG.cpp:284 msgid "This Is Not Dromed Warning" msgstr "" #: ..\..\radiant\brush\csg\CSG.cpp:285 msgid "" "Note: be careful when using the CSG tool, as you might end up\n" "with an unnecessary number of tiny brushes and/or leaks.\n" "This popup will not be shown again." msgstr "" #: ..\..\radiant\brush\csg\CSG.cpp:297 ..\..\radiant\brush\csg\CSG.cpp:298 msgid "CSG Subtract: No brushes selected." msgstr "" #: ..\..\radiant\brush\csg\CSG.cpp:408 ..\..\radiant\brush\csg\CSG.cpp:409 msgid "CSG Merge: No brushes selected." msgstr "" #: ..\..\radiant\camera\CameraSettings.cpp:56 msgid "Settings/Camera" msgstr "" #: ..\..\radiant\camera\CameraSettings.cpp:59 msgid "Movement Speed (game units)" msgstr "" #: ..\..\radiant\camera\CameraSettings.cpp:60 msgid "Rotation Speed" msgstr "" #: ..\..\radiant\camera\CameraSettings.cpp:63 msgid "Freelook mode can be toggled" msgstr "" #: ..\..\radiant\camera\CameraSettings.cpp:64 msgid "Discrete movement (non-freelook mode)" msgstr "" #: ..\..\radiant\camera\CameraSettings.cpp:65 msgid "Enable far-clip plane (hides distant objects)" msgstr "" #: ..\..\radiant\camera\CameraSettings.cpp:68 msgid "Invert mouse vertical axis (freelook mode)" msgstr "" #: ..\..\radiant\camera\CameraSettings.cpp:71 msgid "Solid selection boxes" msgstr "" #: ..\..\radiant\camera\CameraSettings.cpp:74 msgid "Show camera toolbar" msgstr "" #: ..\..\radiant\camera\FloatingCamWnd.cpp:18 xml_file_content.cpp:62 msgid "Camera" msgstr "" #: ..\..\radiant\camera\tools\FreeMoveTool.h:23 msgid "Freemove Mode" msgstr "" #: ..\..\radiant\camera\tools\JumpToObjectTool.h:22 msgid "Jump to Object" msgstr "" #: ..\..\radiant\camera\tools\PanViewTool.h:23 msgid "Pan Camera View" msgstr "" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:116 msgid "Pick Shader" msgstr "" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:139 msgid "Paste Shader Projected" msgstr "" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:158 msgid "Paste Shader Natural" msgstr "" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:177 msgid "Paste Texture Coordinates" msgstr "" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:196 msgid "Paste Shader to all Brush Faces" msgstr "" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:215 msgid "Paste Shader Name" msgstr "" #: ..\..\radiant\clipper\Clipper.cpp:40 msgid "Settings/Clipper" msgstr "" #: ..\..\radiant\clipper\Clipper.cpp:42 msgid "Clipper tool uses caulk texture" msgstr "" #: ..\..\radiant\clipper\Clipper.cpp:43 msgid "Caulk shader name" msgstr "" #: ..\..\radiant\layers\LayerSystem.cpp:32 msgid "Default" msgstr "" #: ..\..\radiant\layers\LayerSystem.cpp:558 #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1538 msgid "Enter Name" msgstr "" #: ..\..\radiant\layers\LayerSystem.cpp:559 msgid "Enter Layer Name" msgstr "" #: ..\..\radiant\layers\LayerSystem.cpp:571 msgid "Cannot create layer with empty name." msgstr "" #: ..\..\radiant\layers\LayerSystem.cpp:585 msgid "This name already exists." msgstr "" #: ..\..\radiant\map\algorithm\Export.cpp:94 msgid "" "To replace the selection with the exported model\n" "the output path must be located within the mod/project." msgstr "" #: ..\..\radiant\map\algorithm\Export.cpp:133 #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:425 msgid "Unable to create model, classname not found." msgstr "" #: ..\..\radiant\map\algorithm\MapExporter.cpp:114 msgid "Writing map" msgstr "" #: ..\..\radiant\map\algorithm\MapExporter.cpp:222 msgid "Writing node {0:d}" msgstr "" #: ..\..\radiant\map\algorithm\MapImporter.cpp:41 msgid "Loading map" msgstr "" #: ..\..\radiant\map\algorithm\MapImporter.cpp:44 #: ..\..\radiant\map\algorithm\MapImporter.cpp:59 msgid "Loading entity {0:d}\n" msgstr "" #: ..\..\radiant\map\algorithm\Models.cpp:94 #: ..\..\radiant\map\algorithm\Models.cpp:110 ..\..\radiant\map\Map.cpp:111 #: ..\..\radiant\map\Map.cpp:426 ..\..\radiant\map\Map.cpp:525 #: ..\..\radiant\map\Map.cpp:553 ..\..\radiant\RadiantModule.cpp:106 #: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:323 msgid "Processing..." msgstr "" #: ..\..\radiant\map\algorithm\Models.cpp:94 #: ..\..\radiant\map\algorithm\Models.cpp:110 msgid "Reloading Models" msgstr "" #: ..\..\radiant\map\AutoSaver.cpp:204 msgid "Snapshot Folder Size Warning" msgstr "" #: ..\..\radiant\map\AutoSaver.cpp:205 msgid "" "The snapshots saved for this map are exceeding the configured size limit.\n" "Consider cleaning up the folder {0}" msgstr "" #: ..\..\radiant\map\AutoSaver.cpp:330 msgid "Settings/Autosave" msgstr "" #: ..\..\radiant\map\AutoSaver.cpp:333 msgid "Enable Autosave" msgstr "" #: ..\..\radiant\map\AutoSaver.cpp:334 msgid "Autosave Interval (in minutes)" msgstr "" #: ..\..\radiant\map\AutoSaver.cpp:336 msgid "Save Snapshots" msgstr "" #: ..\..\radiant\map\AutoSaver.cpp:337 msgid "Snapshot folder (relative to map folder)" msgstr "" #: ..\..\radiant\map\AutoSaver.cpp:338 msgid "Max total Snapshot size per map (MB)" msgstr "" #: ..\..\radiant\map\CounterManager.cpp:63 msgid "" "Number of brushes/patches/entities in this map\n" "(Number of selected items shown in parentheses)" msgstr "" #: ..\..\radiant\map\CounterManager.cpp:81 msgid "Brushes: {0:d} ({1:d}) Patches: {2:d} ({3:d}) Entities: {4:d} ({5:d})" msgstr "" #: ..\..\radiant\map\FindMapElements.cpp:95 msgid "Find Brush" msgstr "" #: ..\..\radiant\map\FindMapElements.cpp:97 msgid "Entity Number:" msgstr "" #: ..\..\radiant\map\FindMapElements.cpp:98 msgid "Brush Number:" msgstr "" #: ..\..\radiant\map\infofile\InfoFile.cpp:68 msgid "Map Info File Version invalid" msgstr "" #: ..\..\radiant\map\Map.cpp:64 msgid "unnamed.map" msgstr "" #: ..\..\radiant\map\Map.cpp:111 msgid "Loading textures..." msgstr "" #: ..\..\radiant\map\Map.cpp:428 msgid "Preprocessing" msgstr "" #: ..\..\radiant\map\Map.cpp:437 msgid "" "Failure running map pre-save event:\n" "{0}" msgstr "" #: ..\..\radiant\map\Map.cpp:445 msgid "Saving Map" msgstr "" #: ..\..\radiant\map\Map.cpp:484 msgid "Importing..." msgstr "" #: ..\..\radiant\map\Map.cpp:578 msgid "" "Save changes to map \"{0}\"\n" "before closing?" msgstr "" #: ..\..\radiant\map\Map.cpp:585 msgid "{0:d} minutes" msgstr "" #: ..\..\radiant\map\Map.cpp:589 msgid "{0:d} seconds" msgstr "" #: ..\..\radiant\map\Map.cpp:593 msgid "" "If you don't save, changes from the last {0}\n" "will be lost." msgstr "" #: ..\..\radiant\map\Map.cpp:647 msgid "Save Map" msgstr "" #: ..\..\radiant\map\Map.cpp:687 msgid "Save Copy As..." msgstr "" #: ..\..\radiant\map\Map.cpp:784 msgid "New Map" msgstr "" #: ..\..\radiant\map\Map.cpp:793 ..\..\radiant\ui\mru\MRU.cpp:93 msgid "Open Map" msgstr "" #: ..\..\radiant\map\Map.cpp:797 msgid "Open map" msgstr "" #: ..\..\radiant\map\Map.cpp:811 msgid "Import map" msgstr "" #: ..\..\radiant\map\Map.cpp:840 msgid "Export selection" msgstr "" #: ..\..\radiant\map\Map.cpp:855 msgid "Save selected as Prefab" msgstr "" #: ..\..\radiant\map\Map.cpp:936 msgid "" "Failure reading map from clipboard:\n" "{0}" msgstr "" #: ..\..\radiant\map\MapFileManager.cpp:32 msgid "Map" msgstr "" #: ..\..\radiant\map\MapFileManager.cpp:33 xml_file_content.cpp:91 msgid "Region" msgstr "" #: ..\..\radiant\map\MapFileManager.cpp:34 #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:344 msgid "Prefab" msgstr "" #: ..\..\radiant\map\MapResource.cpp:255 ..\..\radiant\map\MapResource.cpp:527 msgid "File is write-protected: {0}" msgstr "" #: ..\..\radiant\map\MapResource.cpp:361 msgid "" "Could not determine map format of file:\n" "{0}" msgstr "" #: ..\..\radiant\map\MapResource.cpp:408 msgid "Map loading cancelled" msgstr "" #: ..\..\radiant\map\MapResource.cpp:420 msgid "" "Failure reading map file:\n" "{0}\n" "\n" "{1}" msgstr "" #: ..\..\radiant\map\MapResource.cpp:483 ..\..\radiant\map\MapResource.cpp:502 msgid "" "Failure opening file:\n" "{0}" msgstr "" #: ..\..\radiant\map\MapResource.cpp:592 msgid "Map writing cancelled" msgstr "" #: ..\..\radiant\map\MapResource.cpp:607 msgid "Could not open output streams for writing" msgstr "" #: ..\..\radiant\map\PointFile.cpp:98 msgid "Could not open pointfile: {0}" msgstr "" #: ..\..\radiant\map\RegionManager.cpp:174 msgid "Warning: Camera not within region, can't set info_player_start." msgstr "" #: ..\..\radiant\map\RegionManager.cpp:262 msgid "Could not set Region: XY Top View not found." msgstr "" #: ..\..\radiant\map\RegionManager.cpp:289 msgid "Could not set Region: please select a single Brush." msgstr "" #: ..\..\radiant\map\RegionManager.cpp:314 msgid "This command is not available in component mode." msgstr "" #: ..\..\radiant\map\RegionManager.cpp:320 msgid "Could not set Region: nothing selected." msgstr "" #: ..\..\radiant\map\RegionManager.cpp:337 msgid "Export region" msgstr "" #: ..\..\radiant\model\ModelExporter.cpp:267 #: ..\..\plugins\particles\ParticlesManager.cpp:323 #: ..\..\plugins\particles\ParticlesManager.cpp:351 msgid "Cannot open file for writing: {0}" msgstr "" #: ..\..\radiant\model\ModelExporter.cpp:289 msgid "Could not rename the existing file to .bak: {0}" msgstr "" #: ..\..\radiant\model\ModelExporter.cpp:303 msgid "Could not rename the temporary file: {0}" msgstr "" #: ..\..\radiant\model\ModelFormatManager.cpp:42 msgid "Settings/Model Export" msgstr "" #: ..\..\radiant\model\ModelFormatManager.cpp:51 msgid "Export Format for scaled Models" msgstr "" #: ..\..\radiant\modulesystem\ModuleRegistry.cpp:131 msgid "Initialising Module: {0}" msgstr "" #: ..\..\radiant\modulesystem\ModuleRegistry.cpp:147 msgid "Searching for Modules" msgstr "" #: ..\..\radiant\modulesystem\ModuleRegistry.cpp:161 msgid "Initialising Modules" msgstr "" #: ..\..\radiant\modulesystem\ModuleRegistry.cpp:175 msgid "Modules initialised" msgstr "" #: ..\..\radiant\patch\algorithm\General.cpp:110 msgid "" "Cannot stitch textures. \n" "Could not cast nodes to patches." msgstr "" #: ..\..\radiant\patch\algorithm\General.cpp:119 msgid "" "Cannot stitch patch textures. \n" "Exactly 2 patches must be selected." msgstr "" #: ..\..\radiant\patch\algorithm\General.cpp:157 msgid "Cannot bulge patch. No patches selected." msgstr "" #: ..\..\radiant\patch\algorithm\Prefab.cpp:244 msgid "Cannot create end-cap, patch must have a width of 5." msgstr "" #: ..\..\radiant\patch\algorithm\Prefab.cpp:251 msgid "Cannot create bevel-cap, patch must have a width of 3." msgstr "" #: ..\..\radiant\patch\algorithm\Prefab.cpp:260 msgid "Cannot create cylinder-cap, patch must have a width of 9." msgstr "" #: ..\..\radiant\patch\Patch.cpp:1638 msgid "Sorry. Patch is not suitable for this kind of operation." msgstr "" #: ..\..\radiant\patch\PatchCreators.cpp:62 msgid "Settings/Patch" msgstr "" #: ..\..\radiant\patch\PatchCreators.cpp:63 msgid "Patch Subdivide Threshold" msgstr "" #: ..\..\radiant\RadiantApp.cpp:104 msgid "Disable sound for this session." msgstr "" #: ..\..\radiant\RadiantApp.cpp:105 msgid "Verbose logging." msgstr "" #: ..\..\radiant\selection\algorithm\Curves.cpp:188 msgid "Can't append curve point - no entities with curve selected." msgstr "" #: ..\..\radiant\selection\algorithm\Curves.cpp:198 msgid "Can't remove curve points - must be in vertex editing mode." msgstr "" #: ..\..\radiant\selection\algorithm\Curves.cpp:218 msgid "Can't remove curve points - no entities with curves selected." msgstr "" #: ..\..\radiant\selection\algorithm\Curves.cpp:228 msgid "Can't insert curve points - must be in vertex editing mode." msgstr "" #: ..\..\radiant\selection\algorithm\Curves.cpp:248 msgid "Can't insert curve points - no entities with curves selected." msgstr "" #: ..\..\radiant\selection\algorithm\Curves.cpp:269 msgid "Can't convert curves - no entities with curves selected." msgstr "" #: ..\..\radiant\selection\algorithm\Entity.cpp:82 msgid "The name {0} already exists in this map!" msgstr "" #: ..\..\radiant\selection\algorithm\Entity.cpp:106 msgid "Cannot set classname to an empty string." msgstr "" #: ..\..\radiant\selection\algorithm\Entity.cpp:112 msgid "Cannot change classname to worldspawn." msgstr "" #: ..\..\radiant\selection\algorithm\Entity.cpp:131 msgid "Cannot change classname of worldspawn entity." msgstr "" #: ..\..\radiant\selection\algorithm\Entity.cpp:169 msgid "Critical: Cannot find selected entities." msgstr "" #: ..\..\radiant\selection\algorithm\Entity.cpp:174 #: ..\..\radiant\selection\algorithm\Entity.cpp:190 msgid "Exactly two entities must be selected for this operation." msgstr "" #: ..\..\radiant\selection\algorithm\Entity.cpp:236 msgid "Unable to create entity {0}, no brushes selected." msgstr "" #: ..\..\radiant\selection\algorithm\Group.cpp:242 msgid "" "Cannot reparent primitives to entity. Please select at least one brush/patch " "and exactly one func_* entity. (The entity has to be selected last.)" msgstr "" #: ..\..\radiant\selection\algorithm\Group.cpp:392 msgid "" "Cannot merge entities, the selection must consist of func_* entities only.\n" "(The first selected entity will be preserved.)" msgstr "" #: ..\..\radiant\selection\algorithm\Group.cpp:403 msgid "Groups can be formed in Primitive and Group Part selection mode only" msgstr "" #: ..\..\radiant\selection\algorithm\Group.cpp:408 msgid "Nothing selected, cannot group anything" msgstr "" #: ..\..\radiant\selection\algorithm\Group.cpp:413 msgid "Select more than one element to form a group" msgstr "" #: ..\..\radiant\selection\algorithm\Group.cpp:439 msgid "The selected elements already form a group" msgstr "" #: ..\..\radiant\selection\algorithm\Group.cpp:465 msgid "Groups can be dissolved in Primitive and Group Part selection mode only" msgstr "" #: ..\..\radiant\selection\algorithm\Group.cpp:470 msgid "Nothing selected, cannot un-group anything" msgstr "" #: ..\..\radiant\selection\algorithm\Group.cpp:490 msgid "The selected elements aren't part of any group" msgstr "" #: ..\..\radiant\selection\algorithm\Patch.cpp:60 msgid "Cannot create caps, no patches selected." msgstr "" #: ..\..\radiant\selection\algorithm\Patch.cpp:197 msgid "Cannot thicken patch. Nothing selected." msgstr "" #: ..\..\radiant\selection\algorithm\Primitives.cpp:78 #: ..\..\radiant\selection\algorithm\Primitives.cpp:82 msgid "No patches selected." msgstr "" #: ..\..\radiant\selection\algorithm\Primitives.cpp:243 msgid "Could not create patch." msgstr "" #: ..\..\radiant\selection\algorithm\Primitives.cpp:361 msgid "No faces selected." msgstr "" #: ..\..\radiant\selection\algorithm\Primitives.cpp:382 msgid "{0:d} faces were not suitable (had more than 4 vertices)." msgstr "" #: ..\..\radiant\selection\algorithm\Primitives.cpp:393 #: ..\..\radiant\selection\algorithm\Primitives.cpp:492 msgid "No brushes selected." msgstr "" #: ..\..\radiant\selection\algorithm\Primitives.cpp:592 msgid "At least one brush must be selected for this operation." msgstr "" #: ..\..\radiant\selection\algorithm\Shader.cpp:307 msgid "" "Can't paste shader to entire brush.\n" "Target is not a brush." msgstr "" #: ..\..\radiant\selection\algorithm\Shader.cpp:346 msgid "" "Can't paste Texture Coordinates.\n" "Target patch dimensions must match." msgstr "" #: ..\..\radiant\selection\algorithm\Shader.cpp:355 msgid "Can't paste Texture Coordinates from patches to faces." msgstr "" #: ..\..\radiant\selection\algorithm\Shader.cpp:361 msgid "Can't paste Texture Coordinates from faces." msgstr "" #: ..\..\radiant\selection\algorithm\Shader.cpp:417 msgid "Can't copy Shader. Couldn't retrieve patch." msgstr "" #: ..\..\radiant\selection\algorithm\Shader.cpp:427 msgid "Can't copy Shader. Couldn't retrieve face." msgstr "" #: ..\..\radiant\selection\algorithm\Shader.cpp:433 msgid "Can't copy Shader. Please select a single face or patch." msgstr "" #: ..\..\radiant\selection\algorithm\Transformation.cpp:97 msgid "Cannot scale by zero value." msgstr "" #: ..\..\radiant\selection\group\SelectionGroupManager.cpp:81 #: ..\..\radiant\selection\group\SelectionGroupManager.cpp:97 msgid "Ungroup Selection" msgstr "" #: ..\..\radiant\selection\group\SelectionGroupManager.cpp:84 #: ..\..\radiant\selection\group\SelectionGroupManager.cpp:91 msgid "Group Selection" msgstr "" #: ..\..\radiant\selection\ManipulateMouseTool.cpp:39 msgid "Manipulate" msgstr "" #: ..\..\radiant\selection\RadiantSelectionSystem.cpp:924 msgid "Settings/Selection" msgstr "" #: ..\..\radiant\selection\RadiantSelectionSystem.cpp:926 msgid "" "Ignore light volume bounds when calculating default rotation pivot location" msgstr "" #: ..\..\radiant\selection\SelectionMouseTools.cpp:54 msgid "Select" msgstr "" #: ..\..\radiant\selection\SelectionMouseTools.cpp:176 xml_file_content.cpp:4 msgid "Select Faces" msgstr "" #: ..\..\radiant\selection\SelectionMouseTools.cpp:195 msgid "Cycle Selection" msgstr "" #: ..\..\radiant\selection\SelectionMouseTools.cpp:248 msgid "Cycle Face Selection" msgstr "" #: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:104 msgid "Selection Set: " msgstr "" #: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:112 msgid "Clear Selection Sets" msgstr "" #: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:223 msgid "Delete all selection sets?" msgstr "" #: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:224 msgid "" "This will delete all set definitions. The actual map objects will not be " "affected by this step.\n" "\n" "Continue with that operation?" msgstr "" #: ..\..\radiant\selection\selectionset\SelectionSetToolmenu.cpp:18 msgid "" "Enter a name and hit ENTER to save a set.\n" "\n" "Select an item from the dropdown list to restore the selection.\n" "\n" "Hold SHIFT when opening the dropdown list and selecting the item to de-" "select the set." msgstr "" #: ..\..\radiant\selection\selectionset\SelectionSetToolmenu.cpp:62 msgid "Cannot create selection set" msgstr "" #: ..\..\radiant\selection\selectionset\SelectionSetToolmenu.cpp:63 msgid "" "Cannot create a selection set, there is nothing selected in the current " "scene." msgstr "" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:25 msgid "The name of the shader in the clipboard" msgstr "" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:89 msgid "ShaderClipboard: {0}" msgstr "" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:92 msgid "Face" msgstr "" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:95 msgid "Patch" msgstr "" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:98 #: ..\..\radiant\ui\common\ShaderSelector.cpp:356 #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:73 #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:60 #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:361 xml_file_content.cpp:14 msgid "Shader" msgstr "" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:102 msgid "ShaderClipboard is empty." msgstr "" #: ..\..\radiant\settings\GameManager.cpp:119 #: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:182 msgid "Game" msgstr "" #: ..\..\radiant\settings\GameManager.cpp:120 msgid "" "This page has been moved!\n" "Please use the game settings dialog in the menu: File > Game/Project " "Setup..." msgstr "" #: ..\..\radiant\settings\GameManager.cpp:139 msgid "GameManager: No game type selected, can't continue." msgstr "" #: ..\..\radiant\settings\GameManager.cpp:157 msgid "GameManager: No valid game files found, can't continue." msgstr "" #: ..\..\radiant\settings\GameManager.cpp:193 msgid "No game type selected." msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:102 msgid "Settings/Language" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:103 msgid "Language" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:105 msgid "" "Note: You'll need to restart DarkRadiant\n" "after changing the language setting." msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:216 #: ..\..\radiant\settings\LanguageManager.cpp:253 msgid "English" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:217 msgid "Abkhazian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:218 msgid "Avestan" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:219 msgid "Afrikaans" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:220 msgid "Akan" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:221 msgid "Amharic" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:222 msgid "Aragonese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:223 msgid "Arabic" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:224 msgid "Assamese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:225 msgid "Avaric" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:226 msgid "Aymara" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:227 msgid "Azerbaijani" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:228 msgid "Bashkir" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:229 msgid "Belarusian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:230 msgid "Bulgarian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:231 msgid "Bihari languages" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:232 msgid "Bislama" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:233 msgid "Bambara" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:234 msgid "Bengali" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:235 msgid "Tibetan" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:236 msgid "Breton" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:237 msgid "Bosnian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:238 msgid "Catalan" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:239 msgid "Chechen" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:240 msgid "Chamorro" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:241 msgid "Corsican" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:242 msgid "Cree" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:243 msgid "Czech" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:244 msgid "Chuvash" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:245 #: ..\..\radiant\settings\LanguageManager.cpp:246 msgid "Welsh" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:247 msgid "Danish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:248 msgid "German" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:249 msgid "Divehi" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:250 msgid "Dzongkha" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:251 msgid "Ewe" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:252 msgid "Greek" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:254 msgid "Esperanto" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:255 msgid "Spanish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:256 msgid "Estonian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:257 msgid "Basque" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:258 msgid "Persian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:259 msgid "Fulah" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:260 msgid "Finnish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:261 msgid "Fijian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:262 msgid "Faroese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:263 msgid "French" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:264 msgid "Western Frisian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:265 msgid "Irish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:266 msgid "Gaelic; Scottish Gaelic" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:267 msgid "Galician" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:268 msgid "Guarani" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:269 msgid "Gujarati" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:270 msgid "Manx" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:271 msgid "Hausa" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:272 msgid "Hebrew" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:273 msgid "Hindi" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:274 msgid "Hiri Motu" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:275 msgid "Croatian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:276 msgid "Haitian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:277 msgid "Hungarian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:278 msgid "Armenian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:279 msgid "Herero" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:280 msgid "Interlingua" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:281 msgid "Indonesian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:282 msgid "Interlingue; Occidental" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:283 msgid "Igbo" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:284 msgid "Sichuan Yi; Nuosu" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:285 msgid "Inupiaq" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:286 msgid "Ido" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:287 msgid "Icelandic" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:288 msgid "Italian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:289 msgid "Inuktitut" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:290 msgid "Japanese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:291 msgid "Javanese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:292 msgid "Georgian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:293 msgid "Kongo" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:294 msgid "Kikuyu" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:295 msgid "Kuanyama" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:296 msgid "Kazakh" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:297 msgid "Kalaallisut; Greenlandic" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:298 msgid "Central Khmer" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:299 msgid "Kannada" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:300 msgid "Korean" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:301 msgid "Kanuri" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:302 msgid "Kashmiri" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:303 msgid "Kurdish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:304 msgid "Komi" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:305 msgid "Cornish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:306 msgid "Kirghiz" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:307 msgid "Latin" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:308 msgid "Luxembourgish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:309 msgid "Ganda" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:310 msgid "Limburgan" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:311 msgid "Lingala" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:312 msgid "Lao" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:313 msgid "Lithuanian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:314 msgid "Luba-Katanga" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:315 msgid "Latvian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:316 msgid "Malagasy" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:317 msgid "Marshallese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:318 msgid "Maori" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:319 msgid "Macedonian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:320 msgid "Malayalam" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:321 msgid "Mongolian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:322 msgid "Marathi" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:323 msgid "Malay" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:324 msgid "Maltese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:325 msgid "Burmese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:326 msgid "Nauru" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:327 msgid "Ndebele, North" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:328 msgid "Nepali" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:329 msgid "Ndonga" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:330 msgid "Dutch" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:331 msgid "Norwegian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:332 msgid "Ndebele, South" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:333 msgid "Navajo" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:334 msgid "Chichewa" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:335 msgid "Occitan" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:336 msgid "Ojibwa" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:337 msgid "Oromo" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:338 msgid "Oriya" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:339 msgid "Ossetian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:340 msgid "Panjabi" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:341 msgid "Pali" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:342 msgid "Polish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:343 msgid "Pushto" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:344 msgid "Portuguese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:345 msgid "Quechua" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:346 msgid "Romansh" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:347 msgid "Rundi" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:348 msgid "Romanian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:349 msgid "Russian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:350 msgid "Kinyarwanda" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:351 msgid "Sanskrit" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:352 msgid "Sardinian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:353 msgid "Sindhi" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:354 msgid "Northern Sami" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:355 msgid "Sango" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:356 msgid "Sinhala" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:357 msgid "Slovak" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:358 msgid "Slovenian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:359 msgid "Samoan" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:360 msgid "Shona" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:361 msgid "Somali" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:362 msgid "Albanian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:363 msgid "Serbian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:364 msgid "Swati" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:365 msgid "Sotho, Southern" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:366 msgid "Sundanese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:367 msgid "Swedish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:368 msgid "Swahili" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:369 msgid "Tamil" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:370 msgid "Telugu" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:371 msgid "Tajik" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:372 msgid "Thai" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:373 msgid "Tigrinya" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:374 msgid "Turkmen" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:375 msgid "Tagalog" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:376 msgid "Tswana" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:377 msgid "Tonga" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:378 msgid "Turkish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:379 msgid "Tsonga" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:380 msgid "Tatar" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:381 msgid "Twi" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:382 msgid "Tahitian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:383 msgid "Uighur" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:384 msgid "Ukrainian" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:385 msgid "Urdu" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:386 msgid "Uzbek" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:387 msgid "Venda" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:388 msgid "Vietnamese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:389 msgid "Volapuek" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:390 msgid "Walloon" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:391 msgid "Wolof" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:392 msgid "Xhosa" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:393 msgid "Yiddish" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:394 msgid "Yoruba" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:395 msgid "Zhuang" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:396 msgid "Chinese" msgstr "" #: ..\..\radiant\settings\LanguageManager.cpp:397 msgid "Zulu" msgstr "" #: ..\..\radiant\settings\PreferencePage.cpp:18 msgid "{0} Settings" msgstr "" #: ..\..\radiant\textool\TexTool.cpp:38 msgid "Texture Tool" msgstr "" #: ..\..\radiant\ui\aas\AasControl.cpp:36 msgid "Reload AAS File" msgstr "" #: ..\..\radiant\ui\aas\AasControlDialog.cpp:30 msgid "AAS Viewer" msgstr "" #: ..\..\radiant\ui\aas\AasControlDialog.cpp:80 msgid "Search for AAS Files" msgstr "" #: ..\..\radiant\ui\aas\AasControlDialog.cpp:84 msgid "Show Area Numbers" msgstr "" #: ..\..\radiant\ui\aas\AasControlDialog.cpp:87 msgid "Hide distant Areas" msgstr "" #: ..\..\radiant\ui\about\AboutDialog.cpp:26 msgid "About DarkRadiant" msgstr "" #: ..\..\radiant\ui\about\AboutDialog.cpp:61 msgid "Build date: {0}" msgstr "" #: ..\..\radiant\ui\about\AboutDialog.cpp:70 msgid "Version: {0:d}.{1:d}.{2:d}" msgstr "" #: ..\..\radiant\ui\about\AboutDialog.cpp:80 msgid "Vendor: {0}" msgstr "" #: ..\..\radiant\ui\about\AboutDialog.cpp:81 msgid "Version: {0}" msgstr "" #: ..\..\radiant\ui\about\AboutDialog.cpp:82 msgid "Renderer: {0}" msgstr "" #: ..\..\radiant\ui\brush\QuerySidesDialog.cpp:15 msgid "Enter Number of Sides" msgstr "" #: ..\..\radiant\ui\brush\QuerySidesDialog.cpp:50 msgid "Number of sides: " msgstr "" #: ..\..\radiant\ui\commandlist\CommandList.cpp:20 msgid "Shortcut List" msgstr "" #: ..\..\radiant\ui\commandlist\CommandList.cpp:57 #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:123 #: xml_file_content.cpp:2 msgid "Command" msgstr "" #: ..\..\radiant\ui\commandlist\CommandList.cpp:59 #: ..\..\libs\wxutil\KeyValueTable.cpp:41 msgid "Key" msgstr "" #: ..\..\radiant\ui\commandlist\CommandList.cpp:92 msgid "Reset to Default" msgstr "" #: ..\..\radiant\ui\commandlist\CommandList.cpp:133 msgid "Enter new Shortcut" msgstr "" #: ..\..\radiant\ui\commandlist\CommandList.cpp:184 #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:185 msgid "Reset to default?" msgstr "" #: ..\..\radiant\ui\commandlist\CommandList.cpp:185 #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:186 msgid "" "Really clear all bindings and reload\n" "them from the default settings?" msgstr "" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:39 msgid "Note: This shortcut is already assigned to:" msgstr "" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:49 #: ..\..\plugins\script\ScriptWindow.cpp:85 xml_file_content.cpp:12 #: xml_file_content.cpp:3 xml_file_content.cpp:7 xml_file_content.cpp:10 #: xml_file_content.cpp:18 xml_file_content.cpp:8 xml_file_content.cpp:6 #: xml_file_content.cpp:14 xml_file_content.cpp:15 msgid "OK" msgstr "" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:52 xml_file_content.cpp:2 #: xml_file_content.cpp:6 xml_file_content.cpp:9 xml_file_content.cpp:17 #: xml_file_content.cpp:1 xml_file_content.cpp:14 xml_file_content.cpp:5 #: xml_file_content.cpp:13 xml_file_content.cpp:20 msgid "Cancel" msgstr "" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:136 msgid "" "The specified shortcut is already assigned to {0}\n" "Overwrite the current setting and assign this shortcut to {1} instead?" msgstr "" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:142 msgid "Overwrite existing shortcut?" msgstr "" #: ..\..\radiant\ui\common\CommandEntry.cpp:26 msgid "Go" msgstr "" #: ..\..\radiant\ui\common\EntityChooser.cpp:16 msgid "Select Entity" msgstr "" #: ..\..\radiant\ui\common\ShaderChooser.cpp:15 msgid "Choose Shader" msgstr "" #: ..\..\radiant\ui\common\ShaderDefinitionView.cpp:23 msgid "Material:" msgstr "" #: ..\..\radiant\ui\common\ShaderDefinitionView.cpp:24 msgid "Defined in:" msgstr "" #: ..\..\radiant\ui\common\ShaderDefinitionView.cpp:41 msgid "Definition:" msgstr "" #: ..\..\radiant\ui\common\ShaderDefinitionView.cpp:91 msgid "View Shader Definition" msgstr "" #: ..\..\radiant\ui\common\ShaderSelector.cpp:192 #: ..\..\radiant\ui\common\ShaderSelector.cpp:222 #: ..\..\radiant\ui\einspector\EntityInspector.cpp:476 #: ..\..\plugins\eclasstree\EClassTree.cpp:143 #: ..\..\libs\wxutil\KeyValueTable.cpp:44 msgid "Value" msgstr "" #: ..\..\radiant\ui\common\ShaderSelector.cpp:220 msgid "Attribute" msgstr "" #: ..\..\radiant\ui\common\ShaderSelector.cpp:357 #: ..\..\radiant\ui\common\ShaderSelector.cpp:375 #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:74 msgid "Defined in" msgstr "" #: ..\..\radiant\ui\common\ShaderSelector.cpp:358 #: ..\..\radiant\ui\common\ShaderSelector.cpp:391 #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:75 #: ..\..\plugins\dm.editing\AIVocalSetChooserDialog.cpp:53 #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:133 xml_file_content.cpp:1 msgid "Description" msgstr "" #: ..\..\radiant\ui\common\ShaderSelector.cpp:366 #: ..\..\plugins\dm.conversation\CommandEditor.cpp:222 msgid "None" msgstr "" #: ..\..\radiant\ui\common\ShaderSelector.cpp:374 msgid "Image map" msgstr "" #: ..\..\radiant\ui\common\ShaderSelector.cpp:388 msgid "Light flags" msgstr "" #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:44 msgid "Copy shader name" msgstr "" #: ..\..\radiant\ui\einspector\AddPropertyDialog.cpp:27 msgid "Add property" msgstr "" #: ..\..\radiant\ui\einspector\AddPropertyDialog.cpp:31 msgid "Custom properties defined for this entity class, if any" msgstr "" #: ..\..\radiant\ui\einspector\AddPropertyDialog.cpp:76 #: ..\..\radiant\ui\einspector\EntityInspector.cpp:471 #: ..\..\plugins\eclasstree\EClassTree.cpp:140 msgid "Property" msgstr "" #: ..\..\radiant\ui\einspector\ClassnamePropertyEditor.cpp:26 msgid "Choose entity class..." msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:103 msgid "Show inherited properties" msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:107 msgid "Show help" msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:313 msgid "Add property..." msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:318 msgid "Delete property" msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:326 msgid "Copy Spawnarg(s)" msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:331 msgid "Cut Spawnarg(s)" msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:336 msgid "Paste Spawnarg(s)" msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:348 #: ..\..\radiant\ui\einspector\EntityInspector.cpp:351 #: ..\..\plugins\uimanager\GroupDialog.cpp:30 msgid "Entity" msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:481 msgid "?" msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:1194 msgid "Entity {0}" msgstr "" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:1205 msgid "Entity {0}, Primitive {1}" msgstr "" #: ..\..\radiant\ui\einspector\EntityPropertyEditor.cpp:31 msgid "Choose target entity..." msgstr "" #: ..\..\radiant\ui\einspector\FloatPropertyEditor.cpp:70 #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:62 msgid "Apply..." msgstr "" #: ..\..\radiant\ui\einspector\LightTextureChooser.cpp:48 msgid "Choose texture" msgstr "" #: ..\..\radiant\ui\einspector\ModelPropertyEditor.cpp:40 msgid "Choose model..." msgstr "" #: ..\..\radiant\ui\einspector\ModelPropertyEditor.cpp:45 msgid "Choose particle..." msgstr "" #: ..\..\radiant\ui\einspector\ModelPropertyEditor.cpp:80 msgid "Warning: " msgstr "" #: ..\..\radiant\ui\einspector\ModelPropertyEditor.cpp:81 msgid "" "Changing this entity's model to the selected value will\n" "remove all child primitives from it:\n" msgstr "" #: ..\..\radiant\ui\einspector\SkinChooser.cpp:26 msgid "Choose Skin" msgstr "" #: ..\..\radiant\ui\einspector\SkinChooser.cpp:84 msgid "Skin" msgstr "" #: ..\..\radiant\ui\einspector\SkinChooser.cpp:196 msgid "Matching skins" msgstr "" #: ..\..\radiant\ui\einspector\SkinChooser.cpp:219 msgid "All skins" msgstr "" #: ..\..\radiant\ui\einspector\SkinPropertyEditor.cpp:23 #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:110 msgid "Choose skin..." msgstr "" #: ..\..\radiant\ui\einspector\SoundPropertyEditor.cpp:23 msgid "Choose sound..." msgstr "" #: ..\..\radiant\ui\einspector\TexturePropertyEditor.cpp:23 msgid "Choose texture..." msgstr "" #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:54 msgid "X: " msgstr "" #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:56 msgid " Y: " msgstr "" #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:58 msgid " Z: " msgstr "" #: ..\..\radiant\ui\entitychooser\EntityClassChooser.cpp:25 msgid "Create entity" msgstr "" #: ..\..\radiant\ui\entitychooser\EntityClassChooser.cpp:282 #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:374 #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:397 #: ..\..\plugins\uimanager\SoundChooser.cpp:191 msgid "Loading..." msgstr "" #: ..\..\radiant\ui\entitychooser\EntityClassChooser.cpp:293 #: ..\..\plugins\eclasstree\EClassTree.cpp:127 msgid "Classname" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:21 msgid "Filter Settings" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:138 msgid "enabled" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:138 msgid "disabled" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:182 #: ..\..\plugins\dm.conversation\ConversationDialog.cpp:78 #: ..\..\plugins\entitylist\EntityList.cpp:72 xml_file_content.cpp:1 msgid "Name" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:185 msgid "State" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:265 msgid "NewFilter" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:283 #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:353 msgid "Empty Filter" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:284 msgid "No rules defined for this filter, cannot insert." msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:297 msgid "Name Conflict" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:298 msgid "Cannot add, filter with same name already exists." msgstr "" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:354 msgid "No rules defined for this filter. Delete it?" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:16 msgid "Edit Filter" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:17 msgid "View Filter" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:111 #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:161 #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:306 msgid "show" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:111 #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:162 msgid "hide" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:132 msgid "Index" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:145 #: ..\..\plugins\dm.objectives\ComponentsDialog.cpp:119 #: ..\..\plugins\dm.stimresponse\ClassEditor.cpp:66 #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:91 #: xml_file_content.cpp:19 msgid "Type" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:152 msgid "Entity Key" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:156 msgid "Match" msgstr "" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:167 msgid "Action" msgstr "" #: ..\..\radiant\ui\findshader\FindShader.cpp:27 msgid "Find & Replace Shader" msgstr "" #: ..\..\radiant\ui\findshader\FindShader.cpp:28 msgid "{0:d} shader(s) replaced." msgstr "" #: ..\..\radiant\ui\findshader\FindShader.cpp:34 msgid "" "When picking texture names, click the pick button and use {0}\n" "in the camera view to pick a material name. The picked texture will be\n" "filled in the entry box next to the activated button." msgstr "" #: ..\..\radiant\ui\findshader\FindShader.cpp:137 msgid "Picking Texures" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:64 msgid "Current Grid Size" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:155 msgid "Settings/Grid" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:157 msgid "Default Grid Size" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:161 msgid "Lines" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:162 msgid "Dotted Lines" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:163 msgid "More Dotted Lines" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:164 msgid "Crosses" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:165 msgid "Dots" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:166 msgid "Big Dots" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:167 msgid "Squares" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:169 msgid "Major Grid Style" msgstr "" #: ..\..\radiant\ui\grid\GridManager.cpp:170 msgid "Minor Grid Style" msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:50 msgid "" "Indicates whether anything among the current selection is part of this layer." msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:70 msgid "" "Click to select all in layer, hold SHIFT to deselect, hold CTRL to set as " "active layer." msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:71 msgid "Rename this layer" msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:72 msgid "Delete this layer" msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:73 msgid "Toggle layer visibility" msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:159 msgid "Do you really want to delete this layer?" msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:163 msgid "Confirm Layer Deletion" msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:184 msgid "Rename Layer" msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:185 msgid "Enter new Layer Name" msgstr "" #: ..\..\radiant\ui\layers\LayerControl.cpp:206 msgid "Could not rename layer, please try again." msgstr "" #: ..\..\radiant\ui\layers\LayerControlDialog.cpp:34 #: ..\..\radiant\ui\mapinfo\LayerInfoTab.cpp:18 xml_file_content.cpp:51 msgid "Layers" msgstr "" #: ..\..\radiant\ui\layers\LayerControlDialog.cpp:74 msgid "Show all" msgstr "" #: ..\..\radiant\ui\layers\LayerControlDialog.cpp:75 msgid "Hide all" msgstr "" #: ..\..\radiant\ui\layers\LayerControlDialog.cpp:81 xml_file_content.cpp:2 msgid "New" msgstr "" #: ..\..\radiant\ui\lightinspector\LightInspector.cpp:29 msgid "Light properties" msgstr "" #: ..\..\radiant\ui\mainframe\EmbeddedLayout.cpp:76 #: ..\..\radiant\ui\mainframe\FloatingLayout.cpp:59 #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:111 xml_file_content.cpp:53 msgid "Texture Browser" msgstr "" #: ..\..\radiant\ui\mainframe\EmbeddedLayout.cpp:79 #: ..\..\radiant\ui\mainframe\FloatingLayout.cpp:62 #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:114 msgid "Textures" msgstr "" #: ..\..\radiant\ui\mainframe\LayoutCommand.h:56 msgid "Window Layout" msgstr "" #: ..\..\radiant\ui\mainframe\LayoutCommand.h:80 msgid "Restart required" msgstr "" #: ..\..\radiant\ui\mainframe\LayoutCommand.h:81 msgid "Restart DarkRadiant to apply changes" msgstr "" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:83 msgid "Settings/Multi Monitor" msgstr "" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:102 msgid "Start DarkRadiant on monitor" msgstr "" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:124 msgid "Settings/Compatibility" msgstr "" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:126 msgid "Disable Windows Desktop Composition" msgstr "" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:284 msgid "Exit DarkRadiant" msgstr "" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:394 #: ..\..\radiant\ui\mainframe\MainFrame.cpp:397 msgid "Console" msgstr "" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:145 msgid "Camera Position" msgstr "" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:148 msgid "Top Left" msgstr "" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:150 msgid "Top Right" msgstr "" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:152 msgid "Bottom Left" msgstr "" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:154 msgid "Bottom Right" msgstr "" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:17 msgid "Entities" msgstr "" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:48 msgid "Entity Class" msgstr "" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:51 #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:51 msgid "Count" msgstr "" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:70 msgid "Brushes:" msgstr "" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:71 msgid "Patches:" msgstr "" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:72 msgid "Entities:" msgstr "" #: ..\..\radiant\ui\mapinfo\LayerInfoTab.cpp:49 msgid "Layer" msgstr "" #: ..\..\radiant\ui\mapinfo\LayerInfoTab.cpp:52 msgid "Node Count" msgstr "" #: ..\..\radiant\ui\mapinfo\MapInfoDialog.cpp:23 msgid "Map Info" msgstr "" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:14 msgid "Models" msgstr "" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:45 msgid "Model" msgstr "" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:48 msgid "Polys" msgstr "" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:54 msgid "Skins" msgstr "" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:75 msgid "Models used:" msgstr "" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:76 msgid "Named Skins used:" msgstr "" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:18 msgid "Shaders" msgstr "" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:20 #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:63 msgid "Select elements using this shader" msgstr "" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:21 #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:64 msgid "Deselect elements using this shader" msgstr "" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:63 msgid "Faces" msgstr "" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:66 msgid "Patches" msgstr "" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:86 msgid "Shaders used:" msgstr "" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:52 msgid "Load in Textures view" msgstr "" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:55 msgid "Apply to selection" msgstr "" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:58 msgid "Show Shader Definition" msgstr "" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:61 msgid "Other Materials" msgstr "" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:433 #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:436 msgid "Media" msgstr "" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:529 #: ..\..\plugins\eclasstree\EClassTree.cpp:43 msgid "Loading, please wait..." msgstr "" #: ..\..\radiant\ui\mediabrowser\TextureDirectoryLoader.h:35 msgid "Loading textures" msgstr "" #: ..\..\radiant\ui\menu\FiltersMenu.cpp:64 msgid "Activate &all Filters" msgstr "" #: ..\..\radiant\ui\menu\FiltersMenu.cpp:65 msgid "&Deactivate all Filters" msgstr "" #: ..\..\radiant\ui\menu\FiltersMenu.cpp:68 msgid "Edit Filters..." msgstr "" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:33 msgid "Model Export" msgstr "" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:170 msgid "Empty Filename" msgstr "" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:170 msgid "No filename specified, cannot run exporter" msgstr "" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:185 msgid "Export failed" msgstr "" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:266 msgid "Empty Selection" msgstr "" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:266 msgid "Nothing selected, cannot run exporter" msgstr "" #: ..\..\radiant\ui\modelselector\MaterialsList.cpp:45 msgid "Material" msgstr "" #: ..\..\radiant\ui\modelselector\MaterialsList.cpp:47 xml_file_content.cpp:8 msgid "Visible" msgstr "" #: ..\..\radiant\ui\modelselector\ModelPopulator.h:98 msgid "Building tree..." msgstr "" #: ..\..\radiant\ui\modelselector\ModelPopulator.h:143 #: ..\..\radiant\ui\modelselector\ModelPopulator.h:151 msgid "{0:d} models loaded" msgstr "" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:36 msgid "Choose Model" msgstr "" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:351 msgid "Model Path" msgstr "" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:445 msgid "Model name" msgstr "" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:446 msgid "Skin name" msgstr "" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:447 msgid "Total vertices" msgstr "" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:448 msgid "Total polys" msgstr "" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:449 msgid "Material surfaces" msgstr "" #: ..\..\radiant\ui\mousetool\BindToolDialog.cpp:16 msgid "Select new Binding: {0}" msgstr "" #: ..\..\radiant\ui\mousetool\BindToolDialog.cpp:44 msgid "" "Please select a new button/modifier combination\n" "by clicking on the area below." msgstr "" #: ..\..\radiant\ui\mousetool\BindToolDialog.cpp:51 msgid "Click here to assign" msgstr "" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:23 msgid "Edit Mouse Bindings" msgstr "" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:50 msgid "Reset all mappings to default" msgstr "" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:72 msgid "Double click row to edit a binding" msgstr "" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:122 msgid "Tool" msgstr "" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:125 msgid "Modifier" msgstr "" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:128 msgid "Button" msgstr "" #: ..\..\radiant\ui\mru\MRU.cpp:26 msgid "Recently used Maps" msgstr "" #: ..\..\radiant\ui\mru\MRU.cpp:109 msgid "Could not read map file: {0}" msgstr "" #: ..\..\radiant\ui\mru\MRU.cpp:125 msgid "Settings/Map Files" msgstr "" #: ..\..\radiant\ui\mru\MRU.cpp:127 msgid "Number of most recently used files" msgstr "" #: ..\..\radiant\ui\mru\MRU.cpp:128 msgid "Open last map on startup" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:52 msgid "Create entity..." msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:54 msgid "Create player start here" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:56 msgid "Move player start here" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:58 msgid "Create model..." msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:60 msgid "Surround with monsterclip" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:62 msgid "Create light..." msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:64 msgid "Insert prefab..." msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:66 msgid "Create speaker..." msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:68 msgid "Convert to func_static" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:70 msgid "Reparent primitives" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:71 msgid "Revert to worldspawn" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:72 msgid "Revert part to worldspawn" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:75 msgid "Merge Entities" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:76 xml_file_content.cpp:157 msgid "Make Visportal" msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:325 msgid "Unable to create light, classname not found." msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:352 msgid "Unable to create speaker, classname not found." msgstr "" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:433 msgid "Nothing must be selected for model creation" msgstr "" #: ..\..\radiant\ui\overlay\OverlayDialog.cpp:25 msgid "Background image" msgstr "" #: ..\..\radiant\ui\particles\ParticlesChooser.cpp:37 msgid "Choose particles" msgstr "" #: ..\..\radiant\ui\particles\ParticlesChooser.cpp:66 #: ..\..\plugins\particles\editor\ParticleEditor.cpp:159 msgid "Particle" msgstr "" #: ..\..\radiant\ui\patch\BulgePatchDialog.cpp:9 msgid "Bulge Patch" msgstr "" #: ..\..\radiant\ui\patch\BulgePatchDialog.cpp:10 msgid "Noise:" msgstr "" #: ..\..\radiant\ui\patch\CapDialog.cpp:16 msgid "Create Cap Patch" msgstr "" #: ..\..\radiant\ui\patch\CapDialog.cpp:20 msgid "Bevel" msgstr "" #: ..\..\radiant\ui\patch\CapDialog.cpp:21 msgid "End Cap" msgstr "" #: ..\..\radiant\ui\patch\CapDialog.cpp:22 msgid "Inverted Bevel" msgstr "" #: ..\..\radiant\ui\patch\CapDialog.cpp:23 msgid "Inverted Endcap" msgstr "" #: ..\..\radiant\ui\patch\CapDialog.cpp:24 msgid "Cylinder" msgstr "" #: ..\..\radiant\ui\patch\PatchCreateDialog.cpp:13 msgid "Create Flat Patch Mesh" msgstr "" #: ..\..\radiant\ui\patch\PatchInspector.cpp:29 msgid "Patch Inspector" msgstr "" #: ..\..\radiant\ui\patch\PatchInspector.cpp:30 #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:59 #: ..\..\radiant\ui\transform\TransformDialog.cpp:38 msgid "Step:" msgstr "" #: ..\..\radiant\ui\patch\PatchThickenDialog.cpp:14 msgid "Patch Thicken" msgstr "" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:40 msgid "Choose Prefab" msgstr "" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:98 msgid "Rescan Prefab Folders" msgstr "" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:103 msgid "Create Group out of Prefab parts" msgstr "" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:138 msgid "Browse mod resources" msgstr "" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:141 msgid "Select recently used path:" msgstr "" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:143 msgid "Browse custom path:" msgstr "" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:555 msgid "" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:21 msgid "Game Setup" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:33 msgid "Game Type:" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:136 #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:152 msgid "Invalid Settings" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:137 msgid "Please select a game type" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:150 msgid "" "Warning:\n" "{0}\n" "Do you want to correct these settings?" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:37 msgid "Engine Path" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:42 msgid "Mod (fs_game)" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:47 msgid "Mod Base (fs_game_base)" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:70 #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:139 msgid "Engine path \"{0}\" does not exist.\n" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:77 msgid "The mod base path \"{0}\" does not exist.\n" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:84 msgid "The mod path \"{0}\" does not exist.\n" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:37 msgid "DarkMod Path" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:45 msgid "This is the path where your TheDarkMod.exe is located." msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:49 msgid "" "The FM folder name of the mission you want to work on, e.g. 'saintlucia'." msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:51 msgid "Mission" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:98 msgid "" "The mission path {0} doesn't exist.\n" "Do you intend to start a new mission and create that folder?" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:101 msgid "Start a new Mission named {0}?" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:110 msgid "Could not create directory" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:110 msgid "Failed to create the folder {0}" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:163 msgid "" "The engine executable \"{0}\" could not be found in the specified folder.\n" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:170 msgid "The mission path \"{0}\" does not exist.\n" msgstr "" #: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:23 msgid "DarkRadiant Preferences" msgstr "" #: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:117 msgid "Settings/" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:42 msgid "Surface Inspector" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:43 msgid "Texture Properties" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:44 msgid "Texture Operations" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:52 msgid "Horiz. Shift:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:53 msgid "Vert. Shift:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:54 msgid "Horiz. Scale:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:55 msgid "Vert. Scale:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:56 msgid "Rotation:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:57 #: xml_file_content.cpp:15 msgid "Shader:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:61 msgid "Fit Texture:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:62 msgid "Fit" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:64 msgid "Align Texture:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:65 msgid "Top" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:66 msgid "Bottom" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:67 #: xml_file_content.cpp:16 msgid "Right" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:68 #: xml_file_content.cpp:15 msgid "Left" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:70 msgid "Flip Texture:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:71 msgid "Flip Horizontal" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:72 msgid "Flip Vertical" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:74 msgid "Modify Texture:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:75 msgid "Natural" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:76 msgid "Normalise" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:78 msgid "Default Scale:" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:79 #: xml_file_content.cpp:9 msgid "Texture Lock" msgstr "" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:634 msgid "Both fit values must be > 0." msgstr "" #: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:41 msgid "Seek in Media Browser" msgstr "" #: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:273 #: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:816 msgid "No shader" msgstr "" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:61 msgid "Settings/Texture Browser" msgstr "" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:63 msgid "Uniform texture thumbnail size (pixels)" msgstr "" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:64 msgid "Texture scrollbar" msgstr "" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:65 msgid "Mousewheel Increment" msgstr "" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:66 msgid "Max shadername length" msgstr "" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:68 msgid "Show Texture Filter" msgstr "" #: ..\..\radiant\ui\transform\TransformDialog.cpp:26 msgid "Arbitrary Transformation" msgstr "" #: ..\..\radiant\ui\transform\TransformDialog.cpp:27 msgid "Rotation" msgstr "" #: ..\..\radiant\ui\transform\TransformDialog.cpp:28 msgid "Scale" msgstr "" #: ..\..\radiant\ui\transform\TransformDialog.cpp:30 msgid "X-Axis Rotate:" msgstr "" #: ..\..\radiant\ui\transform\TransformDialog.cpp:31 msgid "Y-Axis Rotate:" msgstr "" #: ..\..\radiant\ui\transform\TransformDialog.cpp:32 msgid "Z-Axis Rotate:" msgstr "" #: ..\..\radiant\ui\transform\TransformDialog.cpp:34 msgid "X-Axis Scale:" msgstr "" #: ..\..\radiant\ui\transform\TransformDialog.cpp:35 msgid "Y-Axis Scale:" msgstr "" #: ..\..\radiant\ui\transform\TransformDialog.cpp:36 msgid "Z-Axis Scale:" msgstr "" #: ..\..\radiant\ui\UserInterfaceModule.cpp:39 msgid "Create Layer..." msgstr "" #: ..\..\radiant\ui\UserInterfaceModule.cpp:41 msgid "Add to Layer..." msgstr "" #: ..\..\radiant\ui\UserInterfaceModule.cpp:42 msgid "Move to Layer..." msgstr "" #: ..\..\radiant\ui\UserInterfaceModule.cpp:43 msgid "Remove from Layer..." msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:196 msgid "Settings/Orthoview" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:198 msgid "View chases Mouse Cursor during Drags" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:199 msgid "Maximum Chase Mouse Speed" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:200 msgid "Update Views on Camera Movement" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:201 msgid "Show Crosshairs" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:202 msgid "Show Grid" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:203 msgid "Show Size Info" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:204 msgid "Show Entity Angle Arrow" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:205 msgid "Show Entity Names" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:206 xml_file_content.cpp:81 msgid "Show Blocks" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:207 msgid "Show Coordinates" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:208 xml_file_content.cpp:84 msgid "Show Axes" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:209 xml_file_content.cpp:83 msgid "Show Window Outline" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:210 xml_file_content.cpp:85 msgid "Show Workzone" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:211 msgid "Translate Manipulator always constrained to Axis" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:212 msgid "Higher Selection Priority for Entities" msgstr "" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:666 msgid "Shows the mouse position in the orthoview" msgstr "" #: ..\..\radiant\xyview\tools\BrushCreatorTool.cpp:25 msgid "Drag-create Brush" msgstr "" #: ..\..\radiant\xyview\tools\CameraAngleTool.h:28 msgid "Point Camera" msgstr "" #: ..\..\radiant\xyview\tools\CameraMoveTool.h:27 msgid "Drag Camera" msgstr "" #: ..\..\radiant\xyview\tools\ClipperTool.cpp:21 xml_file_content.cpp:151 #: xml_file_content.cpp:13 xml_file_content.cpp:18 msgid "Clipper" msgstr "" #: ..\..\radiant\xyview\tools\MeasurementTool.cpp:28 msgid "Measure" msgstr "" #: ..\..\radiant\xyview\tools\MoveViewTool.h:27 msgid "Drag View" msgstr "" #: ..\..\radiant\xyview\tools\ZoomTool.h:30 msgid "Zoom View" msgstr "" #: ..\..\radiant\xyview\XYWnd.cpp:229 msgid "XY Top" msgstr "" #: ..\..\radiant\xyview\XYWnd.cpp:232 msgid "XZ Front" msgstr "" #: ..\..\radiant\xyview\XYWnd.cpp:235 msgid "YZ Side" msgstr "" #: ..\..\radiant\xyview\XYWnd.cpp:498 msgid "x: {0:6.1f} y: {1:6.1f} z: {2:6.1f}" msgstr "" #: ..\..\plugins\dm.conversation\CommandArgumentItem.cpp:118 #: ..\..\plugins\dm.conversation\CommandEditor.cpp:40 msgid "Actor {0:d} ({1})" msgstr "" #: ..\..\plugins\dm.conversation\CommandArgumentItem.cpp:158 msgid "Browse Sound Shaders" msgstr "" #: ..\..\plugins\dm.conversation\CommandArgumentItem.cpp:215 msgid "Browse Animations" msgstr "" #: ..\..\plugins\dm.conversation\CommandEditor.cpp:24 msgid "Edit Command" msgstr "" #: ..\..\plugins\dm.conversation\ConversationDialog.cpp:30 msgid "Conversation Editor" msgstr "" #: ..\..\plugins\dm.conversation\ConversationDialog.cpp:280 msgid "Unable to create conversation Entity: class '{0}' not found." msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:27 msgid "Edit Conversation" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:92 msgid "Actor (click to edit)" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:121 #: xml_file_content.cpp:1 msgid "Actor" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:125 msgid "Wait" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:232 msgid "Actor {0:d}" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:234 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206 msgid "yes" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:234 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206 msgid "no" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:403 msgid "New Actor" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:455 msgid "The actor {0} cannot be found in the current map." msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:462 msgid "Actors missing" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:466 msgid "All actors are correctly referring to entities in the map." msgstr "" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:467 msgid "Actors OK" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEntity.cpp:47 msgid "New Conversation" msgstr "" #: ..\..\plugins\dm.conversation\ConversationEntityFinder.h:78 #: ..\..\plugins\dm.objectives\ObjectiveEntityFinder.cpp:36 msgid "{0} at [ {1} ]" msgstr "" #: ..\..\plugins\dm.conversation\plugin.cpp:50 msgid "Conversations..." msgstr "" #: ..\..\plugins\dm.difficulty\DifficultyDialog.cpp:26 msgid "Difficulty Editor" msgstr "" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:76 xml_file_content.cpp:4 msgid "Setting" msgstr "" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:91 msgid "Assign" msgstr "" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:92 #: ..\..\plugins\dm.stimresponse\ClassEditor.cpp:81 #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:51 #: xml_file_content.cpp:2 xml_file_content.cpp:5 xml_file_content.cpp:8 #: xml_file_content.cpp:12 xml_file_content.cpp:1 xml_file_content.cpp:3 #: xml_file_content.cpp:17 xml_file_content.cpp:6 msgid "Add" msgstr "" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:93 msgid "Multiply" msgstr "" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:94 msgid "Ignore" msgstr "" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:152 msgid "This default setting is overridden, cannot edit." msgstr "" #: ..\..\plugins\dm.difficulty\DifficultySettings.cpp:168 msgid " (overridden)" msgstr "" #: ..\..\plugins\dm.difficulty\plugin.cpp:58 msgid "Difficulty..." msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:60 msgid "Can operate Doors" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:61 msgid "Can light Torches" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:62 msgid "Can operate Switch Lights" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:63 msgid "Can operate Elevators" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:64 msgid "Can greet others" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:65 msgid "Can search" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:66 msgid "AI is civilian" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:67 msgid "Start sleeping" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:68 msgid "Lay down to the left" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:69 msgid "Start sitting" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:70 msgid "Patrol" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:71 msgid "Animal Patrol Mode" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:72 msgid "Start in Alert Idle State" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:73 msgid "Disable Alert Idle State" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:74 msgid "Drunk" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:75 msgid "Body is shoulderable" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:76 msgid "AI doesn't think outside the player PVS" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:78 msgid "AI can drown" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:81 msgid "AI can be flatfooted" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:82 msgid "AI is immune to KOs at high alert levels" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:83 msgid "AI is immune to KOs" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:84 msgid "AI is immune to Gas" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:86 msgid "Team" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:87 msgid "Rank" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:88 msgid "Sitting Angle" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:89 msgid "Drunk Acuity Factor" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:90 msgid "Visual Acuity" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:91 msgid "Audio Acuity" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:93 msgid "Horizontal FOV" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:94 msgid "Vertical FOV" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:96 msgid "Min. Interleave Distance" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:97 msgid "Max. Interleave Distance" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:99 msgid "Health" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:100 msgid "Critical Health" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:101 msgid "Melee Range" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:105 msgid "Appearance" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:110 msgid "Skin: " msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:111 msgid "Head: " msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:111 #: ..\..\plugins\dm.editing\AIHeadPropertyEditor.cpp:31 msgid "Choose AI head..." msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:112 msgid "Vocal Set: " msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:112 msgid "Choose Vocal Set..." msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:117 msgid "Behaviour" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:163 msgid "Abilities" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:178 msgid "Optimization" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:190 msgid "Health / Combat" msgstr "" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:272 #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:275 msgid "AI" msgstr "" #: ..\..\plugins\dm.editing\AIHeadChooserDialog.cpp:18 msgid "Choose AI Head" msgstr "" #: ..\..\plugins\dm.editing\AIVocalSetChooserDialog.cpp:19 msgid "Choose AI Vocal Set" msgstr "" #: ..\..\plugins\dm.editing\AIVocalSetChooserDialog.cpp:44 msgid "Available Sets" msgstr "" #: ..\..\plugins\dm.editing\AIVocalSetPreview.cpp:114 msgid "Error: File not found." msgstr "" #: ..\..\plugins\dm.editing\AIVocalSetPropertyEditor.cpp:31 msgid "Select Vocal Set..." msgstr "" #: ..\..\plugins\dm.editing\DarkmodTxt.cpp:131 msgid "Order of the elements Title/Description/Author/etc. is incorrect" msgstr "" #: ..\..\plugins\dm.editing\FixupMap.cpp:26 msgid "Fixup in progress" msgstr "" #: ..\..\plugins\dm.editing\FixupMap.cpp:60 msgid "Processing line {0}..." msgstr "" #: ..\..\plugins\dm.editing\FixupMap.cpp:65 msgid "Fixup cancelled" msgstr "" #: ..\..\plugins\dm.editing\FixupMap.cpp:71 msgid "Completed" msgstr "" #: ..\..\plugins\dm.editing\FixupMap.cpp:155 #: ..\..\plugins\dm.editing\FixupMap.cpp:166 msgid "File not readable" msgstr "" #: ..\..\plugins\dm.editing\FixupMap.cpp:156 msgid "The specified file doesn't exist." msgstr "" #: ..\..\plugins\dm.editing\FixupMap.cpp:167 msgid "The specified file can't be opened." msgstr "" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:15 msgid "Fixup Map" msgstr "" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:16 msgid "Fixup File" msgstr "" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:47 msgid "{0} shaders replaced." msgstr "" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:48 msgid "{0} entities replaced." msgstr "" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:49 msgid "{0} models replaced." msgstr "" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:50 msgid "{0} spawnargs replaced." msgstr "" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:55 msgid "Errors occurred:" msgstr "" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:61 msgid "(Line {0}): {1}" msgstr "" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:66 msgid "Fixup Results" msgstr "" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:25 msgid "Mission Info Editor (darkmod.txt)" msgstr "" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:44 msgid "" "Failed to parse darkmod.txt:\n" "{0}" msgstr "" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:109 msgid "#" msgstr "" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:112 #: xml_file_content.cpp:17 msgid "Title" msgstr "" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:140 msgid "Add Title" msgstr "" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:145 msgid "Delete Title" msgstr "" #: ..\..\plugins\dm.editing\MissionInfoTextFile.cpp:43 msgid "" "Could not write {0} contents:\n" "{1}" msgstr "" #: ..\..\plugins\dm.editing\MissionReadmeDialog.cpp:23 msgid "Mission Readme Editor (readme.txt)" msgstr "" #: ..\..\plugins\dm.editing\MissionReadmeDialog.cpp:41 msgid "" "Failed to parse readme.txt:\n" "{0}" msgstr "" #: ..\..\plugins\dm.editing\plugin.cpp:65 msgid "Fixup Map..." msgstr "" #: ..\..\plugins\dm.editing\plugin.cpp:75 msgid "Edit Package Info (darkmod.txt)..." msgstr "" #: ..\..\plugins\dm.gui\GuiSelector.cpp:22 msgid "Choose a Gui Definition..." msgstr "" #: ..\..\plugins\dm.gui\GuiSelector.cpp:141 #: ..\..\plugins\dm.gui\GuiSelector.cpp:150 msgid "Gui Path" msgstr "" #: ..\..\plugins\dm.gui\GuiSelector.cpp:146 msgid "One-Sided Readable Guis" msgstr "" #: ..\..\plugins\dm.gui\GuiSelector.cpp:155 msgid "Two-Sided Readable Guis" msgstr "" #: ..\..\plugins\dm.gui\plugin.cpp:90 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:43 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:542 msgid "Readable Editor" msgstr "" #: ..\..\plugins\dm.gui\plugin.cpp:97 msgid "Reload Readable Guis" msgstr "" #: ..\..\plugins\dm.gui\plugin.cpp:107 msgid "Settings/Readable Editor" msgstr "" #: ..\..\plugins\dm.gui\plugin.cpp:111 msgid "Mod/xdata" msgstr "" #: ..\..\plugins\dm.gui\plugin.cpp:112 msgid "Mod Base/xdata" msgstr "" #: ..\..\plugins\dm.gui\plugin.cpp:113 ..\..\plugins\dm.gui\plugin.cpp:117 msgid "Custom Folder" msgstr "" #: ..\..\plugins\dm.gui\plugin.cpp:115 msgid "XData Storage Folder" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:45 msgid "" "Cannot run Readable Editor on this selection.\n" "Please select a single XData entity." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:227 msgid "Insert whole Page" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:228 msgid "Insert on left Side" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:229 msgid "Insert on right Side" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:235 msgid "Delete whole Page" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:236 msgid "Delete on left Side" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:237 msgid "Delete on right Side" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:243 msgid "Append Page" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:248 msgid "Prepend Page" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:253 msgid "Show last XData import summary" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:254 msgid "Show duplicated definitions" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:255 msgid "Show Gui import summary" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:292 msgid "" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:317 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:644 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1400 msgid "Failed to import {0}." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:319 msgid "Creating a new XData definition..." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:321 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:646 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:665 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:716 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1402 #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:62 msgid "Do you want to open the import summary?" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:323 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:648 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:667 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:718 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1404 #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:79 msgid "Import failed" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:379 msgid "" "You have imported an XData definition that is contained in a PK4, which " "can't be accessed for saving." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:381 msgid "" "Please rename your XData definition, so that it is stored under a different " "filename." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:398 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:419 msgid "Failed to open {0} for saving." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:405 msgid "" "Merging failed, because the length of the definition to be overwritten could " "not be retrieved." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:443 msgid "Mod path not defined. Using Base path..." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:456 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:474 msgid "Mod Base path not defined, neither is Mod path. Using Engine path..." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:461 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:479 msgid "Mod Base path not defined. Using Mod path..." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:505 msgid "" "{0}{1} already exists in another path.\n" "\n" "XData will be stored in {2}{3}!" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:663 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:714 msgid "Failed to load gui definition {0}." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:836 msgid "Import definition?" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:837 msgid "The definition {0} already exists. Should it be imported?" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:861 msgid "Import failed:" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:864 msgid "Consult the import summary for further information." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:887 msgid "" "To avoid duplicated XData definitions the current definition has been " "renamed to {0}." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:891 msgid "XData has been renamed." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1159 msgid "The specified gui definition is not a readable." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1164 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1176 msgid "" "The specified gui definition is not suitable for the currently chosen page-" "layout." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1186 msgid "Failure during import." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1189 msgid "The specified Definition does not exist." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1197 msgid "Not a suitable Gui Definition!" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1198 msgid "Start the Gui Browser?" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1232 msgid "Switching to default Gui..." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1233 msgid "You didn't choose a Gui. Using the default Gui now." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1250 msgid "" "No import summary available. An XData definition has to be imported first..." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1262 msgid "XData import summary" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1270 msgid "No import summary available. Browse Gui Definitions first." msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1281 msgid "Gui import summary" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1347 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1365 msgid "Please specify an XData name first!" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1554 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1580 msgid "Duplicated XData definitions" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1555 msgid "There are no duplicated definitions!" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1574 msgid "{0} has been defined in:" msgstr "" #: ..\..\plugins\dm.gui\ReadablePopulator.h:35 msgid "Analysing Guis" msgstr "" #: ..\..\plugins\dm.gui\ReadableReloader.h:30 msgid "Reloading Guis" msgstr "" #: ..\..\plugins\dm.gui\XData.cpp:257 ..\..\plugins\dm.gui\XData.cpp:285 #: ..\..\plugins\dm.gui\XData.cpp:367 ..\..\plugins\dm.gui\XData.cpp:382 msgid "Page Index out of bounds." msgstr "" #: ..\..\plugins\dm.gui\XData.h:118 ..\..\plugins\dm.gui\XData.h:125 msgid "GUI Page Index out of bounds." msgstr "" #: ..\..\plugins\dm.gui\XDataSelector.cpp:17 msgid "Choose an XData Definition..." msgstr "" #: ..\..\plugins\dm.gui\XDataSelector.cpp:48 msgid "Xdata Path" msgstr "" #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:20 msgid "Choose a file..." msgstr "" #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:60 msgid "{0} successfully imported." msgstr "" #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:64 msgid "Problems during import" msgstr "" #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:99 msgid "" "The requested definition has been found in multiple Files. Choose the file:" msgstr "" #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:104 #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:127 msgid "File" msgstr "" #: ..\..\plugins\dm.objectives\ce\AIFindBodyComponentEditor.cpp:31 msgid "Body:" msgstr "" #: ..\..\plugins\dm.objectives\ce\AIFindBodyComponentEditor.cpp:36 #: ..\..\plugins\dm.objectives\ce\AlertComponentEditor.cpp:43 #: ..\..\plugins\dm.objectives\ce\DestroyComponentEditor.cpp:37 #: ..\..\plugins\dm.objectives\ce\ItemComponentEditor.cpp:37 #: ..\..\plugins\dm.objectives\ce\KillComponentEditor.cpp:38 #: ..\..\plugins\dm.objectives\ce\KnockoutComponentEditor.cpp:38 #: ..\..\plugins\dm.objectives\ce\PickpocketComponentEditor.cpp:37 msgid "Amount:" msgstr "" #: ..\..\plugins\dm.objectives\ce\AIFindItemComponentEditor.cpp:23 #: ..\..\plugins\dm.objectives\ce\DestroyComponentEditor.cpp:31 #: ..\..\plugins\dm.objectives\ce\ItemComponentEditor.cpp:31 #: ..\..\plugins\dm.objectives\ce\PickpocketComponentEditor.cpp:31 msgid "Item:" msgstr "" #: ..\..\plugins\dm.objectives\ce\AlertComponentEditor.cpp:37 msgid "AI:" msgstr "" #: ..\..\plugins\dm.objectives\ce\AlertComponentEditor.cpp:46 msgid "Minimum Alert Level:" msgstr "" #: ..\..\plugins\dm.objectives\ce\CustomClockedComponentEditor.cpp:36 msgid "Script Function:" msgstr "" #: ..\..\plugins\dm.objectives\ce\CustomClockedComponentEditor.cpp:42 #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:68 msgid "Clock interval:" msgstr "" #: ..\..\plugins\dm.objectives\ce\CustomClockedComponentEditor.cpp:48 #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:70 msgid "seconds:" msgstr "" #: ..\..\plugins\dm.objectives\ce\CustomComponentEditor.cpp:17 msgid "" "A custom component requires no specifiers,\n" "the state of this component is manually controlled \n" "(i.e. by scripts or triggers)." msgstr "" #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:50 #: ..\..\plugins\dm.objectives\ce\InfoLocationComponentEditor.cpp:24 #: ..\..\plugins\dm.objectives\ce\LocationComponentEditor.cpp:24 msgid "Entity:" msgstr "" #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:56 msgid "Location Entity:" msgstr "" #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:62 msgid "Distance:" msgstr "" #: ..\..\plugins\dm.objectives\ce\InfoLocationComponentEditor.cpp:30 #: ..\..\plugins\dm.objectives\ce\LocationComponentEditor.cpp:30 msgid "Location:" msgstr "" #: ..\..\plugins\dm.objectives\ce\KillComponentEditor.cpp:32 msgid "Kill target:" msgstr "" #: ..\..\plugins\dm.objectives\ce\KnockoutComponentEditor.cpp:32 msgid "Knockout target:" msgstr "" #: ..\..\plugins\dm.objectives\ce\ReadableClosedComponentEditor.cpp:22 #: ..\..\plugins\dm.objectives\ce\ReadableOpenedComponentEditor.cpp:22 #: ..\..\plugins\dm.objectives\ce\ReadablePageReachedComponentEditor.cpp:31 msgid "Readable:" msgstr "" #: ..\..\plugins\dm.objectives\ce\ReadablePageReachedComponentEditor.cpp:37 msgid "Page Number:" msgstr "" #: ..\..\plugins\dm.objectives\ComponentsDialog.cpp:28 msgid "Edit Objective" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:22 msgid "AI is killed" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:27 msgid "AI is knocked out" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:32 msgid "AI finds an item" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:37 msgid "AI finds a body" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:42 msgid "AI is alerted" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:47 msgid "Object is destroyed" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:52 msgid "Player possesses item" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:57 msgid "Player pickpockets AI" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:62 msgid "Item is in location" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:67 msgid "Item is in info_location" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:72 msgid "Custom script" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:78 msgid "Custom script queried periodically" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:85 msgid "Two entities are within a radius of each other" msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:93 msgid "Readable is opened." msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:101 msgid "Readable is closed." msgstr "" #: ..\..\plugins\dm.objectives\ComponentType.cpp:109 msgid "A certain page of a readable is reached." msgstr "" #: ..\..\plugins\dm.objectives\DifficultyPanel.cpp:21 msgid "All Levels" msgstr "" #: ..\..\plugins\dm.objectives\DifficultyPanel.cpp:30 msgid "Level 1: Easy" msgstr "" #: ..\..\plugins\dm.objectives\DifficultyPanel.cpp:31 msgid "Level 2: Hard" msgstr "" #: ..\..\plugins\dm.objectives\DifficultyPanel.cpp:32 msgid "Level 3: Expert" msgstr "" #: ..\..\plugins\dm.objectives\LogicEditor.cpp:24 msgid "Success Logic:" msgstr "" #: ..\..\plugins\dm.objectives\LogicEditor.cpp:25 msgid "Failure Logic:" msgstr "" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:18 msgid "Edit Mission Logic" msgstr "" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:21 msgid "This is the standard logic for all difficulty levels" msgstr "" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:24 msgid "" "These logics override the standard logic for the given difficulty level\n" "if the logic string is non-empty." msgstr "" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:45 msgid "Default Logic" msgstr "" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:57 msgid "Difficulty-specific Logic" msgstr "" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:66 msgid "Logic for Difficulty Level {0:d}" msgstr "" #: ..\..\plugins\dm.objectives\Objective.h:73 msgid "INCOMPLETE" msgstr "" #: ..\..\plugins\dm.objectives\Objective.h:74 msgid "COMPLETE" msgstr "" #: ..\..\plugins\dm.objectives\Objective.h:75 msgid "FAILED" msgstr "" #: ..\..\plugins\dm.objectives\Objective.h:76 msgid "INVALID" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:24 #: xml_file_content.cpp:13 msgid "Edit Objective Conditions" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:145 msgid "Change Objective State" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:147 msgid "Change Visibility" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:149 msgid "Change Mandatory Flag" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:211 #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:212 #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:213 #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:214 msgid "Set state to {0}" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:225 msgid "Set Invisible" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:226 msgid "Set Visible" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:237 msgid "Clear mandatory flag" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:238 msgid "Set mandatory flag" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:463 msgid "Condition affecting objective {0:d}" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:493 msgid "If Objective {0} in Mission {1} is in state '{2}' do the following: " msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:504 msgid "Set State on Objective {0} to {1}" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:511 msgid "Make Objective {0} visible" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:515 msgid "Make Objective {0} invisible" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:522 msgid "Make Objective {0} mandatory" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:526 msgid "Make Objective {0} not mandatory" msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:537 msgid "This condition is not valid or complete yet." msgstr "" #: ..\..\plugins\dm.objectives\ObjectiveEntity.cpp:281 msgid "New objective {0:d}" msgstr "" #: ..\..\plugins\dm.objectives\objectives.cpp:67 msgid "Objectives..." msgstr "" #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:33 msgid "Mission Objectives" msgstr "" #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:97 msgid "Start" msgstr "" #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:135 msgid "Diff." msgstr "" #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:260 msgid "Exception occurred: " msgstr "" #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:437 msgid "Unable to create Objective Entity: classes not defined in registry." msgstr "" #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:466 msgid "Unable to create Objective Entity: class '{0}' not found." msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:20 msgid "entity" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:20 msgid "entities" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:43 msgid "" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:46 msgid "entity " msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:54 msgid "{0:d} loot in gold" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:57 msgid "{0:d} loot in goods" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:60 msgid "{0:d} loot in jewels" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:63 msgid "{0:d} loot" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:66 msgid "{0:d} of \"{1}\"" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:70 msgid "{0} of type {1}" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:73 msgid "{0} of spawnclass {1}" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:76 msgid "{0} AI of type {1}" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:79 msgid "{0} AI of team {1}" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:82 msgid "{0} AI of {1}" msgstr "" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:16 msgid "No specifier" msgstr "" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:20 msgid "Name of single entity" msgstr "" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:24 msgid "Overall (component-specific)" msgstr "" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:29 msgid "Group identifier (component-specific)" msgstr "" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:34 msgid "Any entity of specified class" msgstr "" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:39 msgid "Any entity with SDK-level spawnclass" msgstr "" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:44 msgid "Any AI of specified type" msgstr "" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:48 msgid "Any AI on specified team" msgstr "" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:53 msgid "Any AI with specified combat status" msgstr "" #: ..\..\plugins\dm.stimresponse\ClassEditor.cpp:62 msgid "S/R" msgstr "" #: ..\..\plugins\dm.stimresponse\ClassEditor.cpp:82 xml_file_content.cpp:7 msgid "Remove" msgstr "" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:53 #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:397 #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:412 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:405 #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:92 #: xml_file_content.cpp:181 xml_file_content.cpp:3 xml_file_content.cpp:7 #: xml_file_content.cpp:9 xml_file_content.cpp:16 xml_file_content.cpp:2 #: xml_file_content.cpp:5 xml_file_content.cpp:6 xml_file_content.cpp:18 msgid "Delete" msgstr "" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:106 #: xml_file_content.cpp:2 msgid "Name:" msgstr "" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:117 msgid "" "Note: Please beware that deleting custom stims may\n" "affect other entities as well. So check before you delete." msgstr "" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:129 msgid "Add Stim Type" msgstr "" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:130 msgid "Remove Stim Type" msgstr "" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:185 #: ..\..\plugins\dm.stimresponse\StimTypes.cpp:294 msgid "Custom Stim" msgstr "" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:209 msgid "Delete Custom Stim" msgstr "" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:210 msgid "" "Beware that other entities might still be using this stim type.\n" "Do you really want to delete this custom stim?" msgstr "" #: ..\..\plugins\dm.stimresponse\EffectEditor.cpp:22 msgid "Edit Response Effect" msgstr "" #: ..\..\plugins\dm.stimresponse\EffectEditor.cpp:108 msgid "Effect:" msgstr "" #: ..\..\plugins\dm.stimresponse\EffectEditor.cpp:115 xml_file_content.cpp:2 msgid "Active" msgstr "" #: ..\..\plugins\dm.stimresponse\EffectEditor.cpp:120 msgid "Arguments" msgstr "" #: ..\..\plugins\dm.stimresponse\plugin.cpp:59 msgid "Stim/Response..." msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:231 msgid "Effect" msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:234 msgid "Details (double-click to edit)" msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:391 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:399 msgid "Activate" msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:393 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:401 msgid "Deactivate" msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:395 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:403 xml_file_content.cpp:11 msgid "Duplicate" msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:402 msgid "Add new Effect" msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:404 xml_file_content.cpp:6 #: xml_file_content.cpp:13 xml_file_content.cpp:4 xml_file_content.cpp:8 msgid "Edit" msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:407 xml_file_content.cpp:4 msgid "Move Up" msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:409 xml_file_content.cpp:5 msgid "Move Down" msgstr "" #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:195 msgid "Error: eclass pointer invalid." msgstr "" #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:26 msgid "Stim/Response Editor" msgstr "" #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:31 msgid "A single entity must be selected to edit Stim/Response properties." msgstr "" #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:97 msgid "Stims" msgstr "" #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:104 msgid "Responses" msgstr "" #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:111 msgid "Custom Stims" msgstr "" #: ..\..\plugins\eclassmgr\EClassManager.cpp:335 #: ..\..\plugins\eclassmgr\EClassManager.cpp:336 msgid "Reloading Defs" msgstr "" #: ..\..\plugins\eclasstree\EClassTree.cpp:23 msgid "Entity Class Tree" msgstr "" #: ..\..\plugins\eclasstree\plugin.cpp:51 msgid "Entity Class Tree..." msgstr "" #: ..\..\plugins\entity\EntityCreator.cpp:38 msgid "createNodeForEntity(): cannot create entity for NULL entityclass." msgstr "" #: ..\..\plugins\entitylist\EntityList.cpp:23 xml_file_content.cpp:60 msgid "Entity List" msgstr "" #: ..\..\plugins\entitylist\EntityList.cpp:86 msgid "Focus camera on selected entity" msgstr "" #: ..\..\plugins\entitylist\EntityList.cpp:87 msgid "List visible nodes only" msgstr "" #: ..\..\plugins\eventmanager\MouseToolGroup.cpp:23 msgid "XY View" msgstr "" #: ..\..\plugins\eventmanager\MouseToolGroup.cpp:25 xml_file_content.cpp:50 msgid "Camera View" msgstr "" #: ..\..\plugins\eventmanager\MouseToolGroup.cpp:27 msgid "Unknown" msgstr "" #: ..\..\plugins\filetypes\FileTypeRegistry.cpp:12 msgid "All Files" msgstr "" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:49 #: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:44 msgid "" "Failed parsing entity {0:d}:\n" "{1}" msgstr "" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:94 #: ..\..\plugins\mapdoom3\Quake4MapReader.cpp:47 msgid "Unable to parse map version (parse exception)." msgstr "" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:102 #: ..\..\plugins\mapdoom3\Quake4MapReader.cpp:54 msgid "Could not recognise map version number format." msgstr "" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:110 #: ..\..\plugins\mapdoom3\Quake4MapReader.cpp:62 msgid "Incorrect map version: required {0:f}, found {1:f}" msgstr "" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:143 #: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:101 msgid "Primitive #{0:d}: parse error" msgstr "" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:153 #: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:111 msgid "Primitive #{0:d}: parse exception {1}" msgstr "" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:244 #: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:202 msgid "Parsed invalid value '{0}' for key '{1}'" msgstr "" #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:125 #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:234 msgid "BrushDefParser: invalid token '{0}'" msgstr "" #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef3.cpp:115 msgid "BrushDef3Parser: invalid token '{0}'" msgstr "" #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef3.cpp:186 msgid "BrushDef3ParserQuake4: invalid token '{0}'" msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:44 msgid "Particle Editor" msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:208 msgid "Stage" msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1034 msgid "Note: changes will be written to the file {0}" msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1341 msgid "Save Changes" msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1342 msgid "" "Do you want to save the changes\n" "you made to the particle {0}?" msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1375 msgid "" "Error saving particle definition:\n" "{0}" msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1505 msgid "Select .prt file" msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1539 msgid "Enter Particle Name" msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1553 msgid "Cannot create particle with an empty name." msgstr "" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1568 msgid "This name is already in use." msgstr "" #: ..\..\plugins\particles\editor\ParticleEditorModule.h:56 msgid "Particle Editor..." msgstr "" #: ..\..\plugins\particles\ParticlesManager.cpp:284 msgid "Cannot save particle, it has not been registered yet." msgstr "" #: ..\..\plugins\particles\ParticlesManager.cpp:364 msgid "Cannot open file for reading: {0}" msgstr "" #: ..\..\plugins\particles\ParticlesManager.cpp:411 msgid "Could not remove the file {0}" msgstr "" #: ..\..\plugins\particles\ParticlesManager.cpp:425 msgid "Could not rename the temporary file {0}" msgstr "" #: ..\..\plugins\script\ScriptingSystem.cpp:239 #: ..\..\plugins\script\ScriptingSystem.cpp:242 msgid "Script" msgstr "" #: ..\..\plugins\script\ScriptingSystem.cpp:483 msgid "Reload Scripts" msgstr "" #: ..\..\plugins\script\ScriptMenu.cpp:60 msgid "No scripts available" msgstr "" #: ..\..\plugins\script\ScriptWindow.cpp:37 msgid "Python Script Input" msgstr "" #: ..\..\plugins\script\ScriptWindow.cpp:39 msgid "Run Script" msgstr "" #: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:323 msgid "Loading Shaders" msgstr "" #: ..\..\plugins\shaders\ShaderFileLoader.cpp:95 msgid "Parsing material file {0}" msgstr "" #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:16 #: xml_file_content.cpp:141 msgid "MD5 Animation Viewer" msgstr "" #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:83 #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:103 msgid "Model Definition" msgstr "" #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:86 msgid "Available Animations" msgstr "" #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:125 msgid "Animation" msgstr "" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:24 msgid "Edit Colour Schemes" msgstr "" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:81 #: xml_file_content.cpp:5 msgid "Colour" msgstr "" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:93 #: xml_file_content.cpp:4 msgid "Copy" msgstr "" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:268 msgid "Copy Colour Scheme" msgstr "" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:268 msgid "Enter a name for the new scheme:" msgstr "" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:278 msgid "A Scheme with that name already exists." msgstr "" #: ..\..\plugins\uimanager\SoundChooser.cpp:140 msgid "Choose sound" msgstr "" #: ..\..\plugins\uimanager\SoundChooser.cpp:170 msgid "Soundshader" msgstr "" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:28 msgid "Sound Files" msgstr "" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:57 msgid "Play" msgstr "" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:60 msgid "Play and loop" msgstr "" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:63 msgid "Stop" msgstr "" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:209 msgid "Error: File not found." msgstr "" #: ..\..\plugins\uimanager\UIManager.cpp:119 msgid "Describes available Mouse Commands" msgstr "" #: ..\..\plugins\undo\UndoSystem.cpp:379 msgid "Settings/Undo System" msgstr "" #: ..\..\plugins\undo\UndoSystem.cpp:380 msgid "Undo Queue Size" msgstr "" #: ..\..\libs\wxutil\dialog\MessageBox.cpp:20 xml_file_content.cpp:8 #: xml_file_content.cpp:10 xml_file_content.cpp:13 xml_file_content.cpp:4 #: xml_file_content.cpp:3 xml_file_content.cpp:21 msgid "Save" msgstr "" #: ..\..\libs\wxutil\dialog\MessageBox.cpp:20 msgid "Close without saving" msgstr "" #: ..\..\libs\wxutil\FileChooser.cpp:58 msgid "Open File" msgstr "" #: ..\..\libs\wxutil\FileChooser.cpp:58 msgid "Save File" msgstr "" #: ..\..\libs\wxutil\FileChooser.cpp:121 msgid "All Files (*.*)" msgstr "" #: ..\..\libs\wxutil\ModalProgressDialog.cpp:20 #: ..\..\libs\wxutil\ModalProgressDialog.cpp:30 msgid "Operation cancelled by user" msgstr "" #: ..\..\libs\wxutil\Modifier.h:146 msgid "Alt" msgstr "" #: ..\..\libs\wxutil\Modifier.h:147 msgid "Ctrl" msgstr "" #: ..\..\libs\wxutil\Modifier.h:148 msgid "Shift" msgstr "" #: ..\..\libs\wxutil\PathEntry.cpp:97 msgid "Choose File" msgstr "" #: ..\..\libs\wxutil\PathEntry.cpp:118 msgid "Choose Directory" msgstr "" #: ..\..\libs\wxutil\preview\ModelPreview.cpp:154 #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:182 msgid "" "Unable to setup the preview,\n" "could not find the entity class {0}" msgstr "" #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:51 msgid "Show coordinate axes" msgstr "" #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:57 msgid "Show wireframe" msgstr "" #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:61 #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:63 msgid "Auto Loop" msgstr "" #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:67 msgid "Reload Particle Defs" msgstr "" #: ..\..\libs\wxutil\preview\RenderPreview.cpp:102 #: ..\..\libs\wxutil\preview\RenderPreview.cpp:104 xml_file_content.cpp:1 msgid "Filters" msgstr "" #: xml_file_content.cpp:1 msgid "&File" msgstr "" #: xml_file_content.cpp:2 msgid "&New Map" msgstr "" #: xml_file_content.cpp:3 msgid "&Open..." msgstr "" #: xml_file_content.cpp:4 msgid "&Import map..." msgstr "" #: xml_file_content.cpp:5 msgid "Import &prefab..." msgstr "" #: xml_file_content.cpp:6 msgid "&Save" msgstr "" #: xml_file_content.cpp:7 msgid "Save &as..." msgstr "" #: xml_file_content.cpp:8 msgid "Save © as..." msgstr "" #: xml_file_content.cpp:9 msgid "&Save selected as Map..." msgstr "" #: xml_file_content.cpp:10 msgid "&Save selected as prefab..." msgstr "" #: xml_file_content.cpp:11 msgid "&Export selected as model..." msgstr "" #: xml_file_content.cpp:12 msgid "Save selected as Collision Model..." msgstr "" #: xml_file_content.cpp:13 msgid "Save ®ion..." msgstr "" #: xml_file_content.cpp:14 msgid "&Reload Models" msgstr "" #: xml_file_content.cpp:15 msgid "Reload Selected Models" msgstr "" #: xml_file_content.cpp:16 msgid "Reload S&kins" msgstr "" #: xml_file_content.cpp:17 xml_file_content.cpp:28 msgid "Reload Materials" msgstr "" #: xml_file_content.cpp:18 msgid "Reload Defs" msgstr "" #: xml_file_content.cpp:19 msgid "Reload Particles" msgstr "" #: xml_file_content.cpp:20 msgid "&Game/Project Setup..." msgstr "" #: xml_file_content.cpp:21 msgid "&Pointfile" msgstr "" #: xml_file_content.cpp:22 msgid "E&xit" msgstr "" #: xml_file_content.cpp:23 msgid "&Edit" msgstr "" #: xml_file_content.cpp:24 msgid "&Undo" msgstr "" #: xml_file_content.cpp:25 msgid "&Redo" msgstr "" #: xml_file_content.cpp:26 msgid "&Copy" msgstr "" #: xml_file_content.cpp:27 msgid "&Paste" msgstr "" #: xml_file_content.cpp:28 msgid "Paste to Camera" msgstr "" #: xml_file_content.cpp:29 msgid "&Duplicate" msgstr "" #: xml_file_content.cpp:30 msgid "D&elete" msgstr "" #: xml_file_content.cpp:31 msgid "Rep&arent Primitives" msgstr "" #: xml_file_content.cpp:32 msgid "Reparent Primitives to &Worldspawn" msgstr "" #: xml_file_content.cpp:33 msgid "Merge selected Entities" msgstr "" #: xml_file_content.cpp:34 msgid "Copy Shader" msgstr "" #: xml_file_content.cpp:35 msgid "Paste Shader" msgstr "" #: xml_file_content.cpp:36 msgid "Paste Shader (Natural)" msgstr "" #: xml_file_content.cpp:37 msgid "C&lear selection" msgstr "" #: xml_file_content.cpp:38 msgid "&Invert selection" msgstr "" #: xml_file_content.cpp:39 msgid "Select complete t&all" msgstr "" #: xml_file_content.cpp:40 msgid "Select i&nside" msgstr "" #: xml_file_content.cpp:41 msgid "Select &touching" msgstr "" #: xml_file_content.cpp:42 msgid "Select Children" msgstr "" #: xml_file_content.cpp:43 msgid "Expand selection to all Siblings" msgstr "" #: xml_file_content.cpp:44 msgid "Select all of type" msgstr "" #: xml_file_content.cpp:45 msgid "Keyboard Shortcuts..." msgstr "" #: xml_file_content.cpp:46 msgid "Mouse Bindings..." msgstr "" #: xml_file_content.cpp:47 msgid "Pre&ferences..." msgstr "" #: xml_file_content.cpp:48 msgid "V&iew" msgstr "" #: xml_file_content.cpp:49 msgid "New XY view" msgstr "" #: xml_file_content.cpp:52 msgid "Console View" msgstr "" #: xml_file_content.cpp:54 msgid "Media Browser" msgstr "" #: xml_file_content.cpp:55 msgid "Entity Inspector" msgstr "" #: xml_file_content.cpp:56 msgid "&Light Inspector" msgstr "" #: xml_file_content.cpp:57 msgid "&Surface Inspector" msgstr "" #: xml_file_content.cpp:58 msgid "&Patch Inspector" msgstr "" #: xml_file_content.cpp:59 msgid "&Texture Tool" msgstr "" #: xml_file_content.cpp:61 msgid "AAS Area Viewer" msgstr "" #: xml_file_content.cpp:63 msgid "&Center" msgstr "" #: xml_file_content.cpp:64 msgid "&Up Floor" msgstr "" #: xml_file_content.cpp:65 msgid "&Down Floor" msgstr "" #: xml_file_content.cpp:66 msgid "Far Clip Plane In" msgstr "" #: xml_file_content.cpp:67 msgid "Far Clip Plane Out" msgstr "" #: xml_file_content.cpp:68 msgid "Next leak spot" msgstr "" #: xml_file_content.cpp:69 msgid "Previous leak spot" msgstr "" #: xml_file_content.cpp:70 msgid "Orthographic" msgstr "" #: xml_file_content.cpp:71 msgid "Next (XY, XZ, YZ)" msgstr "" #: xml_file_content.cpp:72 msgid "XY (Top)" msgstr "" #: xml_file_content.cpp:73 msgid "YZ" msgstr "" #: xml_file_content.cpp:74 msgid "XZ" msgstr "" #: xml_file_content.cpp:75 msgid "&XY 100%" msgstr "" #: xml_file_content.cpp:76 msgid "&XY Zoom In" msgstr "" #: xml_file_content.cpp:77 msgid "&XY Zoom Out" msgstr "" #: xml_file_content.cpp:78 msgid "Show" msgstr "" #: xml_file_content.cpp:79 msgid "Show &Angles" msgstr "" #: xml_file_content.cpp:80 msgid "Show &Names" msgstr "" #: xml_file_content.cpp:82 msgid "Show C&oordinates" msgstr "" #: xml_file_content.cpp:86 msgid "Show size info" msgstr "" #: xml_file_content.cpp:87 msgid "Hide/Show" msgstr "" #: xml_file_content.cpp:88 msgid "Hide Selected" msgstr "" #: xml_file_content.cpp:89 msgid "Hide Deselected" msgstr "" #: xml_file_content.cpp:90 msgid "Show hidden" msgstr "" #: xml_file_content.cpp:92 msgid "&Switch off" msgstr "" #: xml_file_content.cpp:93 msgid "Set from &XY view" msgstr "" #: xml_file_content.cpp:94 msgid "Set from &Brush" msgstr "" #: xml_file_content.cpp:95 msgid "Set from Se&lection" msgstr "" #: xml_file_content.cpp:96 msgid "Colours..." msgstr "" #: xml_file_content.cpp:97 msgid "Background Image..." msgstr "" #: xml_file_content.cpp:98 msgid "Mo&dify" msgstr "" #: xml_file_content.cpp:99 xml_file_content.cpp:16 msgid "Components" msgstr "" #: xml_file_content.cpp:100 msgid "&Edges" msgstr "" #: xml_file_content.cpp:101 msgid "&Vertices" msgstr "" #: xml_file_content.cpp:102 msgid "&Faces" msgstr "" #: xml_file_content.cpp:103 msgid "En&tities" msgstr "" #: xml_file_content.cpp:104 msgid "Nudge" msgstr "" #: xml_file_content.cpp:105 msgid "Nudge Left" msgstr "" #: xml_file_content.cpp:106 msgid "Nudge Right" msgstr "" #: xml_file_content.cpp:107 msgid "Nudge Up" msgstr "" #: xml_file_content.cpp:108 msgid "Nudge Down" msgstr "" #: xml_file_content.cpp:109 xml_file_content.cpp:16 msgid "Rotate" msgstr "" #: xml_file_content.cpp:110 msgid "Rotate X" msgstr "" #: xml_file_content.cpp:111 msgid "Rotate Y" msgstr "" #: xml_file_content.cpp:112 msgid "Rotate Z" msgstr "" #: xml_file_content.cpp:113 msgid "Mirror" msgstr "" #: xml_file_content.cpp:114 msgid "Mirror &X" msgstr "" #: xml_file_content.cpp:115 msgid "Mirror &Y" msgstr "" #: xml_file_content.cpp:116 msgid "Mirror &Z" msgstr "" #: xml_file_content.cpp:117 xml_file_content.cpp:10 msgid "Rotate Objects independently" msgstr "" #: xml_file_content.cpp:118 msgid "Rotate and scale..." msgstr "" #: xml_file_content.cpp:119 msgid "&Grid" msgstr "" #: xml_file_content.cpp:120 msgid "Snap selected to grid" msgstr "" #: xml_file_content.cpp:121 msgid "Grid0.125" msgstr "" #: xml_file_content.cpp:122 msgid "Grid0.25" msgstr "" #: xml_file_content.cpp:123 msgid "Grid0.5" msgstr "" #: xml_file_content.cpp:124 msgid "Grid1" msgstr "" #: xml_file_content.cpp:125 msgid "Grid2" msgstr "" #: xml_file_content.cpp:126 msgid "Grid4" msgstr "" #: xml_file_content.cpp:127 msgid "Grid8" msgstr "" #: xml_file_content.cpp:128 msgid "Grid16" msgstr "" #: xml_file_content.cpp:129 msgid "Grid32" msgstr "" #: xml_file_content.cpp:130 msgid "Grid64" msgstr "" #: xml_file_content.cpp:131 msgid "Grid128" msgstr "" #: xml_file_content.cpp:132 msgid "Grid256" msgstr "" #: xml_file_content.cpp:133 msgid "M&ap" msgstr "" #: xml_file_content.cpp:134 msgid "Find brush..." msgstr "" #: xml_file_content.cpp:135 msgid "Find and replace textures..." msgstr "" #: xml_file_content.cpp:136 msgid "Map info..." msgstr "" #: xml_file_content.cpp:137 msgid "E&ntity" msgstr "" #: xml_file_content.cpp:138 msgid "&Revert group to worldspawn" msgstr "" #: xml_file_content.cpp:139 msgid "&Connect selected entities" msgstr "" #: xml_file_content.cpp:140 msgid "&Bind selected entities" msgstr "" #: xml_file_content.cpp:142 msgid "B&rush" msgstr "" #: xml_file_content.cpp:143 msgid "Prism..." msgstr "" #: xml_file_content.cpp:144 msgid "Cone..." msgstr "" #: xml_file_content.cpp:145 msgid "Sphere..." msgstr "" #: xml_file_content.cpp:146 msgid "CSG" msgstr "" #: xml_file_content.cpp:147 msgid "Make &Hollow" msgstr "" #: xml_file_content.cpp:148 msgid "Make &Room" msgstr "" #: xml_file_content.cpp:149 msgid "CSG &Subtract" msgstr "" #: xml_file_content.cpp:150 msgid "CSG &Merge" msgstr "" #: xml_file_content.cpp:152 msgid "Clip Selection" msgstr "" #: xml_file_content.cpp:153 msgid "Split Selection" msgstr "" #: xml_file_content.cpp:154 msgid "Flip Clip Orientation" msgstr "" #: xml_file_content.cpp:155 msgid "Texture lock" msgstr "" #: xml_file_content.cpp:156 msgid "Create Decal Patches" msgstr "" #: xml_file_content.cpp:158 msgid "Make Detail" msgstr "" #: xml_file_content.cpp:159 msgid "Make Structural" msgstr "" #: xml_file_content.cpp:160 msgid "&Patch" msgstr "" #: xml_file_content.cpp:161 msgid "Create Simple Patch Mesh" msgstr "" #: xml_file_content.cpp:162 msgid "Create End cap" msgstr "" #: xml_file_content.cpp:163 msgid "Create Bevel" msgstr "" #: xml_file_content.cpp:164 msgid "Create Cone" msgstr "" #: xml_file_content.cpp:165 msgid "Create Cylinder" msgstr "" #: xml_file_content.cpp:166 msgid "Create Sphere" msgstr "" #: xml_file_content.cpp:167 msgid "More cylinders" msgstr "" #: xml_file_content.cpp:168 msgid "Create Dense Cylinder" msgstr "" #: xml_file_content.cpp:169 msgid "Create Very Dense Cylinder" msgstr "" #: xml_file_content.cpp:170 msgid "Create Square Cylinder" msgstr "" #: xml_file_content.cpp:171 msgid "Insert" msgstr "" #: xml_file_content.cpp:172 msgid "Insert 2 Columns at the beginning" msgstr "" #: xml_file_content.cpp:173 msgid "Insert 2 Columns at the end" msgstr "" #: xml_file_content.cpp:174 msgid "Insert 2 Rows at the beginning" msgstr "" #: xml_file_content.cpp:175 msgid "Insert 2 Rows at the end" msgstr "" #: xml_file_content.cpp:176 msgid "Append" msgstr "" #: xml_file_content.cpp:177 msgid "Append 2 columns at the beginning" msgstr "" #: xml_file_content.cpp:178 msgid "Append 2 columns at the end" msgstr "" #: xml_file_content.cpp:179 msgid "Append 2 rows at the beginning" msgstr "" #: xml_file_content.cpp:180 msgid "Append 2 rows at the end" msgstr "" #: xml_file_content.cpp:182 msgid "Delete 2 columns from the beginning" msgstr "" #: xml_file_content.cpp:183 msgid "Delete 2 columns from the end" msgstr "" #: xml_file_content.cpp:184 msgid "Delete 2 rows from the beginning" msgstr "" #: xml_file_content.cpp:185 msgid "Delete 2 rows from the end" msgstr "" #: xml_file_content.cpp:186 msgid "Matrix" msgstr "" #: xml_file_content.cpp:187 msgid "Invert" msgstr "" #: xml_file_content.cpp:188 msgid "Re-disperse" msgstr "" #: xml_file_content.cpp:189 msgid "Rows" msgstr "" #: xml_file_content.cpp:190 msgid "Columns" msgstr "" #: xml_file_content.cpp:191 msgid "Transpose" msgstr "" #: xml_file_content.cpp:192 msgid "Thicken Selected Patches" msgstr "" #: xml_file_content.cpp:193 msgid "Cap Selection" msgstr "" #: xml_file_content.cpp:194 msgid "Stitch Patch Textures" msgstr "" #: xml_file_content.cpp:195 msgid "Bulge patch" msgstr "" #: xml_file_content.cpp:196 msgid "&Curve" msgstr "" #: xml_file_content.cpp:197 msgid "Create NURBS Curve" msgstr "" #: xml_file_content.cpp:198 msgid "Create CatmullRom Curve" msgstr "" #: xml_file_content.cpp:199 msgid "Convert NURBS <-> CatmullRom" msgstr "" #: xml_file_content.cpp:200 msgid "Append Curve Control Point" msgstr "" #: xml_file_content.cpp:201 msgid "Insert Curve Control Points" msgstr "" #: xml_file_content.cpp:202 msgid "Remove Curve Control Points" msgstr "" #: xml_file_content.cpp:203 msgid "&Help" msgstr "" #: xml_file_content.cpp:204 msgid "&About" msgstr "" #: xml_file_content.cpp:1 msgid "Texture Background" msgstr "" #: xml_file_content.cpp:2 msgid "Grid Background" msgstr "" #: xml_file_content.cpp:3 msgid "Grid Major" msgstr "" #: xml_file_content.cpp:4 msgid "Grid Minor" msgstr "" #: xml_file_content.cpp:5 msgid "Grid Text" msgstr "" #: xml_file_content.cpp:6 msgid "Grid Block" msgstr "" #: xml_file_content.cpp:7 msgid "Brushes" msgstr "" #: xml_file_content.cpp:8 msgid "Entities (default)" msgstr "" #: xml_file_content.cpp:9 msgid "Camera Background" msgstr "" #: xml_file_content.cpp:10 msgid "Selected Items" msgstr "" #: xml_file_content.cpp:11 msgid "Selected Group Items" msgstr "" #: xml_file_content.cpp:12 msgid "Selected Items (Camera)" msgstr "" #: xml_file_content.cpp:14 msgid "Active View Name" msgstr "" #: xml_file_content.cpp:15 msgid "X-Axis" msgstr "" #: xml_file_content.cpp:16 msgid "Y-Axis" msgstr "" #: xml_file_content.cpp:17 msgid "Z-Axis" msgstr "" #: xml_file_content.cpp:18 msgid "Workzone" msgstr "" #: xml_file_content.cpp:19 msgid "Camera Icon" msgstr "" #: xml_file_content.cpp:20 msgid "Brush Size Info" msgstr "" #: xml_file_content.cpp:21 msgid "Light Volumes" msgstr "" #: xml_file_content.cpp:22 msgid "Brush Vertices" msgstr "" #: xml_file_content.cpp:23 msgid "Patch Inner Vertex" msgstr "" #: xml_file_content.cpp:24 msgid "Patch Corner Vertex" msgstr "" #: xml_file_content.cpp:25 msgid "Light Vertices (deselected)" msgstr "" #: xml_file_content.cpp:26 msgid "Light Vertices (selected)" msgstr "" #: xml_file_content.cpp:27 msgid "Light Vertices (normal)" msgstr "" #: xml_file_content.cpp:28 msgid "Light Start/End (selected)" msgstr "" #: xml_file_content.cpp:29 msgid "Light Start/End (deselected)" msgstr "" #: xml_file_content.cpp:30 msgid "XYView Crosshairs" msgstr "" #: xml_file_content.cpp:31 msgid "Drag-Selection Box" msgstr "" #: xml_file_content.cpp:1 msgid "Open a map file" msgstr "" #: xml_file_content.cpp:2 msgid "Save the current map" msgstr "" #: xml_file_content.cpp:3 msgid "Change views" msgstr "" #: xml_file_content.cpp:4 msgid "Select complete tall" msgstr "" #: xml_file_content.cpp:5 msgid "Select touching" msgstr "" #: xml_file_content.cpp:6 msgid "Select inside" msgstr "" #: xml_file_content.cpp:7 msgid "Create Decals for selected Faces" msgstr "" #: xml_file_content.cpp:8 msgid "x-axis Mirror" msgstr "" #: xml_file_content.cpp:9 msgid "x-axis Rotate" msgstr "" #: xml_file_content.cpp:10 msgid "y-axis Mirror" msgstr "" #: xml_file_content.cpp:11 msgid "y-axis Rotate" msgstr "" #: xml_file_content.cpp:12 msgid "z-axis Mirror" msgstr "" #: xml_file_content.cpp:13 msgid "z-axis Rotate" msgstr "" #: xml_file_content.cpp:14 msgid "Snap selection to grid" msgstr "" #: xml_file_content.cpp:15 msgid "Floor selection" msgstr "" #: xml_file_content.cpp:16 msgid "CSG Subtract" msgstr "" #: xml_file_content.cpp:17 msgid "CSG Merge" msgstr "" #: xml_file_content.cpp:18 msgid "Hollow" msgstr "" #: xml_file_content.cpp:19 msgid "Make Room" msgstr "" #: xml_file_content.cpp:20 msgid "Group selected items" msgstr "" #: xml_file_content.cpp:21 msgid "Ungroup selected items" msgstr "" #: xml_file_content.cpp:22 msgid "Put caps on the current patch" msgstr "" #: xml_file_content.cpp:23 msgid "Creates a NURBS curve" msgstr "" #: xml_file_content.cpp:24 msgid "Convert the selected curve (NURBS <-> CatmullRom)" msgstr "" #: xml_file_content.cpp:25 msgid "Appends a control point to the selected curves" msgstr "" #: xml_file_content.cpp:26 msgid "Inserts a curve control point before the selected ones" msgstr "" #: xml_file_content.cpp:27 msgid "Removes the selected curve control points" msgstr "" #: xml_file_content.cpp:29 msgid "Find & Replace" msgstr "" #: xml_file_content.cpp:30 msgid "Decrease Grid Size" msgstr "" #: xml_file_content.cpp:31 msgid "Increase Grid Size" msgstr "" #: xml_file_content.cpp:32 msgid "Snap to Grid" msgstr "" #: xml_file_content.cpp:33 msgid "Merge Selection" msgstr "" #: xml_file_content.cpp:34 msgid "Flip Selection Horiz (S-Axis)" msgstr "" #: xml_file_content.cpp:35 msgid "Flip Selection Vertical (T-Axis)" msgstr "" #: xml_file_content.cpp:36 msgid "Select Related Items" msgstr "" #: xml_file_content.cpp:1 msgid "Cubic clip the camera view" msgstr "" #: xml_file_content.cpp:2 msgid "Select Vertices" msgstr "" #: xml_file_content.cpp:3 msgid "Select Edges" msgstr "" #: xml_file_content.cpp:5 msgid "Select Entities" msgstr "" #: xml_file_content.cpp:6 msgid "Select Group Parts" msgstr "" #: xml_file_content.cpp:7 msgid "Show/hide all light volumes" msgstr "" #: xml_file_content.cpp:8 msgid "Show/hide all speaker volumes" msgstr "" #: xml_file_content.cpp:11 msgid "Rotate func_* Entities around origin" msgstr "" #: xml_file_content.cpp:12 msgid "Snap Rotation Pivot to Grid" msgstr "" #: xml_file_content.cpp:13 msgid "Drag-resize entities symmetrically (leave origin unchanged)" msgstr "" #: xml_file_content.cpp:14 msgid "Offset cloned objects by one grid unit to the right and downwards." msgstr "" #: xml_file_content.cpp:15 msgid "Translate" msgstr "" #: xml_file_content.cpp:17 msgid "Resize" msgstr "" #: xml_file_content.cpp:19 msgid "Model Scaler" msgstr "" #: xml_file_content.cpp:20 msgid "Hide Unused" msgstr "" #: xml_file_content.cpp:21 msgid "Toggle Grid" msgstr "" #: xml_file_content.cpp:22 msgid "Center Pivot when scaling faces" msgstr "" #: xml_file_content.cpp:1 msgid "DarkRadiant x.y.z" msgstr "" #: xml_file_content.cpp:2 msgid "Build date: 2014-08-04" msgstr "" #: xml_file_content.cpp:3 msgid "" "http://www.darkradiant.net/\n" "\n" "This product contains software technology\n" "from id Software, Inc. ('id Technology').\n" "id Technology 2000 id Software,Inc.\n" "DarkRadiant is originally based \n" "on the GPL version of GtkRadiant.\n" "\n" "The Dark Mod (www.thedarkmod.com)" msgstr "" #: xml_file_content.cpp:4 msgid "wxWidgets Properties" msgstr "" #: xml_file_content.cpp:5 xml_file_content.cpp:8 msgid "Version:" msgstr "" #: xml_file_content.cpp:6 msgid "OpenGL Properties" msgstr "" #: xml_file_content.cpp:7 msgid "Vendor:" msgstr "" #: xml_file_content.cpp:9 msgid "Renderer:" msgstr "" #: xml_file_content.cpp:10 msgid "OpenGL Extensions" msgstr "" #: xml_file_content.cpp:11 msgid "DarkRadiant Modules" msgstr "" #: xml_file_content.cpp:1 msgid "wireframeBtn" msgstr "" #: xml_file_content.cpp:2 msgid "flatShadeBtn" msgstr "" #: xml_file_content.cpp:3 msgid "texturedBtn" msgstr "" #: xml_file_content.cpp:4 msgid "lightingBtn" msgstr "" #: xml_file_content.cpp:5 xml_file_content.cpp:1 msgid "startTimeButton" msgstr "" #: xml_file_content.cpp:6 xml_file_content.cpp:3 msgid "stopTimeButton" msgstr "" #: xml_file_content.cpp:7 msgid "clipPlaneInButton" msgstr "" #: xml_file_content.cpp:8 msgid "clipPlaneOutButton" msgstr "" #: xml_file_content.cpp:3 msgid "Command Arguments" msgstr "" #: xml_file_content.cpp:4 msgid "Command Properties" msgstr "" #: xml_file_content.cpp:5 msgid "Wait until finished" msgstr "" #: xml_file_content.cpp:1 msgid "Conversation Entities" msgstr "" #: xml_file_content.cpp:4 msgid "Conversations" msgstr "" #: xml_file_content.cpp:8 xml_file_content.cpp:7 msgid "Clear" msgstr "" #: xml_file_content.cpp:1 msgid "Properties" msgstr "" #: xml_file_content.cpp:3 msgid "Actors must be within talk distance" msgstr "" #: xml_file_content.cpp:4 msgid "Actors always face each other while talking" msgstr "" #: xml_file_content.cpp:5 msgid "Let this conversation play" msgstr "" #: xml_file_content.cpp:6 msgid "times at maximum" msgstr "" #: xml_file_content.cpp:7 msgid "Actors" msgstr "" #: xml_file_content.cpp:10 msgid "Validate all" msgstr "" #: xml_file_content.cpp:11 msgid "Commands" msgstr "" #: xml_file_content.cpp:14 xml_file_content.cpp:9 msgid "Move up" msgstr "" #: xml_file_content.cpp:15 xml_file_content.cpp:10 msgid "Move down" msgstr "" #: xml_file_content.cpp:3 msgid "Refresh" msgstr "" #: xml_file_content.cpp:5 msgid "Classname:" msgstr "" #: xml_file_content.cpp:6 msgid "Spawnarg:" msgstr "" #: xml_file_content.cpp:7 msgid "Argument:" msgstr "" #: xml_file_content.cpp:1 msgid "Output File" msgstr "" #: xml_file_content.cpp:2 msgid "Output Format:" msgstr "" #: xml_file_content.cpp:3 msgid "File Path:" msgstr "" #: xml_file_content.cpp:4 msgid "Export Options" msgstr "" #: xml_file_content.cpp:5 msgid "Center Objects around Origin" msgstr "" #: xml_file_content.cpp:6 msgid "Skip Surfaces textured with Caulk" msgstr "" #: xml_file_content.cpp:7 msgid "Replace Selection with exported Model" msgstr "" #: xml_file_content.cpp:8 msgid "Use Entity Origin as export Origin" msgstr "" #: xml_file_content.cpp:10 msgid "Export" msgstr "" #: xml_file_content.cpp:3 xml_file_content.cpp:68 msgid "View" msgstr "" #: xml_file_content.cpp:2 msgid "Rules" msgstr "" #: xml_file_content.cpp:7 msgid "" "Filter rules are applied in the shown order.\n" "\"Match\" is accepting regular expressions.\n" "\"Object\"-type filters can be used to match \"patch\" or \"brush\"." msgstr "" #: xml_file_content.cpp:1 msgid "Find:" msgstr "" #: xml_file_content.cpp:2 msgid "Replace:" msgstr "" #: xml_file_content.cpp:3 msgid "Search current selection only" msgstr "" #: xml_file_content.cpp:4 msgid "x shader(s) replaced." msgstr "" #: xml_file_content.cpp:5 msgid "Find and Replace" msgstr "" #: xml_file_content.cpp:6 xml_file_content.cpp:13 xml_file_content.cpp:88 msgid "Close" msgstr "" #: xml_file_content.cpp:1 msgid "Light Volume" msgstr "" #: xml_file_content.cpp:2 msgid "Omni" msgstr "" #: xml_file_content.cpp:3 msgid "Projected" msgstr "" #: xml_file_content.cpp:4 msgid "Use start/end" msgstr "" #: xml_file_content.cpp:6 xml_file_content.cpp:9 msgid "Options" msgstr "" #: xml_file_content.cpp:7 msgid "Parallel" msgstr "" #: xml_file_content.cpp:8 msgid "Do not cast shadows (fast)" msgstr "" #: xml_file_content.cpp:9 msgid "Skip specular lighting" msgstr "" #: xml_file_content.cpp:10 msgid "Skip diffuse lighting" msgstr "" #: xml_file_content.cpp:11 msgid "Light Texture" msgstr "" #: xml_file_content.cpp:1 msgid "Mission Info" msgstr "" #: xml_file_content.cpp:2 msgid "Title:" msgstr "" #: xml_file_content.cpp:3 msgid "Author:" msgstr "" #: xml_file_content.cpp:4 msgid "Description:" msgstr "" #: xml_file_content.cpp:6 msgid "Required TDM Version:" msgstr "" #: xml_file_content.cpp:7 msgid "(optional, e.g. \"2.03\")" msgstr "" #: xml_file_content.cpp:8 msgid "Mission Titles:" msgstr "" #: xml_file_content.cpp:9 msgid "" "Optional. Fill in if this \n" "package is a campaign \n" "containing several maps" msgstr "" #: xml_file_content.cpp:10 xml_file_content.cpp:2 msgid "Output Path:" msgstr "" #: xml_file_content.cpp:11 xml_file_content.cpp:3 msgid "C:GamesDarkmodfmsgathers" msgstr "" #: xml_file_content.cpp:12 msgid "Edit readme.txt..." msgstr "" #: xml_file_content.cpp:1 msgid "Mission Readme" msgstr "" #: xml_file_content.cpp:1 msgid "Options:" msgstr "" #: xml_file_content.cpp:2 msgid "Surround with monsterclip brush" msgstr "" #: xml_file_content.cpp:3 msgid "Reload Skins" msgstr "" #: xml_file_content.cpp:4 msgid "Reload Models" msgstr "" #: xml_file_content.cpp:2 msgid "Difficulty" msgstr "" #: xml_file_content.cpp:3 msgid "Iniital State" msgstr "" #: xml_file_content.cpp:4 xml_file_content.cpp:20 msgid "Flags" msgstr "" #: xml_file_content.cpp:5 msgid "Mandatory" msgstr "" #: xml_file_content.cpp:6 msgid "Ongoing" msgstr "" #: xml_file_content.cpp:7 xml_file_content.cpp:22 msgid "Irreversible" msgstr "" #: xml_file_content.cpp:9 msgid "Enabling Objectives" msgstr "" #: xml_file_content.cpp:10 msgid "Sucess Logic" msgstr "" #: xml_file_content.cpp:11 msgid "Failure Logic" msgstr "" #: xml_file_content.cpp:12 msgid "Completion Script" msgstr "" #: xml_file_content.cpp:13 msgid "Failure Script" msgstr "" #: xml_file_content.cpp:14 msgid "Completion Target" msgstr "" #: xml_file_content.cpp:15 msgid "Failure Target" msgstr "" #: xml_file_content.cpp:21 msgid "Satisfied at start" msgstr "" #: xml_file_content.cpp:23 msgid "Boolean NOT" msgstr "" #: xml_file_content.cpp:24 msgid "Player responsible" msgstr "" #: xml_file_content.cpp:25 msgid "&OK" msgstr "" #: xml_file_content.cpp:26 msgid "&Cancel" msgstr "" #: xml_file_content.cpp:1 msgid "Objective Conditions" msgstr "" #: xml_file_content.cpp:4 msgid "Condition" msgstr "" #: xml_file_content.cpp:5 msgid "Source Mission:" msgstr "" #: xml_file_content.cpp:6 msgid "Source Objective:" msgstr "" #: xml_file_content.cpp:7 msgid "Source Objective State:" msgstr "" #: xml_file_content.cpp:8 msgid "Target Objective:" msgstr "" #: xml_file_content.cpp:9 msgid "Action:" msgstr "" #: xml_file_content.cpp:10 msgid "Action Value:" msgstr "" #: xml_file_content.cpp:11 msgid "Sentence" msgstr "" #: xml_file_content.cpp:12 msgid "-" msgstr "" #: xml_file_content.cpp:1 msgid "Objective Entities" msgstr "" #: xml_file_content.cpp:4 msgid "Objectives" msgstr "" #: xml_file_content.cpp:11 msgid "Success Logic" msgstr "" #: xml_file_content.cpp:12 msgid "Edit Mission Success Logic" msgstr "" #: xml_file_content.cpp:1 msgid "Use background image" msgstr "" #: xml_file_content.cpp:2 msgid "Image file" msgstr "" #: xml_file_content.cpp:3 msgid "Transparency" msgstr "" #: xml_file_content.cpp:4 msgid "transparent" msgstr "" #: xml_file_content.cpp:5 msgid "opaque" msgstr "" #: xml_file_content.cpp:6 msgid "Image scale" msgstr "" #: xml_file_content.cpp:7 msgid "Horizontal offset" msgstr "" #: xml_file_content.cpp:8 msgid "Vertical offset" msgstr "" #: xml_file_content.cpp:10 msgid "Keep aspect ratio" msgstr "" #: xml_file_content.cpp:11 msgid "Zoom image with viewport" msgstr "" #: xml_file_content.cpp:12 msgid "Pan image with viewport" msgstr "" #: xml_file_content.cpp:1 msgid "Particle Definitions" msgstr "" #: xml_file_content.cpp:5 msgid "Particle Stages" msgstr "" #: xml_file_content.cpp:8 msgid "Toggle Visibility" msgstr "" #: xml_file_content.cpp:9 msgid "Up" msgstr "" #: xml_file_content.cpp:10 msgid "Down" msgstr "" #: xml_file_content.cpp:12 msgid "Depth Hack:" msgstr "" #: xml_file_content.cpp:13 msgid "Stage Settings" msgstr "" #: xml_file_content.cpp:16 msgid "Colour:" msgstr "" #: xml_file_content.cpp:17 msgid "Fade Colour:" msgstr "" #: xml_file_content.cpp:18 msgid "Use Entity Colour" msgstr "" #: xml_file_content.cpp:19 msgid "Fade In Fraction:" msgstr "" #: xml_file_content.cpp:20 msgid "Fade Out Fraction:" msgstr "" #: xml_file_content.cpp:21 msgid "Fade Index Fraction:" msgstr "" #: xml_file_content.cpp:22 msgid "Animation:" msgstr "" #: xml_file_content.cpp:23 msgid "Frames:" msgstr "" #: xml_file_content.cpp:24 msgid "Rate:" msgstr "" #: xml_file_content.cpp:25 msgid "FPS" msgstr "" #: xml_file_content.cpp:26 msgid "Count / Time" msgstr "" #: xml_file_content.cpp:27 msgid "Count:" msgstr "" #: xml_file_content.cpp:28 msgid "Duration / sec:" msgstr "" #: xml_file_content.cpp:29 msgid "Bunching:" msgstr "" #: xml_file_content.cpp:30 msgid "Cycles:" msgstr "" #: xml_file_content.cpp:31 msgid "Time Offset / sec:" msgstr "" #: xml_file_content.cpp:32 msgid "Dead Time / sec:" msgstr "" #: xml_file_content.cpp:33 msgid "Size / Speed" msgstr "" #: xml_file_content.cpp:34 msgid "Speed:" msgstr "" #: xml_file_content.cpp:35 xml_file_content.cpp:38 xml_file_content.cpp:41 #: xml_file_content.cpp:44 msgid "From:" msgstr "" #: xml_file_content.cpp:36 xml_file_content.cpp:39 xml_file_content.cpp:42 #: xml_file_content.cpp:45 msgid "To:" msgstr "" #: xml_file_content.cpp:37 msgid "Size:" msgstr "" #: xml_file_content.cpp:40 msgid "Rotation Speed:" msgstr "" #: xml_file_content.cpp:43 msgid "Aspect Ratio:" msgstr "" #: xml_file_content.cpp:46 msgid "Gravity:" msgstr "" #: xml_file_content.cpp:47 msgid "Use World Gravity" msgstr "" #: xml_file_content.cpp:48 msgid "Bounds Expansion:" msgstr "" #: xml_file_content.cpp:49 msgid "Distribution" msgstr "" #: xml_file_content.cpp:50 msgid "Shape:" msgstr "" #: xml_file_content.cpp:51 msgid "Rectangular" msgstr "" #: xml_file_content.cpp:52 msgid "Cylindric" msgstr "" #: xml_file_content.cpp:53 msgid "Spherical" msgstr "" #: xml_file_content.cpp:54 msgid "X Size:" msgstr "" #: xml_file_content.cpp:55 msgid "Y Size:" msgstr "" #: xml_file_content.cpp:56 msgid "Z Size:" msgstr "" #: xml_file_content.cpp:57 msgid "Ring Size:" msgstr "" #: xml_file_content.cpp:58 msgid "Offset:" msgstr "" #: xml_file_content.cpp:59 msgid "Randomness:" msgstr "" #: xml_file_content.cpp:60 msgid "Distribute Particles randomly within Volume" msgstr "" #: xml_file_content.cpp:61 msgid "Direction / Orientation" msgstr "" #: xml_file_content.cpp:62 msgid "Direction:" msgstr "" #: xml_file_content.cpp:63 msgid "Cone" msgstr "" #: xml_file_content.cpp:64 msgid "Outward" msgstr "" #: xml_file_content.cpp:65 msgid "Cone Angle:" msgstr "" #: xml_file_content.cpp:66 msgid "Upward Bias:" msgstr "" #: xml_file_content.cpp:67 msgid "Orientation:" msgstr "" #: xml_file_content.cpp:69 msgid "Aimed" msgstr "" #: xml_file_content.cpp:70 msgid "X" msgstr "" #: xml_file_content.cpp:71 msgid "Y" msgstr "" #: xml_file_content.cpp:72 msgid "Z" msgstr "" #: xml_file_content.cpp:73 msgid "Trails:" msgstr "" #: xml_file_content.cpp:74 msgid "Time:" msgstr "" #: xml_file_content.cpp:75 msgid "Initial Angle:" msgstr "" #: xml_file_content.cpp:76 msgid "Path" msgstr "" #: xml_file_content.cpp:77 msgid "Path Type:" msgstr "" #: xml_file_content.cpp:78 msgid "Standard" msgstr "" #: xml_file_content.cpp:79 msgid "Flies" msgstr "" #: xml_file_content.cpp:80 msgid "Helix" msgstr "" #: xml_file_content.cpp:81 msgid "Radial Speed:" msgstr "" #: xml_file_content.cpp:82 msgid "Axial Speed:" msgstr "" #: xml_file_content.cpp:83 msgid "Sphere Radius:" msgstr "" #: xml_file_content.cpp:84 msgid "Cylinder Size X:" msgstr "" #: xml_file_content.cpp:85 msgid "Cylinder Size Y:" msgstr "" #: xml_file_content.cpp:86 msgid "Cylinder Size Z:" msgstr "" #: xml_file_content.cpp:87 msgid "Note: changes will be written to the file ....." msgstr "" #: xml_file_content.cpp:1 msgid "Create simple Patch Mesh" msgstr "" #: xml_file_content.cpp:2 msgid "Width:" msgstr "" #: xml_file_content.cpp:3 msgid "Height:" msgstr "" #: xml_file_content.cpp:4 msgid "Remove selected Brush" msgstr "" #: xml_file_content.cpp:1 msgid "Patch Control Vertices" msgstr "" #: xml_file_content.cpp:2 msgid "Row:" msgstr "" #: xml_file_content.cpp:3 msgid "Column:" msgstr "" #: xml_file_content.cpp:4 msgid "Coordinates" msgstr "" #: xml_file_content.cpp:5 msgid "Patch Tesselation" msgstr "" #: xml_file_content.cpp:6 msgid "Fixed Subdivisions" msgstr "" #: xml_file_content.cpp:7 msgid "Horizontal:" msgstr "" #: xml_file_content.cpp:8 msgid "Vertical:" msgstr "" #: xml_file_content.cpp:1 msgid "Thicken selected Patches" msgstr "" #: xml_file_content.cpp:2 msgid "Extrude along Vertex Normals" msgstr "" #: xml_file_content.cpp:3 msgid "Extrude along X-Axis" msgstr "" #: xml_file_content.cpp:4 msgid "Extrude along Y-Axis" msgstr "" #: xml_file_content.cpp:5 msgid "Extrude along Z-Axis" msgstr "" #: xml_file_content.cpp:6 msgid "Thickness (units):" msgstr "" #: xml_file_content.cpp:7 msgid "Create Seams (\"side walls\")" msgstr "" #: xml_file_content.cpp:1 msgid "General Properties" msgstr "" #: xml_file_content.cpp:2 msgid "Inventory Name:" msgstr "" #: xml_file_content.cpp:3 msgid "XData Name:" msgstr "" #: xml_file_content.cpp:4 msgid "Number of Pages:" msgstr "" #: xml_file_content.cpp:5 msgid "Layout:" msgstr "" #: xml_file_content.cpp:6 msgid "One-sided" msgstr "" #: xml_file_content.cpp:7 msgid "Two-sided" msgstr "" #: xml_file_content.cpp:8 msgid "Pageturn Sound:" msgstr "" #: xml_file_content.cpp:9 msgid "Page Editing" msgstr "" #: xml_file_content.cpp:10 msgid "Insert Page" msgstr "" #: xml_file_content.cpp:11 msgid "Current Page:" msgstr "" #: xml_file_content.cpp:12 msgid "1" msgstr "" #: xml_file_content.cpp:13 msgid "Remove Page" msgstr "" #: xml_file_content.cpp:14 msgid "GUI Definition:" msgstr "" #: xml_file_content.cpp:18 msgid "Body" msgstr "" #: xml_file_content.cpp:19 msgid "Tools" msgstr "" #: xml_file_content.cpp:22 msgid "Save and Close" msgstr "" #: xml_file_content.cpp:2 msgid "pauseTimeButton" msgstr "" #: xml_file_content.cpp:4 msgid "prevButton" msgstr "" #: xml_file_content.cpp:5 msgid "nextButton" msgstr "" #: xml_file_content.cpp:6 msgid "texturedModeButton" msgstr "" #: xml_file_content.cpp:7 msgid "lightingModeButton" msgstr "" #: xml_file_content.cpp:8 msgid "gridButton" msgstr "" #: xml_file_content.cpp:1 msgid "Type:" msgstr "" #: xml_file_content.cpp:3 xml_file_content.cpp:22 msgid "Chance:" msgstr "" #: xml_file_content.cpp:4 msgid "Random Effects:" msgstr "" #: xml_file_content.cpp:5 msgid "Response Effects" msgstr "" #: xml_file_content.cpp:3 msgid "Activation Timer:" msgstr "" #: xml_file_content.cpp:4 msgid "h" msgstr "" #: xml_file_content.cpp:5 msgid "m" msgstr "" #: xml_file_content.cpp:6 msgid "s" msgstr "" #: xml_file_content.cpp:7 xml_file_content.cpp:13 xml_file_content.cpp:15 msgid "ms" msgstr "" #: xml_file_content.cpp:8 msgid "Timer restarts after firing" msgstr "" #: xml_file_content.cpp:9 msgid "Timer reloads" msgstr "" #: xml_file_content.cpp:10 msgid "times" msgstr "" #: xml_file_content.cpp:11 msgid "Timer waits for start (when disabled: starts at spawn time)" msgstr "" #: xml_file_content.cpp:12 msgid "Time interval:" msgstr "" #: xml_file_content.cpp:14 msgid "Duration:" msgstr "" #: xml_file_content.cpp:16 msgid "Radius" msgstr "" #: xml_file_content.cpp:17 msgid "Use bounds" msgstr "" #: xml_file_content.cpp:18 msgid "Radius changes over time to:" msgstr "" #: xml_file_content.cpp:19 msgid "Magnitude:" msgstr "" #: xml_file_content.cpp:20 msgid "Falloff Exponent:" msgstr "" #: xml_file_content.cpp:21 msgid "Max Fire Count:" msgstr "" #: xml_file_content.cpp:23 msgid "Velocity:" msgstr "" #: xml_file_content.cpp:24 msgid "Bounds:" msgstr "" #: xml_file_content.cpp:25 msgid "Min:" msgstr "" #: xml_file_content.cpp:26 msgid "Max:" msgstr "" DarkRadiant-2.5.0/install/i18n/de/000077500000000000000000000000001321750546400165265ustar00rootroot00000000000000DarkRadiant-2.5.0/install/i18n/de/LC_MESSAGES/000077500000000000000000000000001321750546400203135ustar00rootroot00000000000000DarkRadiant-2.5.0/install/i18n/de/LC_MESSAGES/darkradiant.mo000066400000000000000000003206371321750546400231470ustar00rootroot00000000000000Z2eȇ ɇׇ܇ -3: R ^ipvÈ҈  *5;Jfl Ή ԉ މ  24 6BT`Ɗ֊'؊(})B  '*Rb s(   * 1?GPfx ʍ э+ۍ#: > LV er  Ɏ َ: )-5=L _ it#ʏ$ !A]| .  1 ;E N\e7l  Ñ͑ , 3?FZks{ n.6;P Vbj ȓړ &6$E*j#Eʔ'7 H R ^ jt| ŕ ӕݕ " -4 FR bn Ӗ;ޖ8*S+~8;=+]6D:;@=|1(-&JNq6(9 4Z$*ߛy!u!RK5ԝ(B9\' ̞Ԟ #1:BXo ](: K W d r ̠$5 DQay ҡWTck ŢТآ  2 D O#Y}  3£ .?1\  ɤդ#+;)g.̥)/C's(,Ħ'3!Mo u  ˧ۧ '6 Mn~ ƨԨ %5E^w"©ǩ(ש 1 C Q _ k7x "'8I Zdjr y īϫ * @ K Wc#j ͬ #7L\n " #ȭ . HV ny#%֮ + 2?F(a'= "% * 6@S;e Ű   2? Q]m"ر#*2 AN ^h!x-Ȳܲ " ,7Ma $EZmv  δ ߴ  *%2Xo 9 7>^ m {̶ +@ St"ͷ  -;JYh"'' 0CKR Wb7?N do  ʺ ݺ  /5;Qa  ̻ &,2B`x 37м!*14=C S ^ i t ~ýɽнֽ% G!DiҾ۾# 3 AMV i&s  Ϳܿ  !D%:jK  0 CQ ` k2xM  +:Sh!o  !6G6Y  *(8 >KSl  +3DIOV_gptz  0>Zr   % ;GXl    ' 4 ?Kh     %+/8 Vbx  $ .GLfn t ^$=BWh o y  !"  !/DG,(    * 7B IV_o   !=F KYou   +:O*bL:,0A,r  m#+/X4+'%*7b h s ~^"&8;Od w B  #,<U^p I@ GTZ `mv    $49Iai(r *<Qh2 DRZc hsz  UFN`#    *=%[   %3:W gsx ~ D $08? O[ lz  %, HTez)  ")%=cl $ 1K` fqz$ ",16 <GMUY`f oy   ) #6Zr '8 HU e s! , ;,Iv !$ FRbt  (;Qh{!6 >LSY _m r   2IXl   +:Ie)  ! -CZ ak (8$] m y   (17Ts  " 1 >HZt *7J ] jwI19HM'`(#H'l(K* .4mc(#!/@Rpx$<ia0+,\064&@j]~GOXkz ;UhI   / 70A r .  9CR<1,,2._.@ '7_gj "  * 0:@ HTen   % 0 8 FR[`t3| ?   .< @Lay  3eQ  &A9{     "2 AM`f w    #  # ' 59C }  0 C Q^mG!i!      /  D Q  W  b                 $ . @ S  r }            * 7 H ^ ` b $x ^   & 1( 9Z  T5      )  $?Qax.    (3J^d} / ">G[n     +C6 z   "#.(N,w8U=d       +@4u ~  /? GT ] g t  (.FLU,^     2?Y(h.'E?Vn     #3.Fu+ 1E c<o856KRO>?-KmSG OU> `7OB98B=<E@ 9D =~   .y!1!!s"E#F#$b#c#>#1*$\$ w$$ $.$$$$% % 3%)T%~%%&)& 2&=&U&s&&&&&&&&'4'H'g''''''''(#(8( G( S(`(i(((((e)u)7})) )))) )* ***+*@*H*O*e* {* *'*******T+Z+o++++7++ ,$,-,A,Q,g,{,, ,,3,&,'&-$N-'s-#-8-:-13.1e.H.8.D/%^// ///////0/0J0f0w0'000%001#1 C1 d1p1111111%1 2%2;@2 |222222223E3e343333 4 44)4<4 O4 [4 g4q4z4444 4445 5$535G5 a5 n5 |555555" 6/6>6[6j6z666 6$6 6 6-7%47Z7`7s7(777 7(78'980a8828 888(9.*9&Y999)9 9999:P: j:&t: :': :::::: ;;(;:;R;d;y; ;;;#;(<.<H<f<m<u< < <<!<%<C<7= L=X=#h=======$>? ,?M?V? h?r? y???? ? ? ? ?? @1@D@&b@@ @@@B@@A!A>A#EAiAyAAA%AAAB5BDBHBMB SB^BpBB'B(BB,C'5C0]C/CCCCDD"+D(ND:wD+DDD E 'E2E:E @E KElEEEhFoFF FFFFFF FGG &G 2G T lT"xTTT T T TT UUU'U GUQU hUtUU UUUU UU$U V VV#V :VFVOV `VkVzV"V%V+V W%W4WKW]W fW pW zWW W WWWWWWXX3X;XJXSXbXkX XXX,X=XY-Y2YPYWY ]YjYYY%YjYGZaZfZZZZZZZ ZZ [[#([L[\[ m[{[I[3[\/ \ P\Z\a\ r\ \\ \\\ \\\ \]"]5]$M] r] }] ]!]] ],]^^^(^8^>^N^ `^j^ q^ |^^ ^^ ^^^^^ ^^_'_B_9[__U_P`W`;r`Q`a a%a 7a DaOa Ua ca oazaa2%bXb<cCEc2c/c.c4d Pd\drdddd]d&%eLegejeee ee eGe"f+f2f7f Uf_f gf uf f&f ffff gH gVg _gIig ggg g g g gg h h!h,@hmh}hhhhh(hhi7 iEi]ifi{iiii)ii)i&j!Djfj}jjjjjjjk+k>kNkVk_k dk nk|kkk kkkkk^lnlRl'l mm$m -m;mLmSmfmvmm!m mmmn n n ,n 8n"Enhn}nnn)nnnn n oo"'o JokoEzoo o ooopp*p} E} R}^} s} }}}}"}} }- ~:~Q~k~~~ ~~~~ +/Hx!$@*- 4 BN`inw}  ـ #0M2b7́.  " / ;Ii}   ̂Ղۂ $>cwɃڃ1 GTf Մ ݄    % ? MYk  >ͅ9 AFL)Նf)f$*-b4q6~݈*\1%5߉Zp+ 6  ׋=19oO5:/j&t}$ ʎَߎN \c\jhǏ05Oc   Ő֐ ސ'  .5 < FR=V ‘ Ǒ ёCܑ\ D}@’67:8r1:ݓ1G8e  .' "5Xq •ӕ ٕ  /<Nbq  3і  &/*6 a:ln  )7$ ,:@ O\r t $  '2JM Vag it|BPS U`h|#ɛΛO C OYbi p}    ȝ ͝۝   '3FM _ k yĞ#؞$';RO ԟ "1G[n}ˠ4OOOuCTr)2 $8T34R1I.:@(lrng4Xc:!i>i-F-LRSg@L GG$`62#]XR{&ve'~n&NU@Am#(]@y7Y Rxz*R9Cjxnt \.Ba M `:Gy^<PO9enfWNw# FaQ./=aM"05*=hKe$B_f![,K'Neg&$!bV10|x4hd<`<b~%E-(*Td!J[r7 W^<OC+D@E~/5?tASYD7"bf,smiT 1me"KSz>>bwE;27'UI_*@5P)=+p8 y sn$qz%*-q&H6{" {`/uAvpNUJ1|L=+GWg{<MH2]NkQkq[p0h"jcwl8ZHX x|J-;z-(I6'32wr+,q3fOD*Ka7aX |JOV  BshPk\tMWhVhz+dU}# 01r#Ap3ZF^lY(Rc }v!%LXyXe($q=%BQZc:#uOEBo FQ q6oK6{j:u)m; ;  ] ,HyC1 Z7~I;?L 'rst/'5U32TE DCgHjoFt,Tn~]%Y42F__J5, ok{VNC@8WYM`Da8Q"3BfL(H }} 9?&%>Mt);AsZAI8lji?o  \51o?^+8& PZRv?vSKEd iLGwG[D4uO/=Q&4V,<lP[>4 .'9kIuQP *y.6_[^ YxB- }b]}9wW:d^sN%+0D ./`cJ)#S\5)d|XvCm;!9c/SWVH<?kY$bjFPp JU796iGZ._V!\30T>|I=Ag):\mz~xEMK l>fS 0p"U (overridden) Y: Z: #&About&Bind selected entities&Cancel&Center&Connect selected entities&Copy&Curve&Deactivate all Filters&Down Floor&Duplicate&Edges&Edit&Export selected as model...&Faces&File&Game/Project Setup...&Grid&Help&Import map...&Invert selection&Light Inspector&New Map&OK&Open...&Paste&Patch&Patch Inspector&Pointfile&Redo&Reload Models&Revert group to worldspawn&Save&Save selected as Map...&Save selected as prefab...&Surface Inspector&Switch off&Texture Tool&Undo&Up Floor&Vertices&XY 100%&XY Zoom In&XY Zoom Out(Line {0}): {1}(optional, e.g. "2.03")-1Error: File not found.Note: You'll need to restart DarkRadiant after changing the language setting.?A Scheme with that name already exists.A certain page of a readable is reached.A custom component requires no specifiers, the state of this component is manually controlled (i.e. by scripts or triggers).A single entity must be selected to edit Stim/Response properties.AAS Area ViewerAAS ViewerAIAI can be flatfootedAI can drownAI doesn't think outside the player PVSAI finds a bodyAI finds an itemAI is alertedAI is civilianAI is immune to GasAI is immune to KOsAI is immune to KOs at high alert levelsAI is killedAI is knocked outAI:AbilitiesAbkhazianAbout DarkRadiantActionAction Value:Action:ActivateActivate &all FiltersActivation Timer:ActiveActive View NameActorActor (click to edit)Actor {0:d}Actor {0:d} ({1})ActorsActors OKActors always face each other while talkingActors missingActors must be within talk distanceAddAdd Stim TypeAdd TitleAdd new EffectAdd propertyAdd property...Add to Layer...AfrikaansAimedAkanAlbanianAlign Texture:All FilesAll Files (*.*)All LevelsAll actors are correctly referring to entities in the map.All skinsAltAmharicAmount:Analysing GuisAnimal Patrol ModeAnimationAnimation:Any AI of specified typeAny AI on specified teamAny AI with specified combat statusAny entity of specified classAny entity with SDK-level spawnclassAppearanceAppendAppend 2 columns at the beginningAppend 2 columns at the endAppend 2 rows at the beginningAppend 2 rows at the endAppend Curve Control PointAppend PageAppends a control point to the selected curvesApply to selectionApply...ArabicAragoneseArbitrary TransformationArgument:ArgumentsArmenianAspect Ratio:AssameseAssignAt least one brush must be selected for this operation.AttributeAudio AcuityAuthor:Auto LoopAutosave Interval (in minutes)Available AnimationsAvailable SetsAvaricAvestanAxial Speed:AymaraAzerbaijaniB&rushBackground Image...Background imageBambaraBashkirBasqueBehaviourBelarusianBengaliBevelBeware that other entities might still be using this stim type. Do you really want to delete this custom stim?Big DotsBihari languagesBislamaBodyBody is shoulderableBody:Boolean NOTBosnianBoth fit values must be > 0.BottomBottom LeftBottom RightBounds Expansion:Bounds:BretonBrowse AnimationsBrowse Sound ShadersBrowse custom path:Browse mod resourcesBrush Number:Brush Size InfoBrush VerticesBrushDef3Parser: invalid token '{0}'BrushDef3ParserQuake4: invalid token '{0}'BrushDefParser: invalid token '{0}'BrushesBrushes:Brushes: {0:d} ({1:d}) Patches: {2:d} ({3:d}) Entities: {4:d} ({5:d})Build date: 2014-08-04Build date: {0}Building tree...BulgarianBulge PatchBulge patchBunching:BurmeseButtonC&lear selectionC:GamesDarkmodfmsgathersCOMPLETECSGCSG &MergeCSG &SubtractCSG MergeCSG Merge: No brushes selected.CSG SubtractCSG Subtract: No brushes selected.CameraCamera BackgroundCamera IconCamera PositionCamera ViewCan greet othersCan light TorchesCan operate DoorsCan operate ElevatorsCan operate Switch LightsCan searchCan't append curve point - no entities with curve selected.Can't convert curves - no entities with curves selected.Can't copy Shader. Couldn't retrieve face.Can't copy Shader. Couldn't retrieve patch.Can't copy Shader. Please select a single face or patch.Can't insert curve points - must be in vertex editing mode.Can't insert curve points - no entities with curves selected.Can't paste Texture Coordinates from faces.Can't paste Texture Coordinates from patches to faces.Can't paste Texture Coordinates. Target patch dimensions must match.Can't paste shader to entire brush. Target is not a brush.Can't remove curve points - must be in vertex editing mode.Can't remove curve points - no entities with curves selected.CancelCannot add, filter with same name already exists.Cannot bulge patch. No patches selected.Cannot change classname of worldspawn entity.Cannot change classname to worldspawn.Cannot create a selection set, there is nothing selected in the current scene.Cannot create bevel-cap, patch must have a width of 3.Cannot create caps, no patches selected.Cannot create cylinder-cap, patch must have a width of 9.Cannot create end-cap, patch must have a width of 5.Cannot create layer with empty name.Cannot create particle with an empty name.Cannot create selection setCannot merge entities, the selection must consist of func_* entities only. (The first selected entity will be preserved.)Cannot open file for reading: {0}Cannot open file for writing: {0}Cannot reparent primitives to entity. Please select at least one brush/patch and exactly one func_* entity. (The entity has to be selected last.)Cannot run Readable Editor on this selection. Please select a single XData entity.Cannot save particle, it has not been registered yet.Cannot scale by zero value.Cannot set classname to an empty string.Cannot stitch patch textures. Exactly 2 patches must be selected.Cannot stitch textures. Could not cast nodes to patches.Cannot thicken patch. Nothing selected.Cap SelectionCatalanCaulk shader nameCenter Objects around OriginCenter Pivot when scaling facesCentral KhmerChamorroChance:Change Mandatory FlagChange Objective StateChange VisibilityChange viewsChanging this entity's model to the selected value will remove all child primitives from it: ChechenChichewaChineseChoose AI HeadChoose AI Vocal SetChoose AI head...Choose DirectoryChoose FileChoose ModelChoose PrefabChoose ShaderChoose SkinChoose Vocal Set...Choose a Gui Definition...Choose a file...Choose an XData Definition...Choose entity class...Choose model...Choose particle...Choose particlesChoose skin...Choose soundChoose sound...Choose target entity...Choose textureChoose texture...ChuvashClassnameClassname:ClearClear Selection SetsClear mandatory flagClick here to assignClick to select all in layer, hold SHIFT to deselect, hold CTRL to set as active layer.Clip SelectionClipperClipper tool uses caulk textureClock interval:CloseClose without savingColourColour:Colours...Column:ColumnsCommandCommand ArgumentsCommand PropertiesCommandsCompletedCompletion ScriptCompletion TargetComponentsConditionCondition affecting objective {0:d}ConeCone Angle:Cone...Confirm Layer DeletionConsoleConsole ViewConsult the import summary for further information.Conversation EditorConversation EntitiesConversationsConversations...Convert NURBS <-> CatmullRomConvert the selected curve (NURBS <-> CatmullRom)Convert to func_staticCoordinatesCopyCopy Colour SchemeCopy ShaderCopy Spawnarg(s)Copy shader nameCornishCorsicanCould not create directoryCould not create patch.Could not determine map format of file: {0}Could not open output streams for writingCould not open pointfile: {0}Could not read map file: {0}Could not recognise map version number format.Could not remove the file {0}Could not rename layer, please try again.Could not rename the existing file to .bak: {0}Could not rename the temporary file {0}Could not rename the temporary file: {0}Could not set Region: XY Top View not found.Could not set Region: nothing selected.Could not set Region: please select a single Brush.Could not write {0} contents: {1}CountCount / TimeCount:Create BevelCreate Cap PatchCreate CatmullRom CurveCreate ConeCreate CylinderCreate Decal PatchesCreate Decals for selected FacesCreate Dense CylinderCreate End capCreate Flat Patch MeshCreate Group out of Prefab partsCreate Layer...Create NURBS CurveCreate Seams ("side walls")Create Simple Patch MeshCreate SphereCreate Square CylinderCreate Very Dense CylinderCreate entityCreate entity...Create light...Create model...Create player start hereCreate simple Patch MeshCreate speaker...Creates a NURBS curveCreating a new XData definition...CreeCritical HealthCritical: Cannot find selected entities.CroatianCrossesCtrlCubic clip the camera viewCurrent Grid SizeCurrent Page:Custom FolderCustom StimCustom StimsCustom properties defined for this entity class, if anyCustom scriptCustom script queried periodicallyCut Spawnarg(s)Cycle Face SelectionCycle SelectionCycles:CylinderCylinder Size X:Cylinder Size Y:Cylinder Size Z:CylindricCzechD&eleteDanishDarkMod PathDarkRadiant ModulesDarkRadiant PreferencesDarkRadiant x.y.zDeactivateDead Time / sec:Decrease Grid SizeDefaultDefault Grid SizeDefault LogicDefault Scale:Default texture scaleDefined inDefined in:Definition:DeleteDelete 2 columns from the beginningDelete 2 columns from the endDelete 2 rows from the beginningDelete 2 rows from the endDelete Custom StimDelete TitleDelete all selection sets?Delete on left SideDelete on right SideDelete propertyDelete this layerDelete whole PageDepth Hack:Describes available Mouse CommandsDescriptionDescription:Deselect elements using this shaderDetails (double-click to edit)Diff.DifficultyDifficulty EditorDifficulty-specific LogicDifficulty...Direction / OrientationDirection:Disable Alert Idle StateDisable Windows Desktop CompositionDisable sound for this session.Discrete movement (non-freelook mode)Distance:Distribute Particles randomly within VolumeDistributionDivehiDo not cast shadows (fast)Do you really want to delete this layer?Do you want to open the import summary?Do you want to save the changes you made to the particle {0}?DotsDotted LinesDouble click row to edit a bindingDownDrag CameraDrag ViewDrag-Selection BoxDrag-create BrushDrag-resize entities symmetrically (leave origin unchanged)DrunkDrunk Acuity FactorDuplicateDuplicated XData definitionsDuration / sec:Duration:DutchDzongkhaE&ntityE&xitEditEdit Colour SchemesEdit CommandEdit ConversationEdit FilterEdit Filters...Edit Mission LogicEdit Mission Success LogicEdit Mouse BindingsEdit ObjectiveEdit Objective ConditionsEdit Package Info (darkmod.txt)...Edit Response EffectEdit readme.txt...EffectEffect:Empty FilenameEmpty FilterEmpty SelectionEn&titiesEnable AutosaveEnable Texture Lock (for Brushes)Enable far-clip plane (hides distant objects)Enabling ObjectivesEnd CapEngine PathEngine path "{0}" does not exist. EnglishEnter Layer NameEnter NameEnter Number of SidesEnter Particle NameEnter a name and hit ENTER to save a set. Select an item from the dropdown list to restore the selection. Hold SHIFT when opening the dropdown list and selecting the item to de-select the set.Enter a name for the new scheme:Enter new Layer NameEnter new ShortcutEntitiesEntities (default)Entities:EntityEntity ClassEntity Class TreeEntity Class Tree...Entity InspectorEntity KeyEntity ListEntity Number:Entity {0}Entity {0}, Primitive {1}Entity:Error saving particle definition: {0}Error: File not found.Error: eclass pointer invalid.Errors occurred:EsperantoEstonianEweExactly two entities must be selected for this operation.Exception occurred: Exit DarkRadiantExpand selection to all SiblingsExportExport Format for scaled ModelsExport OptionsExport failedExport regionExport selectionExtrude along Vertex NormalsExtrude along X-AxisExtrude along Y-AxisExtrude along Z-AxisFAILEDFPSFaceFacesFade Colour:Fade In Fraction:Fade Index Fraction:Fade Out Fraction:Failed parsing entity {0:d}: {1}Failed to create the folder {0}Failed to import {0}.Failed to load gui definition {0}.Failed to open {0} for saving.Failed to parse darkmod.txt: {0}Failed to parse readme.txt: {0}Failure LogicFailure Logic:Failure ScriptFailure TargetFailure during import.Failure opening file: {0}Failure reading map file: {0} {1}Failure reading map from clipboard: {0}Failure running map pre-save event: {0}Falloff Exponent:Far Clip Plane InFar Clip Plane OutFaroeseFijianFileFile Path:File is write-protected: {0}File not readableFilter SettingsFilter rules are applied in the shown order. "Match" is accepting regular expressions. "Object"-type filters can be used to match "patch" or "brush".FiltersFind & ReplaceFind & Replace ShaderFind BrushFind and ReplaceFind and replace textures...Find brush...Find:FinnishFitFit Texture:Fixed SubdivisionsFixup FileFixup MapFixup Map...Fixup ResultsFixup cancelledFixup in progressFlagsFliesFlip Clip OrientationFlip HorizontalFlip Selection Horiz (S-Axis)Flip Selection Vertical (T-Axis)Flip Texture:Flip VerticalFloor selectionFocus camera on selected entityFrames:Freelook mode can be toggledFreemove ModeFrenchFrom:FulahGUI Definition:GUI Page Index out of bounds.Gaelic; Scottish GaelicGalicianGameGame SetupGame Type:GameManager: No game type selected, can't continue.GameManager: No valid game files found, can't continue.GandaGeneral PropertiesGeorgianGermanGoGravity:GreekGrid BackgroundGrid BlockGrid MajorGrid MinorGrid TextGrid0.125Grid0.25Grid0.5Grid1Grid128Grid16Grid2Grid256Grid32Grid4Grid64Grid8Group SelectionGroup identifier (component-specific)Group selected itemsGroups can be dissolved in Primitive and Group Part selection mode onlyGroups can be formed in Primitive and Group Part selection mode onlyGuaraniGui PathGui import summaryGujaratiHaitianHausaHead: HealthHealth / CombatHebrewHeight:HelixHereroHide DeselectedHide SelectedHide UnusedHide allHide distant AreasHide/ShowHigher Selection Priority for EntitiesHindiHiri MotuHollowHoriz. Scale:Horiz. Shift:Horizontal FOVHorizontal offsetHorizontal:HungarianINCOMPLETEINVALIDIcelandicIdoIf Objective {0} in Mission {1} is in state '{2}' do the following: If you don't save, changes from the last {0} will be lost.IgboIgnoreIgnore light volume bounds when calculating default rotation pivot locationImage fileImage mapImage scaleImport &prefab...Import definition?Import failedImport failed:Import mapImporting...Incorrect map version: required {0:f}, found {1:f}Increase Grid SizeIndexIndicates whether anything among the current selection is part of this layer.IndonesianIniital StateInitial Angle:Initialising Module: {0}Initialising ModulesInsertInsert 2 Columns at the beginningInsert 2 Columns at the endInsert 2 Rows at the beginningInsert 2 Rows at the endInsert Curve Control PointsInsert PageInsert on left SideInsert on right SideInsert prefab...Insert whole PageInserts a curve control point before the selected onesInterlinguaInterlingue; OccidentalInuktitutInupiaqInvalid SettingsInventory Name:InvertInvert mouse vertical axis (freelook mode)Inverted BevelInverted EndcapIrishIrreversibleItalianItem is in info_locationItem is in locationItem:JapaneseJavaneseJump to ObjectKalaallisut; GreenlandicKannadaKanuriKashmiriKazakhKeep aspect ratioKeyKeyboard Shortcuts...KikuyuKill target:KinyarwandaKirghizKnockout target:KomiKongoKoreanKuanyamaKurdishLanguageLaoLatinLatvianLay down to the leftLayerLayersLayout:LeftLet this conversation playLevel 1: EasyLevel 2: HardLevel 3: ExpertLight Start/End (deselected)Light Start/End (selected)Light TextureLight Vertices (deselected)Light Vertices (normal)Light Vertices (selected)Light VolumeLight VolumesLight flagsLight propertiesLimburganLinesLingalaList visible nodes onlyLithuanianLoad in Textures viewLoading ShadersLoading entity {0:d} Loading mapLoading texturesLoading textures...Loading, please wait...Loading...Location Entity:Location:Logic for Difficulty Level {0:d}Luba-KatangaLuxembourgishM&apMD5 Animation ViewerMacedonianMagnitude:Major Grid StyleMake &HollowMake &RoomMake DetailMake Objective {0} invisibleMake Objective {0} mandatoryMake Objective {0} not mandatoryMake Objective {0} visibleMake RoomMake StructuralMake VisportalMalagasyMalayMalayalamMalteseMandatoryManipulateManxMaoriMapMap InfoMap Info File Version invalidMap info...Map loading cancelledMap writing cancelledMarathiMarshalleseMatchMatching skinsMaterialMaterial surfacesMaterial:MatrixMax Fire Count:Max shadername lengthMax total Snapshot size per map (MB)Max. Interleave DistanceMax:Maximum Chase Mouse SpeedMeasureMediaMedia BrowserMelee RangeMerge EntitiesMerge SelectionMerge selected EntitiesMerging failed, because the length of the definition to be overwritten could not be retrieved.Min. Interleave DistanceMin:Minimum Alert Level:Minor Grid StyleMirrorMirror &XMirror &YMirror &ZMissionMission InfoMission Info Editor (darkmod.txt)Mission ObjectivesMission ReadmeMission Readme Editor (readme.txt)Mission Titles:Mo&difyMod (fs_game)Mod Base (fs_game_base)Mod Base path not defined, neither is Mod path. Using Engine path...Mod Base path not defined. Using Mod path...Mod Base/xdataMod path not defined. Using Base path...Mod/xdataModelModel DefinitionModel ExportModel PathModel ScalerModel nameModelsModels used:ModifierModify Texture:Modules initialisedMongolianMore Dotted LinesMore cylindersMouse Bindings...Mousewheel IncrementMove DownMove UpMove downMove player start hereMove to Layer...Move upMovement Speed (game units)MultiplyNameName ConflictName of single entityName:Named Skins used:NaturalNauruNavajoNdebele, NorthNdebele, SouthNdongaNepaliNewNew ActorNew ConversationNew MapNew XY viewNew objective {0:d}NewFilterNext (XY, XZ, YZ)Next leak spotNo brushes selected.No faces selected.No filename specified, cannot run exporterNo game type selected.No import summary available. An XData definition has to be imported first...No import summary available. Browse Gui Definitions first.No patches selected.No rules defined for this filter, cannot insert.No rules defined for this filter. Delete it?No scripts availableNo shaderNo specifierNode CountNoise:NoneNormaliseNorthern SamiNorwegianNot a suitable Gui Definition!Note: Please beware that deleting custom stims may affect other entities as well. So check before you delete.Note: This shortcut is already assigned to:Note: be careful when using the CSG tool, as you might end up with an unnecessary number of tiny brushes and/or leaks. This popup will not be shown again.Note: changes will be written to the file .....Note: changes will be written to the file {0}Nothing must be selected for model creationNothing selected, cannot group anythingNothing selected, cannot run exporterNothing selected, cannot un-group anythingNudgeNudge DownNudge LeftNudge RightNudge UpNumber of Pages:Number of brushes/patches/entities in this map (Number of selected items shown in parentheses)Number of most recently used filesNumber of sides: OKObject is destroyedObjective ConditionsObjective EntitiesObjectivesObjectives...OccitanOffset cloned objects by one grid unit to the right and downwards.Offset:OjibwaOmniOne-Sided Readable GuisOne-sidedOngoingOpen FileOpen MapOpen a map fileOpen last map on startupOpen mapOpenGL ExtensionsOpenGL PropertiesOperation cancelled by userOptimizationOptional. Fill in if this package is a campaign containing several mapsOptionsOptions:Order of the elements Title/Description/Author/etc. is incorrectOrientation:OriyaOromoOrthographicOssetianOther MaterialsOutput FileOutput Format:Output Path:OutwardOverall (component-specific)Overwrite existing shortcut?Page EditingPage Index out of bounds.Page Number:Pageturn Sound:PaliPan Camera ViewPan image with viewportPanjabiParallelParsed invalid value '{0}' for key '{1}'Parsing material file {0}ParticleParticle DefinitionsParticle EditorParticle Editor...Particle StagesPaste ShaderPaste Shader (Natural)Paste Shader NamePaste Shader NaturalPaste Shader ProjectedPaste Shader to all Brush FacesPaste Spawnarg(s)Paste Texture CoordinatesPaste to CameraPatchPatch Control VerticesPatch Corner VertexPatch Inner VertexPatch InspectorPatch Subdivide ThresholdPatch TesselationPatch ThickenPatchesPatches:PathPath Type:PatrolPersianPick ShaderPicking TexuresPlayPlay and loopPlayer pickpockets AIPlayer possesses itemPlayer responsiblePlease rename your XData definition, so that it is stored under a different filename.Please select a game typePlease select a new button/modifier combination by clicking on the area below.Please specify an XData name first!Point CameraPolishPolysPortuguesePre&ferences...PrefabPrepend PagePreprocessingPrevious leak spotPrimitive #{0:d}: parse errorPrimitive #{0:d}: parse exception {1}Prism...Problems during importProcessing line {0}...Processing...ProjectedPropertiesPropertyPushtoPut caps on the current patchPython Script InputQuechuaRadial Speed:RadiusRadius changes over time to:Random Effects:Randomness:RankRate:Re-disperseReadable EditorReadable is closed.Readable is opened.Readable:Really clear all bindings and reload them from the default settings?Recently used MapsRectangularRefreshRegionReload AAS FileReload DefsReload MaterialsReload ModelsReload Particle DefsReload ParticlesReload Readable GuisReload S&kinsReload ScriptsReload Selected ModelsReload SkinsReloading DefsReloading GuisReloading ModelsRemoveRemove Curve Control PointsRemove PageRemove Stim TypeRemove from Layer...Remove selected BrushRemoves the selected curve control pointsRename LayerRename this layerRenderer:Renderer: {0}Rep&arent PrimitivesReparent Primitives to &WorldspawnReparent primitivesReplace Selection with exported ModelReplace:Required TDM Version:Rescan Prefab FoldersReset all mappings to defaultReset to DefaultReset to default?ResizeResponse EffectsResponsesRestart DarkRadiant to apply changesRestart requiredRevert part to worldspawnRevert to worldspawnRightRing Size:RomanianRomanshRotateRotate Objects independentlyRotate XRotate YRotate ZRotate and scale...Rotate func_* Entities around originRotationRotation SpeedRotation Speed:Rotation:Row:RowsRulesRun ScriptRundiRussianS/RSamoanSangoSanskritSardinianSatisfied at startSaveSave &as...Save © as...Save ®ion...Save ChangesSave Copy As...Save FileSave MapSave SnapshotsSave and CloseSave changes to map "{0}" before closing?Save selected as Collision Model...Save selected as PrefabSave the current mapSaving MapScaleScriptScript Function:Search current selection onlySearch for AAS FilesSearching for ModulesSeek in Media BrowserSelectSelect &touchingSelect .prt fileSelect ChildrenSelect EdgesSelect EntitiesSelect EntitySelect FacesSelect Group PartsSelect Related ItemsSelect VerticesSelect Vocal Set...Select all of typeSelect complete t&allSelect complete tallSelect elements using this shaderSelect i&nsideSelect insideSelect more than one element to form a groupSelect new Binding: {0}Select recently used path:Select touchingSelected Group ItemsSelected ItemsSelected Items (Camera)Selection Set: SentenceSerbianSet InvisibleSet State on Objective {0} to {1}Set VisibleSet from &BrushSet from &XY viewSet from Se&lectionSet mandatory flagSet state to {0}SettingSettings/Settings/AutosaveSettings/CameraSettings/ClipperSettings/CompatibilitySettings/GridSettings/LanguageSettings/Map FilesSettings/Model ExportSettings/Multi MonitorSettings/OrthoviewSettings/PatchSettings/PrimitivesSettings/Readable EditorSettings/SelectionSettings/Texture BrowserSettings/Undo SystemShaderShader:ShaderClipboard is empty.ShaderClipboard: {0}ShadersShaders used:Shape:ShiftShonaShortcut ListShowShow &AnglesShow &NamesShow Area NumbersShow AxesShow BlocksShow C&oordinatesShow CoordinatesShow CrosshairsShow Entity Angle ArrowShow Entity NamesShow GridShow Gui import summaryShow Shader DefinitionShow Size InfoShow Texture FilterShow Window OutlineShow WorkzoneShow allShow camera toolbarShow coordinate axesShow duplicated definitionsShow helpShow hiddenShow inherited propertiesShow last XData import summaryShow size infoShow wireframeShow/hide all light volumesShow/hide all speaker volumesShows the mouse position in the orthoviewSichuan Yi; NuosuSindhiSinhalaSitting AngleSize / SpeedSize:SkinSkin nameSkin: SkinsSkip Surfaces textured with CaulkSkip diffuse lightingSkip specular lightingSlovakSlovenianSnap Rotation Pivot to GridSnap selected to gridSnap selection to gridSnap to GridSnapshot Folder Size WarningSnapshot folder (relative to map folder)Solid selection boxesSomaliSorry. Patch is not suitable for this kind of operation.Sotho, SouthernSound FilesSoundshaderSource Mission:Source Objective State:Source Objective:SpanishSpawnarg:Speed:Sphere Radius:Sphere...SphericalSplit SelectionSquaresStageStage SettingsStandardStartStart DarkRadiant on monitorStart a new Mission named {0}?Start in Alert Idle StateStart sittingStart sleepingStart the Gui Browser?StateStep:Stim/Response EditorStim/Response...StimsStitch Patch TexturesStopSuccess LogicSuccess Logic:Sucess LogicSundaneseSurface InspectorSurround with monsterclipSurround with monsterclip brushSwahiliSwatiSwedishSwitching to default Gui...TagalogTahitianTajikTamilTarget Objective:TatarTeamTeluguTexture BackgroundTexture BrowserTexture LockTexture OperationsTexture PropertiesTexture ToolTexture lockTexture scrollbarTexturesThaiThe FM folder name of the mission you want to work on, e.g. 'saintlucia'.The actor {0} cannot be found in the current map.The definition {0} already exists. Should it be imported?The engine executable "{0}" could not be found in the specified folder. The mission path "{0}" does not exist. The mission path {0} doesn't exist. Do you intend to start a new mission and create that folder?The mod base path "{0}" does not exist. The mod path "{0}" does not exist. The name of the shader in the clipboardThe name {0} already exists in this map!The requested definition has been found in multiple Files. Choose the file:The selected elements already form a groupThe selected elements aren't part of any groupThe snapshots saved for this map are exceeding the configured size limit. Consider cleaning up the folder {0}The specified Definition does not exist.The specified file can't be opened.The specified file doesn't exist.The specified gui definition is not a readable.The specified gui definition is not suitable for the currently chosen page-layout.The specified shortcut is already assigned to {0} Overwrite the current setting and assign this shortcut to {1} instead?There are no duplicated definitions!These logics override the standard logic for the given difficulty level if the logic string is non-empty.Thicken Selected PatchesThicken selected PatchesThickness (units):This Is Not Dromed WarningThis command is not available in component mode.This condition is not valid or complete yet.This default setting is overridden, cannot edit.This is the path where your TheDarkMod.exe is located.This is the standard logic for all difficulty levelsThis name already exists.This name is already in use.This page has been moved! Please use the game settings dialog in the menu: File > Game/Project Setup...This will delete all set definitions. The actual map objects will not be affected by this step. Continue with that operation?TibetanTigrinyaTime Offset / sec:Time interval:Time:Timer reloadsTimer restarts after firingTimer waits for start (when disabled: starts at spawn time)TitleTitle:To avoid duplicated XData definitions the current definition has been renamed to {0}.To replace the selection with the exported model the output path must be located within the mod/project.To:Toggle GridToggle VisibilityToggle layer visibilityTongaToolToolsTopTop LeftTop RightTotal polysTotal verticesTrails:TranslateTranslate Manipulator always constrained to AxisTransparencyTransposeTsongaTswanaTurkishTurkmenTwiTwo entities are within a radius of each otherTwo-Sided Readable GuisTwo-sidedTypeType:UighurUkrainianUnable to create Objective Entity: class '{0}' not found.Unable to create Objective Entity: classes not defined in registry.Unable to create conversation Entity: class '{0}' not found.Unable to create entity {0}, no brushes selected.Unable to create light, classname not found.Unable to create model, classname not found.Unable to create speaker, classname not found.Unable to parse map version (parse exception).Unable to setup the preview, could not find the entity class {0}Undo Queue SizeUngroup SelectionUngroup selected itemsUniform texture thumbnail size (pixels)UnknownUpUpdate Views on Camera MovementUpward Bias:UrduUse Entity ColourUse Entity Origin as export OriginUse World GravityUse background imageUse boundsUse start/endUzbekV&iewValidate allValueVelocity:VendaVendor:Vendor: {0}Verbose logging.Version:Version: {0:d}.{1:d}.{2:d}Version: {0}Vert. Scale:Vert. Shift:Vertical FOVVertical offsetVertical:VietnameseViewView FilterView Shader DefinitionView chases Mouse Cursor during DragsVisibleVisual AcuityVocal Set: VolapuekWaitWait until finishedWalloonWarning: {0} Do you want to correct these settings?Warning: Warning: Camera not within region, can't set info_player_start.WelshWestern FrisianWhen picking texture names, click the pick button and use {0} in the camera view to pick a material name. The picked texture will be filled in the entry box next to the activated button.Width:Window LayoutWolofWorkzoneWriting mapWriting node {0:d}XX Size:X-AxisX-Axis Rotate:X-Axis Scale:X: XData Name:XData Storage FolderXData has been renamed.XData import summaryXY (Top)XY TopXY ViewXYView CrosshairsXZXZ FrontXdata PathXhosaYY Size:Y-AxisY-Axis Rotate:Y-Axis Scale:YZYZ SideYiddishYorubaYou didn't choose a Gui. Using the default Gui now.You have imported an XData definition that is contained in a PK4, which can't be accessed for saving.ZZ Size:Z-AxisZ-Axis Rotate:Z-Axis Scale:ZhuangZoom ViewZoom image with viewportZuluclipPlaneInButtonclipPlaneOutButtoncreateNodeForEntity(): cannot create entity for NULL entityclass.disabledenabledentitiesentityentity flatShadeBtngridButtonhhidehttp://www.darkradiant.net/ This product contains software technology from id Software, Inc. ('id Technology'). id Technology 2000 id Software,Inc. DarkRadiant is originally based on the GPL version of GtkRadiant. The Dark Mod (www.thedarkmod.com)lightingBtnlightingModeButtonmmsnextButtonnoopaquepauseTimeButtonprevButtonsseconds:showstartTimeButtonstopTimeButtontexturedBtntexturedModeButtontimestimes at maximumtransparentunnamed.mapwireframeBtnwxWidgets Propertiesx shader(s) replaced.x-axis Mirrorx-axis Rotatex: {0:6.1f} y: {1:6.1f} z: {2:6.1f}y-axis Mirrory-axis Rotateyesz-axis Mirrorz-axis Rotate{0:d} faces were not suitable (had more than 4 vertices).{0:d} loot{0:d} loot in gold{0:d} loot in goods{0:d} loot in jewels{0:d} minutes{0:d} models loaded{0:d} of "{1}"{0:d} seconds{0:d} shader(s) replaced.{0} AI of team {1}{0} AI of type {1}{0} AI of {1}{0} Settings{0} at [ {1} ]{0} entities replaced.{0} has been defined in:{0} models replaced.{0} of spawnclass {1}{0} of type {1}{0} shaders replaced.{0} spawnargs replaced.{0} successfully imported.{0}{1} already exists in another path. XData will be stored in {2}{3}!Project-Id-Version: DarkRadiant Report-Msgid-Bugs-To: greebo@angua.at POT-Creation-Date: 2017-12-17 17:20+0100 PO-Revision-Date: 2017-12-17 17:20+0100 Last-Translator: codereader Language-Team: The Dark Mod MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Poedit-KeywordsList: _;gettext;gettext_noop X-Poedit-Basepath: . X-Poedit-Language: German X-Poedit-SearchPath-0: ..\..\..\..\radiant X-Poedit-SearchPath-1: ..\..\..\..\libs X-Poedit-SearchPath-2: ..\..\..\..\plugins (außer Kraft)Y:Z:#Über DarkRadiantAusgewählte Entities verknüpfenAbbrechenZentrierenAusgewählte Entities verbinden&KopierenKurve&Deaktiviere alle FilterEin Stockwerk tiefer&DuplizierenEdgesBearbeitenAuswahl als Model exportieren...Faces&DateiSpiel/Projekteinstellungen...RasterHilfe&Map importierenAuswahl umkehrenLight Inspector&Neue MapOK&Öffnen&EinfügenPatchPatch InspectorPointfileWiede&rherstellen&Modelle neu ladenGruppe zu Worldspawn umwandeln&Speichern&Auswahl als Map speichern...Auswahl als &Prefab speichern...Surface InspectorAusschaltenTextur-WerkzeugRückgängigEin Stockwerk höherVerticesXY 100%XY EinzoomenXY Auszoomen(Zeile {0}): {1}(option, z.B. "2.03")-1Fehler: Datei nicht gefunden.Hinweis: Du musst DarkRadiant neu starten, damit die neue Spracheinstellung aktiv wird.?Es existiert bereits ein Schema mit diesem Namen.Eine bestimmte Seite eines Schriftstücks wird angezeigt.Eine benutzerdefinierte Komponente benötigt keine Spezifikatoren, der Zustand der Komponente wird anderweitig gesteuert (z.B. durch Skript oder einen Trigger).Um Stim/Response-Eigenschaften zu editieren muss genau eine Entity ausgewählt sein.AAS VisualisierungAAS VisualisierungAIAI kann kalt erwischt werdenAI kann ertrinkenAI außerhalb des Spieler-PVS denkt nichtAI findet einen KörperAI findet einen GegenstandAI wird alarmiertAI ist ZivilistAI ist immun gegen GasAI ist immun gegen KOsAI ist gegen KOs immun (auf hohem Alert-Level)AI wird getötetAI wird KO geschlagenAI:FähigkeitenAbchasischÜber DarkRadiantAktionWert:Aktion:AktivierenAktiviere &alle FilterAktivierungs-Timer:AktivName der aktiven AnsichtAkteurAkteur (zum Bearbeiten klicken)Akteur {0:d}Akteur {0:d} ({1})AkteureAkteure sind OKAkteure sehen sich beim Sprechen gegenseitig anAkteure fehlenAkteur müssen in Sprechweite seinAddierenStimtyp hinzufügenTitel hinzufügen:Neuen Effekt hinzufügenEigenschaft hinzufügenEigenschaft hinzufügen...Zu Layer hinzufügen...AfrikaansAusgerichtetAkanAlbanischTextur ausrichten:Alle DateienAlle Dateien (*.*)All StufenAlle Akteure beziehen sich auf gültige Entities der aktuellen Map.Alle SkinsAltAmharischAnzahl:Analysiere GUIsPatrol-Modus für TiereAnimationAnimation:Irgendeine AI des angegebenen TypsIrgendeine AI des angegebenen TeamsIrgendeine AI mit dem angegebenen KampfzustandIrgendeine Entity der angegebenen KlasseIrgendeine Entity der angegebenen SDK KlasseErscheinungsbildAnfügenZwei Spalten am Anfang anfügenZwei Spalten am Ende anfügenZwei Zeilen am Anfang anfügenZwei Zeilen am Ende anfügenKurvenkontrollpunkt anfügenSeite anfügenEinen Kurvenkontrollpunkt zur ausgewählten Kurve hinzufügenAuf Auswahl anwendenAnwenden...ArabischAragonesischFreie TransformationArgument:ArgumenteArmenischSeitenverhältnis:AssamesischZuweisenFür diese Operation muss mindestens ein Brush ausgewählt sein.AttributHörschärfeAutor:Auto LoopSpeicherintervallVerfügbare AnimationenVerfügbare Sätze:AwarischAvestischAxiale Geschw.:AymaraAserbaidschanischBrushHintergrundbild...HintergrundbildBambaraBaschkirischBaskischVerhaltenBelarussischBengalischBogenBeachte, dass andere Entities diesen Stimtyp noch verwenden könnten. Möchtest du diesen benutzerdefinierten Stim wirklich löschen?Große PunkteBihariBislamaText:Kann geschultert werdenText:NegierenBosnischBeide Fit-Werte müssen größer als 0 sein.UntenUnten linksUnten rechtsBounds Expansion:Boundingbox:BretonischAnimationen durchsuchenSound Shader durchsuchenPfad durchsuchen:Mod-Ressourcen durchsuchenBrushnummer:Brush-GrößeninformationBrush-VerticesBrushDef3Parser: ungültiges Token '{0}'BrushDef3ParserQuake4: ungültiges Token '{0}'BrushDefParser: Ungültiges Token '{0}'BrushesBrushes:Brushes: {0:d} ({1:d}) Patches: {2:d} ({3:d}) Entities: {4:d} ({5:d})Kompilierungsdatum: %sKompilierungsdatum: {0}Baue Hierarchie auf...BulgarischPatch auswölbenPatch auswölbenHäufung:BurmesischSchaltflächeAuswahl aufhebenC:\Games\Darkmod\fms\gathersERFÜLLTCSGCSG ZusammenfügenCSG SubtraktionCSG ZusammenfügenCSG Zusammenfügen: Keine Brushes ausgewählt.CSG SubtraktionCSG Subtraktion: Keine Brushes ausgewählt.KameraKamera-HintergrundKamera-SymbolKamera-PositionKameraKann andere grüßenKann Fackeln anzündenKann Türen bedienenKann Lifte bedienenKann Lichtschalter betätigenKann suchenKann Kontrollpunkt nicht anfügen, keine Kurven ausgewählt.Kann Kurve nicht konvertieren, keine Kurven ausgewählt.Konnte Face nicht casten, Shader wurde nicht kopiert.Konnte Patch nicht casten, Shader wurde nicht kopiert.Shader nicht kopiert: bitte ein einzelnes Face oder einen Patch auswählen.Kann Kontrollpunkte nicht einfügen, bitte vorher in den Vertex-Modus wechseln.Kann Kontrollpunkte nicht einfügen, keine Kurven ausgewählt.Es ist nicht möglich, Texturkoordinaten von Faces zu kopieren.Es ist nicht möglich, Texturkoordinaten von Patches auf Faces zu kopieren.Kann Texturkoordinaten nicht kopieren, da die Patch-Dimensionen nicht passend sind.Kann Shader nicht auf gesamten Brush anwenden, das Ziel ist kein Brush.Kann Kontrollpunkte nicht entfernen, bitte vorher in den Vertex-Modus wechseln.Kann Kontrollpunkte nicht entfernen, keine Kurven ausgewählt.AbbrechenKann diesen Filter nicht hinzufügen, ein weiterer Filter mit demselben Namen existiert bereits.Kann Patch nicht auswölben: keine Patches ausgewählt.Der Klassenname der Worldspawn-Entity darf nicht geändert werden.Der Klassenname darf nicht auf worldspawn gesetzt werden.Kann Auswahlsatz nicht anlegen, keine Auswahl vorhanden.Kann keine Bevel-Caps erzeugen, Patch muss eine Breite von 3 habenKann keine Caps erzeugen, da keine Patches ausgewählt sind.Kann keine Zylinder-Caps erzeugen, Patch muss eine Breite von 9 habenKann keine End-Caps erzeugen, Patch muss eine Breite von 5 habenEs ist nicht möglich eine Ebene ohne Namen zu erstellen.Es ist nicht möglich einen Partikel ohne Namen zu erstellen.Kann Auswahlsatz nicht erstellenKann Entities nicht zusammenführen, die Auswahl muss ausschließlich aus func_*-Entities bestehen. (Die zuerst ausgewählte Entity bleibt dabei erhalten.)Kann die Datei nicht im Lesemodus öffnen: {0}Kann die Datei nicht im Schreibmodus öffnen: {0}Kann die ausgewählten Primitive nicht neu zuordnen. Bitte zumindest einen Brush/Patch sowie genau eine Entity auswählen. (Die Entity muss als letztes ausgewählt worden sein.)Schriftstück-Editor kann für diese Auswahl nicht ausgeführt werden. Bitte eine einzelne XData-Entity auswählen.Kann Partikeldefinition nicht speichern, sie wurde nicht registriert.Kann nicht mit 0 skalieren.Der Klassenname darf nicht leer seinKann Texturen nicht zusammenheften, für diese Operation müssten genau 2 Patches ausgewählt sein.Kann Texturen nicht zusammenheften. Konnte Nodes nicht casten.Nichts ausgewählt: kann Patch nicht extrudieren.Cap für Auswahl erstellenKatalanischName des Caulk ShadersObjekte um den Ursprung anordnenPivotpunkt beim Skalieren von Faces zentrierenKhmerChamorroWahrscheinlichkeit:Setze die 'obligatorisch'-EigenschaftSetze Status eines MissionszielsSetze Misionsziel auf sichtbar/unsichtbarAnsicht wechselnWenn die "model"-Eigenschaft dieser Entity auf den gewählten Wert festgelegt wird, werden alle ihr untergeordneten Primitive gelöscht: TschetschenischChichewaChinesischKopf für AI auswählenSprachsatz für AI auswählenKopf für AI auswählen...Verzeichnis auswählenDatei auswählenModell auswählenPrefab wählenShader auswählenSkin auswählenSprachsatz für AI auswählenGUI-Definition auswählen...Datei auswählen...XData-Definition auswählen...Entityklasse auswählenModell auswählen...Partikel auswählen...Partikel auswählenSkin auswählen...Sound auswählenSound auswählen...Ziel-Entity auswählen...Textur auswählenTextur auswählen...TschuwaschischKlassennameKlassenname:LöschenAuswahlsätze löschenNicht obligatorisch machenHier klicken um neue Belegung zuzuweisenKlicken um alles in der Ebene auszuwählen, UMSCHALT beim Klicken gedrückt halten um Auswahl aufzuheben, STRG halten um die Ebene als aktive Ebene festzulegen.Auswahl clippenClipperClipper verwendet Caulk-Textur für neu erzeugte Faces.Zeitintervall:SchließenSchließen ohne zu speichernFarbeFarbe:Farben...Spalte:SpaltenBefehlBefehlsargumenteBefehlseigenschaftenBefehleFertigSkript bei ErfüllungTarget bei ErfüllungKomponentenBedingungBedingung betreffend Missionsziel {0:d}KegelÖffnungswinkel:Kegel...Ebenenlöschung bestätigenKonsoleKonsoleBitte sieh in der Import-Zusammenfassung nach, um weitere Informationen zu erhalten.Konversations-EditorKonversations-EntitiesKonversationenKonversationen...Konversion NURBS <-> CatmullRomKonvertiert die ausgewählte Kurve NURBS <-> CatmullRomIn func_static umwandelnKoordinatenKopierenFarbschema kopierenShader kopierenWertepaar(e) kopierenKopiere ShadernamenKornischKorsischKonnte Verzeichnis nicht anlegenKonnte Patch nicht erzeugen.Konnte das Format der Mapdatei nicht bestimmen: {0}Fehler beim Öffnen der Output-StreamsKonnte das Pointfile nicht öffnen: {0}Konnte die Mapdatei nicht lesen: {0}Nicht erkennbares VersionsnummernformatKann die Datei nicht entfernen: {0}Konnte Ebene nicht umbenennen, bitte nochmals versuchen.Konnte die bestehende Datei nicht auf .bak umbenennen: {0}Konnte die temporäre Datei nicht umbenennen: {0}Konnte die temporäre Datei nicht umbenennen: {0}Konnte Region nicht festlegen, weil die XY-Ansicht nicht gefunden wurde.Kann keine Region festlegen: keine Elemente ausgewählt.Kann Region nicht festlegen: bitte einen einzelnen Brush auswählen.Konnte {0} Datei nicht speichern: {1}AnzahlAnzahl / ZeitAnzahl:Bogen erstellenPatch-Cap erzeugenCatmullRom-Kurve erstellenKegel erstellenZylinder erstellenDecal-Patches erstellenDecal-Patches für ausgewählte Faces erstellenErstelle "dichten" ZylinderEndcap erstellenNeuer flacher PatchEine Gruppe aus diesem Prefab erstellenNeuer Layer...NURBS-Kurve erstellenUmschließen (erzeuge "Seitenwände")Neuer einfacher PatchKugel erzeugenErstelle quadratischen ZylinderErstelle "sehr dichten" ZylinderNeue EntityNeue Entity...Neues Licht...Neues Modell...Erzeuge Spielerstartpunkt hierNeuer einfacher PatchNeuer Speaker...NURBS-Kurve erstellenErzeuge eine neue XData-Definition...CreeKritischer GesundheitswertKritischer Fehler: kann ausgewählte Entities nicht finden.KroatischKreuzeStrgFar-Clip-Ebene verwendenAktuelle RastergrößeAktuelle Seite:Benutzerdefinierter OrdnerBenutzerdefinierter StimBenutzerdefinierte StimsBenutzerdefinierte Eigenschaften dieser Entityklasse, falls definiertBenutzerdefiniertes SkriptBenutzerdefiniertes Skript wird periodisch abgefragtWertepaar(e) ausschneidenFace-Auswahl durchrotierenAuswahl durchrotierenDurchläufe:ZylinderZylindergröße X:Zylindergröße Y:Zylindergröße Z:ZylindrischTschechisch&LöschenDänischDarkMod VerzeichnisDarkRadiant ModuleDarkRadiant EinstellungenDarkRadiant x.y.zDeaktivierenTotzeit / Sek.:Rastergröße verkleinernStandardStandard-RastergrößeStandard-LogikStandardskalierung:Standard-TexturskalierungDefiniert inDefiniert in:Definition:LöschenZwei Spalten am Anfang löschenZwei Spalten am Ende löschenZwei Zeilen am Anfang löschenZwei Spalten am Ende löschenBenutzerdefinierten Stim entfernenTitel löschenAlle Auswahlsätze löschen?Links löschenRechts löschenEigenschaft entfernenDiese Ebene löschenGanze Seite löschenDepth Hack:Listet verfügbare Mauskommandos aufBeschreibungBeschreibung:Alle Elemente mit diesem Shader deselektierenDetails (doppelklicken zum Editieren)Schw.SchwierigkeitsgradSchwierigkeitsstufen-EditorLogik für bestimmte SchwierigkeitsstufeSchwierigkeitsstufe...Richtung / OrientierungRichtung:"Alarmierter" Idle-Zustand nicht erlaubtDesktopgestaltung deaktivierenKeine Soundwiedergabe in dieser SessionSchrittweise Bewegung (freier Umsichtsmodus aus)Distanz:Verteile Polygone zufällig im angegebenen VolumenVerteilungDhivehiWirft keine Schatten (schnell).Willst Du diese Ebene wirklich löschen?Willst Du die Import-Zusammenfassung anzeigen?Änderungen am Partikel {0} speichern?PunkteGepunktete LinienDoppelklicken um eine Belegung zu ändernNach untenKamera verschiebenAnsicht verschiebenAuswahl-RechteckErzeuge neue BrushSymmetrisches Verändern von Entities beim Ziehen (Ursprung bleibt unverändert)BetrunkenSinnesschärfen-Faktor bei TrunkenheitDuplizierenMehrfach vorkommende XData-DefinitionenDauer / Sek.:Dauer:DänischDzongkhaEntityB&eendenBearbeitenFarbschemas bearbeitenBefehl bearbeitenKonversation bearbeitenFilter bearbeitenFilter bearbeiten...Missions-Logik bearbeitenLogik für Erfüllung bearbeitenMaustastenbelegungen ändernZiel editieren:Missionsziel-Bedingungen bearbeitenMissions-Info editieren (darkmod.txt)...Response-Effekt editierenBearbeite readme.txt Datei...EffektEffekt:Leerer DateinameLeerer FilterKeine AuswahlEntitiesAktiviere automatisches SpeichernTextur-Lock aktivieren (für Brushes)Aktiviere hintere Clip-Ebene (ferne Objekte werden nicht angezeigt)Vorausgesetzte ZieleDoppelbogenPfad zur EngineEngine-Pfad "{0}" existiert nicht. EnglischEbenennamen eingebenNamen eingebenAnzahl der Seiten festlegenPartikelnamen eingebenNamen eingaben und mit der EINGABETASTE bestätigen um einen neuen Satz anzulegen. Ein Element in der Liste auswählen um die entsprechende Auswahl wiederherzustellen. UMSCHALTTASTE gedrückt halten um die Auswahl der entsprechenden Elemente aufzuheben.Namen für das neue Schema eingeben:Neuen Ebenennamen eingebenNeue Tastenkombination festlegenEntitiesStandard-EntitiesEntities:EntityEntityklasseEntity-KlassenbaumEntity-Klassenbaum...Entity InspectorEntity KeyEntity-ListeEntitynummer:Entity {0}Entity {0}, Primitiv {1}Entity:Fehler beim Speichern der Partikeldefinition: {0}Fehler: Datei nicht gefunden.Fehler: Entityklassenzeiger ungültig.Aufgetretene Fehler:EsperantoEstnischEweFür diese Operation müssen genau zwei Entities ausgewählt sein.Ausnahmefehler:DarkRadiant beendenAuswahl auf Geschwister ausweitenExportExport-Format für skalierte ModelsExport-OptionenExport fehlgeschlagenExportiere RegionAuswahl exportierenExtrusion entlang der Vertex-NormalenExtrusion entlang der X-AchseExtrusion entlang der Y-AchseExtrusion entlang der Z-AchseFEHLGESCHLAGENFPSFaceFacesFade-FarbeFade In Fraction:Fade Index Fraction:Fade Out Fraction:Fehler beim Parsen der Entity {0:d} {1}Konnte das Verzeichnis {0} nicht anlegenImport von {0} fehlgeschlagen.Laden der GUI-Definition {0} fehlgeschlagen.Konnte {0} nicht zum Speichern öffnen.Import der darkmod.txt-Datei fehlgeschlagen: {0}Import der readme.txt-Datei fehlgeschlagen: {0}Logik für FehlschlagLogik für Fehlschlag:Skript bei FehlschlagTarget bei FehlschlagFehler beim Import.Fehler beim Öffnen der Datei: {0}Fehler beim Lesen der Mapdatei: {0} {1}Fehler beim Lesen der Mapdatei aus der Zwischenablage: {0}Fehler beim Aufruf des Pre-Save Events: {0}Falloff-Exponent:Entfernte Clip-Ebene heranEntfernte Clip-Ebene hinausFäröischFidschiDateiDateipfad:Datei ist schreibgeschützt: {0}Datei nicht lesbarFiltereinstellungenFilterregeln werden in der angegebenen Reihenfolge angewandt. Das Ausdruck-Feld akzeptiert reguläre Ausdrücke (RegEx). Für Filter vom Typ Objekt können die Schlüsselwörter patch oder brush verwendet werden.FilterSuchen & ErsetzenShader Suchen & ErsetzenSuche BrushSuchen & ErsetzenTexturen suchen & ersetzen...Brush suchen...Suchen:FinnischEinpassenTexture einpassen:Fixe TesselierungFixup-DateiMap-FixupMap-Fixup...Fixup ErgebnisseFixup abgebrochenFixup läuftFlagsFliegenClip-Orientierung umkehrenHorizonal kippenAuswahl Horizontal kippen (S-Achse)Auswahl vertikal kippen (T-Achse)Textur kippen:Vertikal kippenAuswahl auf den Boden setzenKamera auf ausgewählte Entity richtenFrames:Umschalten von/zu Umsichtsmodus erlaubenUmsichtsmodusFranzösischVon:FulahGUI-Definition:GUI-Seitenindex ungültigGälischGalicischeSpielSpiel-EinstellungenSpiel-Typ:GameManager: kein Spieltyp ausgewählt, kann daher nicht fortfahren.GameManager: keine gültigen Spieldateien (.game) gefunden, kann nicht fortfahren.LugandaAllgemeine EigenschaftenGeorgischDeutschLosSchwerkraft:GriechischRasterhintergrundRasterblockGrobes RasterFeines RasterRastertextGrid0.125Grid0.25Grid0.5Grid1Grid128Grid16Grid2Grid256Grid32Grid4Grid64Grid8Gruppe erstellenGruppenidentifizierer (komponentenspezifisch)Ausgewählte Elemente gruppierenGruppen können nur im Primitiv- und Gruppenteil-Selektionsmodus aufgelöst werdenGruppen können nur im Primitiv- und Gruppenteil-Selektionsmodus erstellt werdenGuaraníGUI PfadGUI ImportzusammenfassungGujaratiHaitianischHausaKopf:GesundheitGesundheit / KampfHebräischHöhe:HelixHereroNicht ausgewähltes verbergenAuswahl verbergenNicht Benutztes verbergenAlle ausblendenWeit entfernte ausblendenVerbergen/ZeigenHöhere Auswahlpriorität für EntitiesHindiHiri MotuAushöhlenHoriz. Skalierung:Horiz. Verschiebung:Horizontaler FOVHorizontale VerschiebungHorizontal:UngarischNICHT ERFÜLLTUNGÜLTIGIsländischIdoWenn das Missionsziel {0} in Mission Nr. {1} den Status '{2}' hat:Wenn Du nicht sicherst, gehen die Änderungen der letzten {0} verloren.IgboIgnorierenIgnoriere Lichtvolumina für die Berechnung des Standard-PivotpunktsBilddateiBildBildskalierung&Prefab importierenDefinition importieren?Import fehlgeschlagenImport fehlgeschlagen:Map importierenImportiere...Ungültige Map-Version: erwartet: {0:f}, gefunden: {1:f}Rastergröße vergrößernIndexZeigt an, ob irgendetwas aus der momentanen Auswahl Teil dieser Ebene ist.IndonesischAnfangszustandAnfangswinkel:Initialisiere Modul: {0}Initialisiere ModuleEinfügenZwei Spalten am Anfang einfügenZwei Spalten am Ende einfügenZwei Zeilen am Anfang einfügenZwei Zeilen am Ende einfügenKurvenkontrollpunkt einfügenSeite einfügenLinks einfügenRechts einfügenPrefab einfügen...Ganze Seite einfügenFügt einen Kurvenkontrollpunkt vor dem ausgewählten Punkt einInterlinguaOccidental-InterlingueInuktitutInupiaqUngültige EinstellungenName im Inventory:InvertierenInvertiere vertikale Mausachse (freier Umsichtsmodus)Invertierter BogenInvertierter DoppelbogenIrischIrreversibelItalienischGegenstand ist am/im Ort (info_location)Gegenstand ist am/im OrtGegenstand:JapanischJavanischZu Objekt springenGrönländischKanaresischKanuriKashmiriKasachischSeitenverhältnis beibehaltenSchlüsselTastenkombinationen...KikuyuTötungs-Ziel:KinyarwandaKirgisischKO-Ziel:KomiKongoKoreanischKwanyamaKurdischSpracheLaoLateinischLettischNach links hinlegenEbeneEbenenLayout:LinksDiese Konversation wird maximalLevel 1: LeichtLevel 1: HartLevel 1: ExperteLicht-Anfangs- und End-Vertices (nicht ausgewählt)Licht-Anfangs- und End-Vertices (ausgewählt)LichttexturLicht-Vertices (nicht ausgewählt)Licht-Vertices (normal)Licht-Vertices (ausgewählt)LichtvoluminaLichtvoluminaLicht-FlagsLichteigenschaftenLimburgischLinienLingálaNur sichtbare Elemente anzeigenLitauischIn Texturbrowser ladenLade ShaderLade Entity {0:d} Lade MapLade TexturenLade Texturen...Lade, bitte warten...Lade...Orts-Entity:Ort:Logik für Schwierigkeitsstufe {0:d}Luba-KatangaLuxemburgischMapMD5-AnimationsvorschauMazedonischStärke:Hauptgitter-StilAushöhlenRaum erstellenAls Detail markierenMissionsziel {0} unsichtbar machenMissionsziel {0} obligatorisch machenMissionsziel {0} nicht obligatorisch machenMissionsziel {0} sichtbar machenRaum erstellenAls Struktur markierenErzeuge VisportalMalagasyMalaiischMalayalamMaltesischObgligatorisches ZielManipulierenManx-GälischMaoriMapMap-InformationUngültige Map Info File VersonMap-InformationLadevorgang abgebrochenSchreibvorgang abgebrochenMarathiMarshallesischAusdruckPassende SkinsMaterialAnzahl OberflächenMaterial:MatrixMax. Auslösevorgänge:Maximal dargestellte Länge für ShadernamenMaximale Grösse des Schnappschussverzeichnisses pro Map (MB)Max. Interleaving DistanzMax:Maximale FolgegeschwindigkeitMessenMediaMediabrowserReichweite für HandwaffenEntities zusammenführenAuswahl zusammenführenAusgewählte Entities zusammenführenFehler beim Zusammenführen. Die Länge der zu überschreibenden Definition konnte nicht abgerufen werden.Min. Interleaving DistanzMin:Minimale Alarmierungs-StufeNebengitter-StilSpiegelnSpiegeln um X-AchseSpiegeln um Y-AchseSpiegeln um Z-AchseMissionMissions-InfoMissions-Info (darkmod.txt)MissionszieleMissions-ReadmeMissions-Readme Editor (readme.txt)Missions-Titel:KomponentenmodusMod (fs_game)Mod Base (fs_game_base)Mod-Base-Pfad nicht definiert, Mod-Pfad auch nicht, verwende Base-Pfad...Mod-Base-Pfad nicht definiert, verwende Mod-Pfad...Mod Base/xdataMod-Pfad nicht definiert, verwende Base-Pfad...Mod/xdataModellModel-DefinitionModel-ExportModell-PfadModell-SkalierungModellnameModelleBenutzte Modelle:HilfstastenTexturierung bearbeiten:Module initialisiertMongolischStärker gepunktete LinienMehr ZylinderartenMaustastenbelegungen...Bildlaufgeschwindgkeit beim ScrollenNach untenNach obenNach untenVerschiebe Spielerstartpunkt hierNach Layer verschieben...Nach obenBewegungsgeschwindigkeit (in Spieleinheiten)MultiplizierenNameNamenskonfliktName der EntityName:Benutzte Skins:Natürliche SkalaNauruischNavajoIsiNdebeleSüd-NdebeleNdongaNepalesischNeuNeuer AkteurNeue KonversationNeue MapNeue XY-AnsichtNeues Ziel {0:d}NeuerFilterNächste Ansicht (XY, XZ, YZ)Nächster Leak-SpotKeine Brushes ausgewählt.Keine Faces ausgewählt.Kein Zieldateipfad angegeben, exportieren nicht möglich.Kein Spieltyp ausgewählt.Die Importzusammenfassung ist erst nach dem Import einer XData-Definition verfügbar.Die Importzusammenfassung ist erst nach dem Öffnen des GUI-Browsers verfügbar.Keine Patches ausgewählt.Kann diesen Filter nicht einfügen, keine Regeln definiert.Für diesen Filter sind keine Regeln definiert, soll der Filter gelöscht werden?Keine Skripts verfügbarKein ShaderKein SpezifikatorKnotenanzahlAmplitude:KeineNormalisierenNordsamischNorwegischKeine geeignete GUI-Definition!Hinweis: Bitte beachte, dass sich das Löschen benutzerdefinierter Stims auf andere Entities auswirken kann. Gehe deshalb sorgfältig vor.Hinweis: diese Kombination ist bereits zugeordnet:Hinweis: das CSG Werkzeug sollte vorsichtig benutzt werden, da es unnötig viele sehr kleine Brushes und/oder Lecks erzeugen kann. Dieser Hinweis wird nicht wieder erscheinen.Hinweis: alle Änderungen werden in den File ... geschriebenHinweis: alle Änderungen werden in den File {0} geschriebenFür diese Operation darf nichts ausgewählt sein.Nichts ausgewählt, kann keine Gruppe erstellenNichts ausgewählt, exportieren nicht möglichNichts ausgewählt, kann keine Gruppierung auflösenVerschiebenVerschiebe nach untenVerschiebe nach linksVerschiebe nach rechtsVerschiebe nach obenAnzahl der Seiten:Anzahl der Brushes/Patches/Entities in der Map (Anzahl der selektierten Elemente in Klammern)Anzahl der zuletzt geöffneten DateienAnzahl der Seitenflächen:OKObjekt wird zerstörtMissionsziel-BedingungenMissionsziel-EntitiesMissionszieleMissionsziele...OkzitanischDuplizierte Objekte um eine Rastereinheit nach rechts unten verschiebenAbstand:OjibweOmniEinseitige Schriftstück-GUIsEinseitigLaufendDatei öffnenMap öffnenMap öffnenLade zuletzt geöffnete Map beim StartMap öffnenOpenGL ErweiterungenOpenGL EigenschaftenAbbruch durch BenutzerOptimierungOptional. Nur ausfüllen bei Kampagnen bestehend aus mehreren Missionen.OptionenOptionen:Die Reihenfolge der Title/Description/Author/etc.-Elemente ist ungültig.Orientierung:OriyaOromo2D-AnsichtOssetischAndere ShaderZieldateiExport-Format:Zielpfad:Auswärts Insgesamt (komponentenspezifisch)Bestehende Tastenkombination überschreiben?TextbearbeitungSeitenindex ungültig.Seite:Sound beim Umblättern:PaliKamera-Ansicht verschiebenBild zusammen mit 2D-Ansicht verschiebenPanjabiParallelUngültigen Wert '{0}' für Schlüssel '{1}' eingelesenLade Material Datei {0}PartikelPartikeldefinitionenPartikeleditorPartikeleditor...PartikelstufenShader einfügenShader einfügen (Natürliche Skalierung)Shadernamen einfügenShader einfügen (Natürliche Skalierung)Shader einfügen (Projektion)Shader einfügen (gesamter Brush)Wertepaar(e) einfügenTexturkoordinaten einfügenBei Kameraposition einfügenPatchPatch-KontrollpunktePatch-Vertices (Ecke)Patch-Vertices (innen)Patch InspectorPatch-TesselierungsgrenzwertPatch-TesselierungPatch-ExtrusionPatchesPatches:PfadPfad-Typ:PatroullierenPersischShader auswählenTexturauswahl per MausklickAbspielenLoopenSpieler bestiehlt AISpieler besitzt GegenstandSpieler ist verantwortlichBitte benenne Deine XData-Definition um, so dass sie unter einem neuen Namen gespeichert wird.Bitte einen Spieltyp auswählenBitte eine neue Maus-/Tastenkombination durch Klick auf die Fläche unten wählen.Bitte zuerst einen XData-Namen angeben!Kamera ausrichtenPolnischPolygonePortugiesischEinstellungen...PrefabSeite voranstellenVorverarbeitungVorheriger Leak-SpotPrimitiv #{0:d}: ParserfehlerPrimitiv #{0:d}: Parserfehler {1}Prisma...Fehler beim Import.Verarbeite Zeile {0}...Vorgang läuft...ProjektionEigenschaftenEigenschaftPaschtunischCap-Patches für Auswahl erstellenPython SkripteingabeQuechuaRadiale Geschwindigkeit:RadiusRadius verändert sich über die Zeit zu:Zufällige Effekte:Zufälligkeit:RangAbspielrate:Neu verteilenSchriftstück-EditorEin Schriftstück wird geschlossenEin Schriftstück wird geöffnetSchriftstück:Wirklich alle Belegungen auf die Standardeinstellungen zurücksetzen?Zuletzt geöffnete MapsRechteckigNeu ladenRegionAAS File neu ladenDefs neu ladenMaterials neu ladenModelle neu ladenPartikel-Definitionen neu ladenPartikel neu ladenSchriftstück-GUIs neu ladenS&kins neu ladenSkripts neu ladenAusgewählte Modelle neu ladenSkins neu ladenNeuladen der DefinitionenNeuladen der GUIsNeuladen der ModelleEntfernenKurvenkontrollpunkt entfernenSeite EntfernenStimtyp entfernenVon Layer entfernen...Entferne die ausgewählte BrushAusgewählte Kurvenkontrollpunkte entfernenEbene umbenennenDiese Ebene umbenennenRenderer:Renderer: {0}Primitive neu zuordnen (Elternknoten ändern)Primitive Worldspawn zuordnenPrimitive neu zuordnenAuswahl durch exportiertes Modelle ersetzenErsetzen:Erforderliche TDM-Version:Prefab-Ordner neu durchsuchenAlle Belegungen zurücksetzenAuf Standardwerte zurücksetzenAuf Standard zurücksetzen?Größe ändernResponse-EffekteResponsesDarkRadiant neu starten um die Änderungen anzuwendenNeustart erforderlichTeil zu Worldspawn umwandelnZu Worldspawn umwandelnRechtsRinggröße:RumänischRätoromanischDrehenObjekte unabhängig voneinander drehenDrehe um X-AchseDrehe um Y-AchseDrehe um Z-AchseDrehen und skalieren...Drehe func_*-Entities um deren UrsprungRotationRotationsgeschwindigkeitRotationsgeschwindigkeit:Rotation:Reihe:ZeilenRegelnStarte SkriptKirundiRussischS/RSamoischSangoSanskritSardischBei Missionsstart erfülltSpeichernSpeichern &als...&Kopie speichern als...&Region speichernÄnderungen SpeichernSpeichere Kopie als...Datei speichernMap speichernSpeichere SchnappschüsseSpeichern & SchließenSollen die Änderungen an der Map "{0}" vor dem Schließen gespeichert werden?_Auswahl als Kollisionsmodell speichern...Auswahl als Prefab speichernDie aktuelle Map speichernSpeichere MapSkalierungSkriptSkriptfunktion:Durchsuche nur die aktuelle AuswahlSuche AAS FilesSuche nach ModulenIn Media Browser zeigenAuswählenBerührtes auswählenEine .prt-Datei auswählenKinder auswählenKanten auswählenEntities auswählenEntity auswählenFaces auswählenTeile einer Gruppe auswähenVerbundene Objekte auswählenVertices auswählenSprachsatz auswählen...Alles vom selben Typ auswählenInneres auswählen (Projektion)Inneres auswählen (Projektion)Alle Elemente mit diesem Shader auswählenInneres auswählenInneres auswählenUm eine Gruppe zu erstellen muss mehr als ein Element ausgewählt werdenNeue Tastenbelegung wählen: {0}Zuletzt geöffneten Pfad auswählen:Berührtes auswählenAusgewählte GruppenelementeAusgewählte ElementeAusgewählte Elemente (Kamera)Auswahlsatz: In WortenSerbischUnsichtbar machenSetze den Status von Missionsziel {0} auf {1}Sichtbar machenÜber Brush festlegenÜber XY-Ansicht festlegenÜber Auswahl festlegenObligatorisch machenSetze Status auf {0}EinstellungEinstellungen/Einstellungen/Automatisches SpeichernEinstellungen/KameraEinstellungen/ClipperEinstellungen/KompatibilitätEinstellungen/RasterEinstellungen/SpracheEinstellungen/MapEinstellungen/Model-ExportEinstellungen/MultimonitorEinstellungen/2D-AnsichtEinstellungen/PatchesEinstellungen/PrimitiveEinstellungen/Schriftstück-EditorEinstellungen/SelektionEinstellungen/TexturbrowserEinstellungen/UndoShaderShader:Shader-Zwischenablage ist leer.Shader-Zwischenablage: {0}ShaderBenutzte Shader:Form:UmschaltShonaListe der TastenkombinationenZeigenZeige WinkelZeige NamenAAS-Nummern anzeigenZeige AchsenZeige BlöckeZeige KoordinatenZeige KoordinatenZeige FadenkreuzZeige Richtungspfeile der EntitiesZeige Namen der EntitiesZeige RasterZeige Zusammenfassung des letzten GUI-ImportsZeige ShaderdefinitionZeige GrößeninformationZeige Textur-FilterZeige FensterumrandungZeige ArbeitsbereichAlle anzeigenZeige Toolbar im Kamera-FensterZeige KoordinatenachsenZeige doppelte DefinitionenZeige HilfeZeige VerborgenesZeige vererbte EigenschaftenZeige Zusammenfassung des letzten XData-ImportsZeige GrößeninformationZeige WireframeZeige/verberge alle LichtvoluminaZeige/verberge alle Speaker-VoluminaZeigt die aktuelle Position des Mauszeigers in der Ortho-AnsichtYiSindhiSinghalesischSitz-WinkelGröße / Geschw.Größe:SkinSkinnameSkin:SkinsCaulk-Flächen nicht exportierenKein Diffusemap-RenderingKein Specularmap-RenderingSlowakischSlowenischRotationspivot am Raster ausrichtenAuswahl am Raster ausrichtenAuswahl am Raster ausrichtenAm Raster ausrichtenGrössenlimit des Schnappschussverzeichnisses (MB)Schnappschussverzeichniss (relativ zu map Verzeichniss)Feste AuswahlrahmenSomaliPatch ist nicht geeignet für diese Operation.Süd-SothoSounddateienSoundshaderQuellmission:Status des Quell-Missionsziels:Quell-Missionsziel:SpanischWertepaar:Geschw.:Kugelradius:Kugel...SphärischAuswahl splittenQuadrateStufeStufeneinstellungenStandardStartStarte DarkRadiant auf MonitorNeue Mission namens {0} anlegen?Starte im "alarmierten" Idle-ZustandSitzt von anfang anSchläft von anfang anGUI-Browser öffnen?ZustandSchritt:Stim/Response-EditorStim/Response...StimsPatch-Texturen zusammenfügenStopLogik für Erfüllung:Logik für Erfüllung:Logik für ErfüllungSundanesischSurface InspectorMit Monsterclip einhüllenMit Monsterclip einhüllenSwahiliSiswatiSchwedischSchalte zu Standard-GUI um...TagalogTahitianischTadschikischTamilischMissionsziel:TatarischTeamTeluguTexturbrowser-HintergrundTexturbrowserTextur-LockTexturoperationenTextureigenschaftenTextur-WerkzeugTextur-LockTexturbrowser-ScrollbalkenTexturenThailändischDer Ordnername der zu editierenden Mission, z.B. 'saintlucia'.Der Akteur {0} wurde in der aktuellen Map nicht gefunden.Die Definition {0} existiert bereits, soll sie importiert werden?Die EXE-Datei "{0}" konnte in dem angegebenen Ordner nicht gefunden werden. Der Missions-Pfad "{0}" existiert nicht. Der Missions-Ordner {0} existiert nicht. Willst Du eine neue Mission anfangen und den Ordner erzeugen?Der Mod-Base-Pfad "{0}" existiert nicht. Der Mod-Pfad "{0}" existiert nicht. Der Name des Shaders in der ZwischenablageDer Name {0} existiert bereits in dieser Map!Die angeforderte Definition wurde in mehreren Dateien gefunden, bitte die genaue Datei auswählen:Die selektierten Elemente bilden bereits eine GruppeKeines der selektierten Elemente ist Teil einer GruppeDie Schnappschüsse für diese Map überschreiten das eingestellte Limit. Es wäre vielleicht gut den Ordner {0} aufzuräumen.Die angegebene Definition existiert nicht.Die angegebene Datei kann nicht geöffnet werden.Die angegebene Datei existiert nicht.Die angegebene GUI-Definition ist kein Schriftstück.Die angegebene GUI-Definition ist für das momentan gewählte Seitenlayout nicht geeignet.Diese Tastenkombination ist bereits {0} zugeordnet. Soll diese Assoziation aufgehoben werden und die Tastenkombination stattdessen {1} zugeordnet werden?Es gibt keine mehrfachen XData-DefinitionenDiese Logik überschreibt die Standard-Logik für die angegebene Schwierigkeitsstufe wenn das Logik-Eingabefeld nicht leer ist.Ausgewählte Patches extrudierenAusgewählte Patches extrudierenTiefe (in Rastereinheiten):Das-ist-nicht-Dromed-WarnungDieser Befehl steht im Komponentenmodus nicht zur Verfügung.Diese Bedingung ist noch nicht gültig oder vollständig.Diese Standardeinstellung wurde außer Kraft gesetzt, Editieren nicht möglich.An dieser stelle sollte sich TheDarkMod.exe befinden.Das ist die Standard-Logik für alle Schwierigkeitsstufen.Dieser Name existiert bereitsDieser Name ist bereits in Verwendung.Diese Seite wurde verschoben! Bitte benutze den Dialog unter dem Menüpunkt Datei > Spiel/Projekteinstellungen...Dies entfernt alle vorhandenen Auswahlsätze. Die Objekte in der aktuellen Map werden dadurch nicht beeinflusst. Fortfahren?TibetanischTigrinyaZeitversatz / Sek.Zeitintervall:Zeit:Timer-NeustartsTimer startet neu nach AblaufTimer wartet auf Start (wenn Option deaktiviert: Timer startet zur Spawn-Zeit)Titel:Titel:Die aktuelle Definition wurde in {0} umbenannt, um doppelte XData-Definitionen zu vermeiden.Um die Auswahl durch das exportierte Model zu ersetzen muss der Pfad innerhalb des Projekts/Mods liegen.Bis:Raster anzeigen/verbergenAnzeigen/VersteckenEbenensichtbarkeit umkehrenTongaWerkzeugWerkzeugeObenOben linksOben rechtsGesamte PolygoneGesamte VerticesSpuren:TranslahierenTranslationen sind immer achsengebundenTransparenzTransponierenTsongaTswanaTürkischTurkmenischTwiZwei Entities befinden sich innerhalb eines bestimmten RadiusZweiseitige Schriftstück-GUIsZweiseitigTypTyp:UigurischUkrainischKann Ziel-Entity nicht erzeugen: EntityKlasse '{0}' nicht gefunden.Kann Ziel-Entity nicht erzeugen: die Entityklassen sind nicht in der Registierung definiert.Kann Konversationsentity nicht anlegen, Klasse '{0}' nicht gefunden.Entity {0} kann nicht erzeugt werden. Keine Brushes ausgewählt.Kann Licht nicht erzeugen, Klassenname nicht gefunden.Kann Modell nicht erzeugen, Klassenname nicht gefunden.Kann Speaker nicht erzeugen, Klassenname nicht gefunden.Konnte Map-Version nicht parsen (Parse-Exception)Vorschau nicht möglich, Entityklasse {0} nicht vorhanden.Größe des Undo-StapelsGruppierung auflösenGruppierte Elemente auflösenEinheitliche Größe der Texturvorschaubilder (in Pixel)UnbekanntNach obenAktualisiere 2D-Ansichten bei KamerabewegungenAufwärtsdrift:UrduEntity-Farbe verwendenVerwende den Entity-Origin als UrsprungWelt-Schwerkraftrichtung verwendenVerwende HintergrundbildBoundingbox benutzenVerwende Start/EndUsbekischAnsichtAlle überprüfenWertGeschwindigkeit:VendaHersteller:Hersteller: {0}Detailliertes LoggingVersion:Version: {0:d}.{1:d}.{2:d}Version: {0}Vert. Skalierung:Vert. Verschiebung:Vertikaler FOVVertikale VerschiebungVertikal:VietnamesischAnsichtFilter anzeigenShaderdefinition ansehenAnsicht folgt Mauszeiger bei Verschiebe-OperationenSichtbarSehschärfeSprachsatz:VolapükWartenWarten bis Befehl fertig ausgeführt wurdeWallonischWarnung: {0} Möchtest Du diese Einstellungen korrigieren?Achtung:Warnung: die Kamera befindet sich nicht innerhalb der Region, konnte daher den Spielerstartpunkt nicht setzen.WalisischWestfriesischUm Texturen per Klick auszuwählen, aktiviere den "Auswählen"-Button und verwende {0} in der Kamera-Ansicht um den Materialnamen auszuwählen. Der gewählte Texturename wird dann in dem Textfeld neben dem gewählten Button eingetragen.Breite:FensterlayoutWolofArbeitsbereichSchreibe MapSchreibe Knoten {0:d}XX-Größe:X-AchseRotation um X-AchseSkalierung X-AchseX:XData-Name:Ordner zum Ablegen der XData-DateienXData wurde umbenannt.XData ImportzusammenfassungXY (Oben)XY ObenXY-AnsichtFadenkreuz (XY-Ansicht)XZXZ VorneXdata-PfadXhosaYY-Größe:Y-AchseRotation um Y-AchseSkalierung Y-AchseYZYZ SeiteYiddischYorubaDu hast keine GUI ausgewählt. Es wird die Standard-GUI verwendet.Du hast eine XData-Definition importiert, die in einem PK4-File gespeichert ist.ZZ-Größe:Z-AchseRotation um Z-AchseSkalierung Z-AchseZhuangAnsicht zoomenBild zusammen mit 2D-Ansicht zoomenZuluclipPlaneInButtonclipPlaneOutButtoncreateNodeForEntity: Entity für Entity-Klasse NULL kann nicht erstellt werden.deaktiviertaktiviertEntitiesEntityentityflatShadeBtnGridhversteckehttp://www.darkradiant.net/ Dieses Produkt enthält teilweise Software der Firma id Software, Inc. ('id Technology'). id Technology 2000 id Software,Inc. DarkRadiant basiert auf der GPL version von GtkRadiant (www.qeradiant.com) The Dark Mod (www.thedarkmod.com)lightingBtnlightingModeButtonmmsnextButtonNeinintransparentpauseTimeButtonprevButtonsSekunden:zeigestartTimeButtonstopTimeButtontexturedBtntexturedModeButtonMal(e)Mal(e) abgespielttransparentunbenannt.mapwireframeBtnwxWidgets Eigenschaftenx shader ersetzt.An X-Achse SpiegelnUm X-Achse rotierenx: {0:6.1f} y: {1:6.1f} z: {2:6.1f}An Y-Achse SpiegelnUm Y-Achse rotierenJaAn Z-Achse SpiegelnUm Z-Achse rotieren{0:d} ungeeignete Faces wurden nicht bearbeitet (Faces mit mehr als 4 Eckpunkten).{0:d} Loot{0:d} Loot in Gold{0:d} Loot in Waren{0:d} Loot in Edelsteinen{0:d} Minuten{0:d} Modelle geladen{0:d} von "{1}"{0:d} Sekunden{0:d} Shader ersetzt.{0} AI vom Team {1}{0} AI vom Typ {1}{0} AI von {1}{0} Einstellungen{0} bei [ {1} ]{0} Entities ersetzt.{0} ist definiert in:{0} Modelle ersetzt.{0} der Spawnklasse {1}{0} vom Typ {1}{0} Shader ersetzt.{0} Wertepaare ersetzt.{0} erfolgreich importiert{0}{1} existiert bereits unter anderem Pfad. XData wird in {2}{3} gespeichert!DarkRadiant-2.5.0/install/i18n/de/LC_MESSAGES/darkradiant.po000066400000000000000000005773361321750546400231640ustar00rootroot00000000000000msgid "" msgstr "" "Project-Id-Version: DarkRadiant\n" "Report-Msgid-Bugs-To: greebo@angua.at\n" "POT-Creation-Date: 2017-12-17 17:20+0100\n" "PO-Revision-Date: 2017-12-17 17:20+0100\n" "Last-Translator: codereader \n" "Language-Team: The Dark Mod\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-KeywordsList: _;gettext;gettext_noop\n" "X-Poedit-Basepath: .\n" "X-Poedit-Language: German\n" "X-Poedit-SearchPath-0: ..\\..\\..\\..\\radiant\n" "X-Poedit-SearchPath-1: ..\\..\\..\\..\\libs\n" "X-Poedit-SearchPath-2: ..\\..\\..\\..\\plugins\n" #: ..\..\radiant\brush\BrushModule.cpp:27 msgid "Settings/Primitives" msgstr "Einstellungen/Primitive" #: ..\..\radiant\brush\BrushModule.cpp:31 msgid "Default texture scale" msgstr "Standard-Texturskalierung" #: ..\..\radiant\brush\BrushModule.cpp:34 msgid "Enable Texture Lock (for Brushes)" msgstr "Textur-Lock aktivieren (für Brushes)" #: ..\..\radiant\brush\csg\CSG.cpp:284 msgid "This Is Not Dromed Warning" msgstr "Das-ist-nicht-Dromed-Warnung" #: ..\..\radiant\brush\csg\CSG.cpp:285 msgid "" "Note: be careful when using the CSG tool, as you might end up\n" "with an unnecessary number of tiny brushes and/or leaks.\n" "This popup will not be shown again." msgstr "" "Hinweis: das CSG Werkzeug sollte vorsichtig benutzt werden,\n" "da es unnötig viele sehr kleine Brushes und/oder Lecks erzeugen kann.\n" "Dieser Hinweis wird nicht wieder erscheinen." #: ..\..\radiant\brush\csg\CSG.cpp:297 #: ..\..\radiant\brush\csg\CSG.cpp:298 msgid "CSG Subtract: No brushes selected." msgstr "CSG Subtraktion: Keine Brushes ausgewählt." #: ..\..\radiant\brush\csg\CSG.cpp:408 #: ..\..\radiant\brush\csg\CSG.cpp:409 msgid "CSG Merge: No brushes selected." msgstr "CSG Zusammenfügen: Keine Brushes ausgewählt." #: ..\..\radiant\camera\CameraSettings.cpp:56 msgid "Settings/Camera" msgstr "Einstellungen/Kamera" #: ..\..\radiant\camera\CameraSettings.cpp:59 msgid "Movement Speed (game units)" msgstr "Bewegungsgeschwindigkeit (in Spieleinheiten)" #: ..\..\radiant\camera\CameraSettings.cpp:60 msgid "Rotation Speed" msgstr "Rotationsgeschwindigkeit" #: ..\..\radiant\camera\CameraSettings.cpp:63 msgid "Freelook mode can be toggled" msgstr "Umschalten von/zu Umsichtsmodus erlauben" #: ..\..\radiant\camera\CameraSettings.cpp:64 msgid "Discrete movement (non-freelook mode)" msgstr "Schrittweise Bewegung (freier Umsichtsmodus aus)" #: ..\..\radiant\camera\CameraSettings.cpp:65 msgid "Enable far-clip plane (hides distant objects)" msgstr "Aktiviere hintere Clip-Ebene (ferne Objekte werden nicht angezeigt)" #: ..\..\radiant\camera\CameraSettings.cpp:68 msgid "Invert mouse vertical axis (freelook mode)" msgstr "Invertiere vertikale Mausachse (freier Umsichtsmodus)" #: ..\..\radiant\camera\CameraSettings.cpp:71 msgid "Solid selection boxes" msgstr "Feste Auswahlrahmen" #: ..\..\radiant\camera\CameraSettings.cpp:74 msgid "Show camera toolbar" msgstr "Zeige Toolbar im Kamera-Fenster" #: ..\..\radiant\camera\FloatingCamWnd.cpp:18 #: xml_file_content.cpp:62 msgid "Camera" msgstr "Kamera" #: ..\..\radiant\camera\tools\FreeMoveTool.h:23 msgid "Freemove Mode" msgstr "Umsichtsmodus" #: ..\..\radiant\camera\tools\JumpToObjectTool.h:22 msgid "Jump to Object" msgstr "Zu Objekt springen" #: ..\..\radiant\camera\tools\PanViewTool.h:23 msgid "Pan Camera View" msgstr "Kamera-Ansicht verschieben" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:116 msgid "Pick Shader" msgstr "Shader auswählen" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:139 msgid "Paste Shader Projected" msgstr "Shader einfügen (Projektion)" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:158 msgid "Paste Shader Natural" msgstr "Shader einfügen (Natürliche Skalierung)" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:177 msgid "Paste Texture Coordinates" msgstr "Texturkoordinaten einfügen" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:196 msgid "Paste Shader to all Brush Faces" msgstr "Shader einfügen (gesamter Brush)" #: ..\..\radiant\camera\tools\ShaderClipboardTools.h:215 msgid "Paste Shader Name" msgstr "Shadernamen einfügen" #: ..\..\radiant\clipper\Clipper.cpp:40 msgid "Settings/Clipper" msgstr "Einstellungen/Clipper" #: ..\..\radiant\clipper\Clipper.cpp:42 msgid "Clipper tool uses caulk texture" msgstr "Clipper verwendet Caulk-Textur für neu erzeugte Faces." #: ..\..\radiant\clipper\Clipper.cpp:43 msgid "Caulk shader name" msgstr "Name des Caulk Shaders" #: ..\..\radiant\layers\LayerSystem.cpp:32 msgid "Default" msgstr "Standard" #: ..\..\radiant\layers\LayerSystem.cpp:558 #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1538 msgid "Enter Name" msgstr "Namen eingeben" #: ..\..\radiant\layers\LayerSystem.cpp:559 msgid "Enter Layer Name" msgstr "Ebenennamen eingeben" #: ..\..\radiant\layers\LayerSystem.cpp:571 msgid "Cannot create layer with empty name." msgstr "Es ist nicht möglich eine Ebene ohne Namen zu erstellen." #: ..\..\radiant\layers\LayerSystem.cpp:585 msgid "This name already exists." msgstr "Dieser Name existiert bereits" #: ..\..\radiant\map\algorithm\Export.cpp:94 msgid "" "To replace the selection with the exported model\n" "the output path must be located within the mod/project." msgstr "" "Um die Auswahl durch das exportierte Model zu ersetzen\n" "muss der Pfad innerhalb des Projekts/Mods liegen." #: ..\..\radiant\map\algorithm\Export.cpp:133 #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:425 msgid "Unable to create model, classname not found." msgstr "Kann Modell nicht erzeugen, Klassenname nicht gefunden." #: ..\..\radiant\map\algorithm\MapExporter.cpp:114 msgid "Writing map" msgstr "Schreibe Map" #: ..\..\radiant\map\algorithm\MapExporter.cpp:222 msgid "Writing node {0:d}" msgstr "Schreibe Knoten {0:d}" #: ..\..\radiant\map\algorithm\MapImporter.cpp:41 msgid "Loading map" msgstr "Lade Map" #: ..\..\radiant\map\algorithm\MapImporter.cpp:44 #: ..\..\radiant\map\algorithm\MapImporter.cpp:59 msgid "Loading entity {0:d}\n" msgstr "Lade Entity {0:d}\n" #: ..\..\radiant\map\algorithm\Models.cpp:94 #: ..\..\radiant\map\algorithm\Models.cpp:110 #: ..\..\radiant\map\Map.cpp:111 #: ..\..\radiant\map\Map.cpp:426 #: ..\..\radiant\map\Map.cpp:525 #: ..\..\radiant\map\Map.cpp:553 #: ..\..\radiant\RadiantModule.cpp:106 #: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:323 msgid "Processing..." msgstr "Vorgang läuft..." #: ..\..\radiant\map\algorithm\Models.cpp:94 #: ..\..\radiant\map\algorithm\Models.cpp:110 msgid "Reloading Models" msgstr "Neuladen der Modelle" #: ..\..\radiant\map\AutoSaver.cpp:204 msgid "Snapshot Folder Size Warning" msgstr "Grössenlimit des Schnappschussverzeichnisses (MB)" #: ..\..\radiant\map\AutoSaver.cpp:205 msgid "" "The snapshots saved for this map are exceeding the configured size limit.\n" "Consider cleaning up the folder {0}" msgstr "" "Die Schnappschüsse für diese Map überschreiten das eingestellte Limit.\n" "Es wäre vielleicht gut den Ordner {0} aufzuräumen." #: ..\..\radiant\map\AutoSaver.cpp:330 msgid "Settings/Autosave" msgstr "Einstellungen/Automatisches Speichern" #: ..\..\radiant\map\AutoSaver.cpp:333 msgid "Enable Autosave" msgstr "Aktiviere automatisches Speichern" #: ..\..\radiant\map\AutoSaver.cpp:334 msgid "Autosave Interval (in minutes)" msgstr "Speicherintervall" # Snapshots oder Schnappschüsse? #: ..\..\radiant\map\AutoSaver.cpp:336 msgid "Save Snapshots" msgstr "Speichere Schnappschüsse" # Snapshot oder Schnappschuss? #: ..\..\radiant\map\AutoSaver.cpp:337 msgid "Snapshot folder (relative to map folder)" msgstr "Schnappschussverzeichniss (relativ zu map Verzeichniss)" #: ..\..\radiant\map\AutoSaver.cpp:338 msgid "Max total Snapshot size per map (MB)" msgstr "Maximale Grösse des Schnappschussverzeichnisses pro Map (MB)" #: ..\..\radiant\map\CounterManager.cpp:63 msgid "" "Number of brushes/patches/entities in this map\n" "(Number of selected items shown in parentheses)" msgstr "" "Anzahl der Brushes/Patches/Entities in der Map\n" "(Anzahl der selektierten Elemente in Klammern)" #: ..\..\radiant\map\CounterManager.cpp:81 msgid "Brushes: {0:d} ({1:d}) Patches: {2:d} ({3:d}) Entities: {4:d} ({5:d})" msgstr "Brushes: {0:d} ({1:d}) Patches: {2:d} ({3:d}) Entities: {4:d} ({5:d})" #: ..\..\radiant\map\FindMapElements.cpp:95 msgid "Find Brush" msgstr "Suche Brush" #: ..\..\radiant\map\FindMapElements.cpp:97 msgid "Entity Number:" msgstr "Entitynummer:" #: ..\..\radiant\map\FindMapElements.cpp:98 msgid "Brush Number:" msgstr "Brushnummer:" #: ..\..\radiant\map\infofile\InfoFile.cpp:68 msgid "Map Info File Version invalid" msgstr "Ungültige Map Info File Verson" #: ..\..\radiant\map\Map.cpp:64 msgid "unnamed.map" msgstr "unbenannt.map" #: ..\..\radiant\map\Map.cpp:111 msgid "Loading textures..." msgstr "Lade Texturen..." #: ..\..\radiant\map\Map.cpp:428 msgid "Preprocessing" msgstr "Vorverarbeitung" #: ..\..\radiant\map\Map.cpp:437 msgid "" "Failure running map pre-save event:\n" "{0}" msgstr "" "Fehler beim Aufruf des Pre-Save Events:\n" "{0}" #: ..\..\radiant\map\Map.cpp:445 msgid "Saving Map" msgstr "Speichere Map" #: ..\..\radiant\map\Map.cpp:484 msgid "Importing..." msgstr "Importiere..." #: ..\..\radiant\map\Map.cpp:578 msgid "" "Save changes to map \"{0}\"\n" "before closing?" msgstr "" "Sollen die Änderungen an der Map \"{0}\"\n" "vor dem Schließen gespeichert werden?" #: ..\..\radiant\map\Map.cpp:585 msgid "{0:d} minutes" msgstr "{0:d} Minuten" #: ..\..\radiant\map\Map.cpp:589 msgid "{0:d} seconds" msgstr "{0:d} Sekunden" #: ..\..\radiant\map\Map.cpp:593 msgid "" "If you don't save, changes from the last {0}\n" "will be lost." msgstr "" "Wenn Du nicht sicherst, gehen die Änderungen\n" "der letzten {0} verloren." #: ..\..\radiant\map\Map.cpp:647 msgid "Save Map" msgstr "Map speichern" #: ..\..\radiant\map\Map.cpp:687 msgid "Save Copy As..." msgstr "Speichere Kopie als..." #: ..\..\radiant\map\Map.cpp:784 msgid "New Map" msgstr "Neue Map" #: ..\..\radiant\map\Map.cpp:793 #: ..\..\radiant\ui\mru\MRU.cpp:93 msgid "Open Map" msgstr "Map öffnen" #: ..\..\radiant\map\Map.cpp:797 msgid "Open map" msgstr "Map öffnen" #: ..\..\radiant\map\Map.cpp:811 msgid "Import map" msgstr "Map importieren" #: ..\..\radiant\map\Map.cpp:840 msgid "Export selection" msgstr "Auswahl exportieren" #: ..\..\radiant\map\Map.cpp:855 msgid "Save selected as Prefab" msgstr "Auswahl als Prefab speichern" #: ..\..\radiant\map\Map.cpp:936 msgid "" "Failure reading map from clipboard:\n" "{0}" msgstr "" "Fehler beim Lesen der Mapdatei aus der Zwischenablage:\n" "{0}" #: ..\..\radiant\map\MapFileManager.cpp:32 msgid "Map" msgstr "Map" #: ..\..\radiant\map\MapFileManager.cpp:33 #: xml_file_content.cpp:91 msgid "Region" msgstr "Region" #: ..\..\radiant\map\MapFileManager.cpp:34 #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:344 msgid "Prefab" msgstr "Prefab" #: ..\..\radiant\map\MapResource.cpp:255 #: ..\..\radiant\map\MapResource.cpp:527 msgid "File is write-protected: {0}" msgstr "Datei ist schreibgeschützt: {0}" #: ..\..\radiant\map\MapResource.cpp:361 msgid "" "Could not determine map format of file:\n" "{0}" msgstr "Konnte das Format der Mapdatei nicht bestimmen: {0}" #: ..\..\radiant\map\MapResource.cpp:408 msgid "Map loading cancelled" msgstr "Ladevorgang abgebrochen" #: ..\..\radiant\map\MapResource.cpp:420 msgid "" "Failure reading map file:\n" "{0}\n" "\n" "{1}" msgstr "" "Fehler beim Lesen der Mapdatei:\n" "{0}\n" "\n" "{1}" #: ..\..\radiant\map\MapResource.cpp:483 #: ..\..\radiant\map\MapResource.cpp:502 msgid "" "Failure opening file:\n" "{0}" msgstr "" "Fehler beim Öffnen der Datei:\n" "{0}" #: ..\..\radiant\map\MapResource.cpp:592 msgid "Map writing cancelled" msgstr "Schreibvorgang abgebrochen" #: ..\..\radiant\map\MapResource.cpp:607 msgid "Could not open output streams for writing" msgstr "Fehler beim Öffnen der Output-Streams" #: ..\..\radiant\map\PointFile.cpp:98 msgid "Could not open pointfile: {0}" msgstr "Konnte das Pointfile nicht öffnen: {0}" #: ..\..\radiant\map\RegionManager.cpp:174 msgid "Warning: Camera not within region, can't set info_player_start." msgstr "Warnung: die Kamera befindet sich nicht innerhalb der Region, konnte daher den Spielerstartpunkt nicht setzen." #: ..\..\radiant\map\RegionManager.cpp:262 msgid "Could not set Region: XY Top View not found." msgstr "Konnte Region nicht festlegen, weil die XY-Ansicht nicht gefunden wurde." #: ..\..\radiant\map\RegionManager.cpp:289 msgid "Could not set Region: please select a single Brush." msgstr "Kann Region nicht festlegen: bitte einen einzelnen Brush auswählen." #: ..\..\radiant\map\RegionManager.cpp:314 msgid "This command is not available in component mode." msgstr "Dieser Befehl steht im Komponentenmodus nicht zur Verfügung." #: ..\..\radiant\map\RegionManager.cpp:320 msgid "Could not set Region: nothing selected." msgstr "Kann keine Region festlegen: keine Elemente ausgewählt." #: ..\..\radiant\map\RegionManager.cpp:337 msgid "Export region" msgstr "Exportiere Region" #: ..\..\radiant\model\ModelExporter.cpp:267 #: ..\..\plugins\particles\ParticlesManager.cpp:323 #: ..\..\plugins\particles\ParticlesManager.cpp:351 msgid "Cannot open file for writing: {0}" msgstr "Kann die Datei nicht im Schreibmodus öffnen: {0}" #: ..\..\radiant\model\ModelExporter.cpp:289 msgid "Could not rename the existing file to .bak: {0}" msgstr "Konnte die bestehende Datei nicht auf .bak umbenennen: {0}" #: ..\..\radiant\model\ModelExporter.cpp:303 msgid "Could not rename the temporary file: {0}" msgstr "Konnte die temporäre Datei nicht umbenennen: {0}" #: ..\..\radiant\model\ModelFormatManager.cpp:42 msgid "Settings/Model Export" msgstr "Einstellungen/Model-Export" #: ..\..\radiant\model\ModelFormatManager.cpp:51 msgid "Export Format for scaled Models" msgstr "Export-Format für skalierte Models" #: ..\..\radiant\modulesystem\ModuleRegistry.cpp:131 msgid "Initialising Module: {0}" msgstr "Initialisiere Modul: {0}" #: ..\..\radiant\modulesystem\ModuleRegistry.cpp:147 msgid "Searching for Modules" msgstr "Suche nach Modulen" #: ..\..\radiant\modulesystem\ModuleRegistry.cpp:161 msgid "Initialising Modules" msgstr "Initialisiere Module" #: ..\..\radiant\modulesystem\ModuleRegistry.cpp:175 msgid "Modules initialised" msgstr "Module initialisiert" #: ..\..\radiant\patch\algorithm\General.cpp:110 msgid "" "Cannot stitch textures. \n" "Could not cast nodes to patches." msgstr "" "Kann Texturen nicht zusammenheften.\n" "Konnte Nodes nicht casten." #: ..\..\radiant\patch\algorithm\General.cpp:119 msgid "" "Cannot stitch patch textures. \n" "Exactly 2 patches must be selected." msgstr "Kann Texturen nicht zusammenheften, für diese Operation müssten genau 2 Patches ausgewählt sein." #: ..\..\radiant\patch\algorithm\General.cpp:157 msgid "Cannot bulge patch. No patches selected." msgstr "Kann Patch nicht auswölben: keine Patches ausgewählt." #: ..\..\radiant\patch\algorithm\Prefab.cpp:244 msgid "Cannot create end-cap, patch must have a width of 5." msgstr "Kann keine End-Caps erzeugen, Patch muss eine Breite von 5 haben" #: ..\..\radiant\patch\algorithm\Prefab.cpp:251 msgid "Cannot create bevel-cap, patch must have a width of 3." msgstr "Kann keine Bevel-Caps erzeugen, Patch muss eine Breite von 3 haben" #: ..\..\radiant\patch\algorithm\Prefab.cpp:260 msgid "Cannot create cylinder-cap, patch must have a width of 9." msgstr "Kann keine Zylinder-Caps erzeugen, Patch muss eine Breite von 9 haben" #: ..\..\radiant\patch\Patch.cpp:1638 msgid "Sorry. Patch is not suitable for this kind of operation." msgstr "Patch ist nicht geeignet für diese Operation." #: ..\..\radiant\patch\PatchCreators.cpp:62 msgid "Settings/Patch" msgstr "Einstellungen/Patches" #: ..\..\radiant\patch\PatchCreators.cpp:63 msgid "Patch Subdivide Threshold" msgstr "Patch-Tesselierungsgrenzwert" #: ..\..\radiant\RadiantApp.cpp:104 msgid "Disable sound for this session." msgstr "Keine Soundwiedergabe in dieser Session" #: ..\..\radiant\RadiantApp.cpp:105 msgid "Verbose logging." msgstr "Detailliertes Logging" #: ..\..\radiant\selection\algorithm\Curves.cpp:188 msgid "Can't append curve point - no entities with curve selected." msgstr "Kann Kontrollpunkt nicht anfügen, keine Kurven ausgewählt." #: ..\..\radiant\selection\algorithm\Curves.cpp:198 msgid "Can't remove curve points - must be in vertex editing mode." msgstr "Kann Kontrollpunkte nicht entfernen, bitte vorher in den Vertex-Modus wechseln." #: ..\..\radiant\selection\algorithm\Curves.cpp:218 msgid "Can't remove curve points - no entities with curves selected." msgstr "Kann Kontrollpunkte nicht entfernen, keine Kurven ausgewählt." #: ..\..\radiant\selection\algorithm\Curves.cpp:228 msgid "Can't insert curve points - must be in vertex editing mode." msgstr "Kann Kontrollpunkte nicht einfügen, bitte vorher in den Vertex-Modus wechseln." #: ..\..\radiant\selection\algorithm\Curves.cpp:248 msgid "Can't insert curve points - no entities with curves selected." msgstr "Kann Kontrollpunkte nicht einfügen, keine Kurven ausgewählt." #: ..\..\radiant\selection\algorithm\Curves.cpp:269 msgid "Can't convert curves - no entities with curves selected." msgstr "Kann Kurve nicht konvertieren, keine Kurven ausgewählt." #: ..\..\radiant\selection\algorithm\Entity.cpp:82 msgid "The name {0} already exists in this map!" msgstr "Der Name {0} existiert bereits in dieser Map!" #: ..\..\radiant\selection\algorithm\Entity.cpp:106 msgid "Cannot set classname to an empty string." msgstr "Der Klassenname darf nicht leer sein" #: ..\..\radiant\selection\algorithm\Entity.cpp:112 msgid "Cannot change classname to worldspawn." msgstr "Der Klassenname darf nicht auf worldspawn gesetzt werden." #: ..\..\radiant\selection\algorithm\Entity.cpp:131 msgid "Cannot change classname of worldspawn entity." msgstr "Der Klassenname der Worldspawn-Entity darf nicht geändert werden." #: ..\..\radiant\selection\algorithm\Entity.cpp:169 msgid "Critical: Cannot find selected entities." msgstr "Kritischer Fehler: kann ausgewählte Entities nicht finden." #: ..\..\radiant\selection\algorithm\Entity.cpp:174 #: ..\..\radiant\selection\algorithm\Entity.cpp:190 msgid "Exactly two entities must be selected for this operation." msgstr "Für diese Operation müssen genau zwei Entities ausgewählt sein." #: ..\..\radiant\selection\algorithm\Entity.cpp:236 msgid "Unable to create entity {0}, no brushes selected." msgstr "Entity {0} kann nicht erzeugt werden. Keine Brushes ausgewählt." #: ..\..\radiant\selection\algorithm\Group.cpp:242 msgid "Cannot reparent primitives to entity. Please select at least one brush/patch and exactly one func_* entity. (The entity has to be selected last.)" msgstr "Kann die ausgewählten Primitive nicht neu zuordnen. Bitte zumindest einen Brush/Patch sowie genau eine Entity auswählen. (Die Entity muss als letztes ausgewählt worden sein.)" #: ..\..\radiant\selection\algorithm\Group.cpp:392 msgid "" "Cannot merge entities, the selection must consist of func_* entities only.\n" "(The first selected entity will be preserved.)" msgstr "" "Kann Entities nicht zusammenführen, die Auswahl muss ausschließlich aus func_*-Entities bestehen.\n" "(Die zuerst ausgewählte Entity bleibt dabei erhalten.)" #: ..\..\radiant\selection\algorithm\Group.cpp:403 msgid "Groups can be formed in Primitive and Group Part selection mode only" msgstr "Gruppen können nur im Primitiv- und Gruppenteil-Selektionsmodus erstellt werden" #: ..\..\radiant\selection\algorithm\Group.cpp:408 msgid "Nothing selected, cannot group anything" msgstr "Nichts ausgewählt, kann keine Gruppe erstellen" #: ..\..\radiant\selection\algorithm\Group.cpp:413 msgid "Select more than one element to form a group" msgstr "Um eine Gruppe zu erstellen muss mehr als ein Element ausgewählt werden" #: ..\..\radiant\selection\algorithm\Group.cpp:439 msgid "The selected elements already form a group" msgstr "Die selektierten Elemente bilden bereits eine Gruppe" #: ..\..\radiant\selection\algorithm\Group.cpp:465 msgid "Groups can be dissolved in Primitive and Group Part selection mode only" msgstr "Gruppen können nur im Primitiv- und Gruppenteil-Selektionsmodus aufgelöst werden" #: ..\..\radiant\selection\algorithm\Group.cpp:470 msgid "Nothing selected, cannot un-group anything" msgstr "Nichts ausgewählt, kann keine Gruppierung auflösen" #: ..\..\radiant\selection\algorithm\Group.cpp:490 msgid "The selected elements aren't part of any group" msgstr "Keines der selektierten Elemente ist Teil einer Gruppe" #: ..\..\radiant\selection\algorithm\Patch.cpp:60 msgid "Cannot create caps, no patches selected." msgstr "Kann keine Caps erzeugen, da keine Patches ausgewählt sind." #: ..\..\radiant\selection\algorithm\Patch.cpp:197 msgid "Cannot thicken patch. Nothing selected." msgstr "Nichts ausgewählt: kann Patch nicht extrudieren." #: ..\..\radiant\selection\algorithm\Primitives.cpp:78 #: ..\..\radiant\selection\algorithm\Primitives.cpp:82 msgid "No patches selected." msgstr "Keine Patches ausgewählt." #: ..\..\radiant\selection\algorithm\Primitives.cpp:243 msgid "Could not create patch." msgstr "Konnte Patch nicht erzeugen." #: ..\..\radiant\selection\algorithm\Primitives.cpp:361 msgid "No faces selected." msgstr "Keine Faces ausgewählt." #: ..\..\radiant\selection\algorithm\Primitives.cpp:382 msgid "{0:d} faces were not suitable (had more than 4 vertices)." msgstr "{0:d} ungeeignete Faces wurden nicht bearbeitet (Faces mit mehr als 4 Eckpunkten)." #: ..\..\radiant\selection\algorithm\Primitives.cpp:393 #: ..\..\radiant\selection\algorithm\Primitives.cpp:492 msgid "No brushes selected." msgstr "Keine Brushes ausgewählt." #: ..\..\radiant\selection\algorithm\Primitives.cpp:592 msgid "At least one brush must be selected for this operation." msgstr "Für diese Operation muss mindestens ein Brush ausgewählt sein." #: ..\..\radiant\selection\algorithm\Shader.cpp:307 msgid "" "Can't paste shader to entire brush.\n" "Target is not a brush." msgstr "Kann Shader nicht auf gesamten Brush anwenden, das Ziel ist kein Brush." #: ..\..\radiant\selection\algorithm\Shader.cpp:346 msgid "" "Can't paste Texture Coordinates.\n" "Target patch dimensions must match." msgstr "Kann Texturkoordinaten nicht kopieren, da die Patch-Dimensionen nicht passend sind." #: ..\..\radiant\selection\algorithm\Shader.cpp:355 msgid "Can't paste Texture Coordinates from patches to faces." msgstr "Es ist nicht möglich, Texturkoordinaten von Patches auf Faces zu kopieren." #: ..\..\radiant\selection\algorithm\Shader.cpp:361 msgid "Can't paste Texture Coordinates from faces." msgstr "Es ist nicht möglich, Texturkoordinaten von Faces zu kopieren." #: ..\..\radiant\selection\algorithm\Shader.cpp:417 msgid "Can't copy Shader. Couldn't retrieve patch." msgstr "Konnte Patch nicht casten, Shader wurde nicht kopiert." #: ..\..\radiant\selection\algorithm\Shader.cpp:427 msgid "Can't copy Shader. Couldn't retrieve face." msgstr "Konnte Face nicht casten, Shader wurde nicht kopiert." #: ..\..\radiant\selection\algorithm\Shader.cpp:433 msgid "Can't copy Shader. Please select a single face or patch." msgstr "Shader nicht kopiert: bitte ein einzelnes Face oder einen Patch auswählen." #: ..\..\radiant\selection\algorithm\Transformation.cpp:97 msgid "Cannot scale by zero value." msgstr "Kann nicht mit 0 skalieren." #: ..\..\radiant\selection\group\SelectionGroupManager.cpp:81 #: ..\..\radiant\selection\group\SelectionGroupManager.cpp:97 msgid "Ungroup Selection" msgstr "Gruppierung auflösen" #: ..\..\radiant\selection\group\SelectionGroupManager.cpp:84 #: ..\..\radiant\selection\group\SelectionGroupManager.cpp:91 msgid "Group Selection" msgstr "Gruppe erstellen" #: ..\..\radiant\selection\ManipulateMouseTool.cpp:39 msgid "Manipulate" msgstr "Manipulieren" #: ..\..\radiant\selection\RadiantSelectionSystem.cpp:924 msgid "Settings/Selection" msgstr "Einstellungen/Selektion" #: ..\..\radiant\selection\RadiantSelectionSystem.cpp:926 msgid "Ignore light volume bounds when calculating default rotation pivot location" msgstr "Ignoriere Lichtvolumina für die Berechnung des Standard-Pivotpunkts" #: ..\..\radiant\selection\SelectionMouseTools.cpp:54 msgid "Select" msgstr "Auswählen" #: ..\..\radiant\selection\SelectionMouseTools.cpp:176 #: xml_file_content.cpp:4 msgid "Select Faces" msgstr "Faces auswählen" #: ..\..\radiant\selection\SelectionMouseTools.cpp:195 msgid "Cycle Selection" msgstr "Auswahl durchrotieren" #: ..\..\radiant\selection\SelectionMouseTools.cpp:248 msgid "Cycle Face Selection" msgstr "Face-Auswahl durchrotieren" #: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:104 msgid "Selection Set: " msgstr "Auswahlsatz: " #: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:112 msgid "Clear Selection Sets" msgstr "Auswahlsätze löschen" #: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:223 msgid "Delete all selection sets?" msgstr "Alle Auswahlsätze löschen?" #: ..\..\radiant\selection\selectionset\SelectionSetManager.cpp:224 msgid "" "This will delete all set definitions. The actual map objects will not be affected by this step.\n" "\n" "Continue with that operation?" msgstr "" "Dies entfernt alle vorhandenen Auswahlsätze. Die Objekte in der aktuellen Map werden dadurch nicht beeinflusst.\n" "\n" "Fortfahren?" #: ..\..\radiant\selection\selectionset\SelectionSetToolmenu.cpp:18 msgid "" "Enter a name and hit ENTER to save a set.\n" "\n" "Select an item from the dropdown list to restore the selection.\n" "\n" "Hold SHIFT when opening the dropdown list and selecting the item to de-select the set." msgstr "" "Namen eingaben und mit der EINGABETASTE bestätigen um einen neuen Satz anzulegen.\n" "\n" "Ein Element in der Liste auswählen um die entsprechende Auswahl wiederherzustellen.\n" "\n" "UMSCHALTTASTE gedrückt halten um die Auswahl der entsprechenden Elemente aufzuheben." #: ..\..\radiant\selection\selectionset\SelectionSetToolmenu.cpp:62 msgid "Cannot create selection set" msgstr "Kann Auswahlsatz nicht erstellen" #: ..\..\radiant\selection\selectionset\SelectionSetToolmenu.cpp:63 msgid "Cannot create a selection set, there is nothing selected in the current scene." msgstr "Kann Auswahlsatz nicht anlegen, keine Auswahl vorhanden." #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:25 msgid "The name of the shader in the clipboard" msgstr "Der Name des Shaders in der Zwischenablage" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:89 msgid "ShaderClipboard: {0}" msgstr "Shader-Zwischenablage: {0}" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:92 msgid "Face" msgstr "Face" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:95 msgid "Patch" msgstr "Patch" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:98 #: ..\..\radiant\ui\common\ShaderSelector.cpp:356 #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:73 #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:60 #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:361 #: xml_file_content.cpp:14 msgid "Shader" msgstr "Shader" #: ..\..\radiant\selection\shaderclipboard\ShaderClipboard.cpp:102 msgid "ShaderClipboard is empty." msgstr "Shader-Zwischenablage ist leer." #: ..\..\radiant\settings\GameManager.cpp:119 #: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:182 msgid "Game" msgstr "Spiel" #: ..\..\radiant\settings\GameManager.cpp:120 msgid "" "This page has been moved!\n" "Please use the game settings dialog in the menu: File > Game/Project Setup..." msgstr "" "Diese Seite wurde verschoben!\n" "Bitte benutze den Dialog unter dem Menüpunkt Datei > Spiel/Projekteinstellungen..." #: ..\..\radiant\settings\GameManager.cpp:139 msgid "GameManager: No game type selected, can't continue." msgstr "GameManager: kein Spieltyp ausgewählt, kann daher nicht fortfahren." #: ..\..\radiant\settings\GameManager.cpp:157 msgid "GameManager: No valid game files found, can't continue." msgstr "GameManager: keine gültigen Spieldateien (.game) gefunden, kann nicht fortfahren." #: ..\..\radiant\settings\GameManager.cpp:193 msgid "No game type selected." msgstr "Kein Spieltyp ausgewählt." #: ..\..\radiant\settings\LanguageManager.cpp:102 msgid "Settings/Language" msgstr "Einstellungen/Sprache" #: ..\..\radiant\settings\LanguageManager.cpp:103 msgid "Language" msgstr "Sprache" #: ..\..\radiant\settings\LanguageManager.cpp:105 msgid "" "Note: You'll need to restart DarkRadiant\n" "after changing the language setting." msgstr "" "Hinweis: Du musst DarkRadiant neu starten,\n" "damit die neue Spracheinstellung aktiv wird." #: ..\..\radiant\settings\LanguageManager.cpp:216 #: ..\..\radiant\settings\LanguageManager.cpp:253 msgid "English" msgstr "Englisch" #: ..\..\radiant\settings\LanguageManager.cpp:217 msgid "Abkhazian" msgstr "Abchasisch" #: ..\..\radiant\settings\LanguageManager.cpp:218 msgid "Avestan" msgstr "Avestisch" #: ..\..\radiant\settings\LanguageManager.cpp:219 msgid "Afrikaans" msgstr "Afrikaans" #: ..\..\radiant\settings\LanguageManager.cpp:220 msgid "Akan" msgstr "Akan" #: ..\..\radiant\settings\LanguageManager.cpp:221 msgid "Amharic" msgstr "Amharisch" #: ..\..\radiant\settings\LanguageManager.cpp:222 msgid "Aragonese" msgstr "Aragonesisch" #: ..\..\radiant\settings\LanguageManager.cpp:223 msgid "Arabic" msgstr "Arabisch" #: ..\..\radiant\settings\LanguageManager.cpp:224 msgid "Assamese" msgstr "Assamesisch" #: ..\..\radiant\settings\LanguageManager.cpp:225 msgid "Avaric" msgstr "Awarisch" #: ..\..\radiant\settings\LanguageManager.cpp:226 msgid "Aymara" msgstr "Aymara" #: ..\..\radiant\settings\LanguageManager.cpp:227 msgid "Azerbaijani" msgstr "Aserbaidschanisch" #: ..\..\radiant\settings\LanguageManager.cpp:228 msgid "Bashkir" msgstr "Baschkirisch" #: ..\..\radiant\settings\LanguageManager.cpp:229 msgid "Belarusian" msgstr "Belarussisch" #: ..\..\radiant\settings\LanguageManager.cpp:230 msgid "Bulgarian" msgstr "Bulgarisch" #: ..\..\radiant\settings\LanguageManager.cpp:231 msgid "Bihari languages" msgstr "Bihari" #: ..\..\radiant\settings\LanguageManager.cpp:232 msgid "Bislama" msgstr "Bislama" #: ..\..\radiant\settings\LanguageManager.cpp:233 msgid "Bambara" msgstr "Bambara" #: ..\..\radiant\settings\LanguageManager.cpp:234 msgid "Bengali" msgstr "Bengalisch" #: ..\..\radiant\settings\LanguageManager.cpp:235 msgid "Tibetan" msgstr "Tibetanisch" #: ..\..\radiant\settings\LanguageManager.cpp:236 msgid "Breton" msgstr "Bretonisch" #: ..\..\radiant\settings\LanguageManager.cpp:237 msgid "Bosnian" msgstr "Bosnisch" #: ..\..\radiant\settings\LanguageManager.cpp:238 msgid "Catalan" msgstr "Katalanisch" #: ..\..\radiant\settings\LanguageManager.cpp:239 msgid "Chechen" msgstr "Tschetschenisch" #: ..\..\radiant\settings\LanguageManager.cpp:240 msgid "Chamorro" msgstr "Chamorro" #: ..\..\radiant\settings\LanguageManager.cpp:241 msgid "Corsican" msgstr "Korsisch" #: ..\..\radiant\settings\LanguageManager.cpp:242 msgid "Cree" msgstr "Cree" #: ..\..\radiant\settings\LanguageManager.cpp:243 msgid "Czech" msgstr "Tschechisch" #: ..\..\radiant\settings\LanguageManager.cpp:244 msgid "Chuvash" msgstr "Tschuwaschisch" #: ..\..\radiant\settings\LanguageManager.cpp:245 #: ..\..\radiant\settings\LanguageManager.cpp:246 msgid "Welsh" msgstr "Walisisch" #: ..\..\radiant\settings\LanguageManager.cpp:247 msgid "Danish" msgstr "Dänisch" #: ..\..\radiant\settings\LanguageManager.cpp:248 msgid "German" msgstr "Deutsch" #: ..\..\radiant\settings\LanguageManager.cpp:249 msgid "Divehi" msgstr "Dhivehi" #: ..\..\radiant\settings\LanguageManager.cpp:250 msgid "Dzongkha" msgstr "Dzongkha" #: ..\..\radiant\settings\LanguageManager.cpp:251 msgid "Ewe" msgstr "Ewe" #: ..\..\radiant\settings\LanguageManager.cpp:252 msgid "Greek" msgstr "Griechisch" #: ..\..\radiant\settings\LanguageManager.cpp:254 msgid "Esperanto" msgstr "Esperanto" #: ..\..\radiant\settings\LanguageManager.cpp:255 msgid "Spanish" msgstr "Spanisch" #: ..\..\radiant\settings\LanguageManager.cpp:256 msgid "Estonian" msgstr "Estnisch" #: ..\..\radiant\settings\LanguageManager.cpp:257 msgid "Basque" msgstr "Baskisch" #: ..\..\radiant\settings\LanguageManager.cpp:258 msgid "Persian" msgstr "Persisch" #: ..\..\radiant\settings\LanguageManager.cpp:259 msgid "Fulah" msgstr "Fulah" #: ..\..\radiant\settings\LanguageManager.cpp:260 msgid "Finnish" msgstr "Finnisch" #: ..\..\radiant\settings\LanguageManager.cpp:261 msgid "Fijian" msgstr "Fidschi" #: ..\..\radiant\settings\LanguageManager.cpp:262 msgid "Faroese" msgstr "Färöisch" #: ..\..\radiant\settings\LanguageManager.cpp:263 msgid "French" msgstr "Französisch" #: ..\..\radiant\settings\LanguageManager.cpp:264 msgid "Western Frisian" msgstr "Westfriesisch" #: ..\..\radiant\settings\LanguageManager.cpp:265 msgid "Irish" msgstr "Irisch" #: ..\..\radiant\settings\LanguageManager.cpp:266 msgid "Gaelic; Scottish Gaelic" msgstr "Gälisch" #: ..\..\radiant\settings\LanguageManager.cpp:267 msgid "Galician" msgstr "Galicische" #: ..\..\radiant\settings\LanguageManager.cpp:268 msgid "Guarani" msgstr "Guaraní" #: ..\..\radiant\settings\LanguageManager.cpp:269 msgid "Gujarati" msgstr "Gujarati" #: ..\..\radiant\settings\LanguageManager.cpp:270 msgid "Manx" msgstr "Manx-Gälisch" #: ..\..\radiant\settings\LanguageManager.cpp:271 msgid "Hausa" msgstr "Hausa" #: ..\..\radiant\settings\LanguageManager.cpp:272 msgid "Hebrew" msgstr "Hebräisch" #: ..\..\radiant\settings\LanguageManager.cpp:273 msgid "Hindi" msgstr "Hindi" #: ..\..\radiant\settings\LanguageManager.cpp:274 msgid "Hiri Motu" msgstr "Hiri Motu" #: ..\..\radiant\settings\LanguageManager.cpp:275 msgid "Croatian" msgstr "Kroatisch" #: ..\..\radiant\settings\LanguageManager.cpp:276 msgid "Haitian" msgstr "Haitianisch" #: ..\..\radiant\settings\LanguageManager.cpp:277 msgid "Hungarian" msgstr "Ungarisch" #: ..\..\radiant\settings\LanguageManager.cpp:278 msgid "Armenian" msgstr "Armenisch" #: ..\..\radiant\settings\LanguageManager.cpp:279 msgid "Herero" msgstr "Herero" #: ..\..\radiant\settings\LanguageManager.cpp:280 msgid "Interlingua" msgstr "Interlingua" #: ..\..\radiant\settings\LanguageManager.cpp:281 msgid "Indonesian" msgstr "Indonesisch" #: ..\..\radiant\settings\LanguageManager.cpp:282 msgid "Interlingue; Occidental" msgstr "Occidental-Interlingue" #: ..\..\radiant\settings\LanguageManager.cpp:283 msgid "Igbo" msgstr "Igbo" #: ..\..\radiant\settings\LanguageManager.cpp:284 msgid "Sichuan Yi; Nuosu" msgstr "Yi" #: ..\..\radiant\settings\LanguageManager.cpp:285 msgid "Inupiaq" msgstr "Inupiaq" #: ..\..\radiant\settings\LanguageManager.cpp:286 msgid "Ido" msgstr "Ido" #: ..\..\radiant\settings\LanguageManager.cpp:287 msgid "Icelandic" msgstr "Isländisch" #: ..\..\radiant\settings\LanguageManager.cpp:288 msgid "Italian" msgstr "Italienisch" #: ..\..\radiant\settings\LanguageManager.cpp:289 msgid "Inuktitut" msgstr "Inuktitut" #: ..\..\radiant\settings\LanguageManager.cpp:290 msgid "Japanese" msgstr "Japanisch" #: ..\..\radiant\settings\LanguageManager.cpp:291 msgid "Javanese" msgstr "Javanisch" #: ..\..\radiant\settings\LanguageManager.cpp:292 msgid "Georgian" msgstr "Georgisch" #: ..\..\radiant\settings\LanguageManager.cpp:293 msgid "Kongo" msgstr "Kongo" #: ..\..\radiant\settings\LanguageManager.cpp:294 msgid "Kikuyu" msgstr "Kikuyu" #: ..\..\radiant\settings\LanguageManager.cpp:295 msgid "Kuanyama" msgstr "Kwanyama" #: ..\..\radiant\settings\LanguageManager.cpp:296 msgid "Kazakh" msgstr "Kasachisch" #: ..\..\radiant\settings\LanguageManager.cpp:297 msgid "Kalaallisut; Greenlandic" msgstr "Grönländisch" #: ..\..\radiant\settings\LanguageManager.cpp:298 msgid "Central Khmer" msgstr "Khmer" #: ..\..\radiant\settings\LanguageManager.cpp:299 msgid "Kannada" msgstr "Kanaresisch" #: ..\..\radiant\settings\LanguageManager.cpp:300 msgid "Korean" msgstr "Koreanisch" #: ..\..\radiant\settings\LanguageManager.cpp:301 msgid "Kanuri" msgstr "Kanuri" #: ..\..\radiant\settings\LanguageManager.cpp:302 msgid "Kashmiri" msgstr "Kashmiri" #: ..\..\radiant\settings\LanguageManager.cpp:303 msgid "Kurdish" msgstr "Kurdisch" #: ..\..\radiant\settings\LanguageManager.cpp:304 msgid "Komi" msgstr "Komi" #: ..\..\radiant\settings\LanguageManager.cpp:305 msgid "Cornish" msgstr "Kornisch" #: ..\..\radiant\settings\LanguageManager.cpp:306 msgid "Kirghiz" msgstr "Kirgisisch" #: ..\..\radiant\settings\LanguageManager.cpp:307 msgid "Latin" msgstr "Lateinisch" #: ..\..\radiant\settings\LanguageManager.cpp:308 msgid "Luxembourgish" msgstr "Luxemburgisch" #: ..\..\radiant\settings\LanguageManager.cpp:309 msgid "Ganda" msgstr "Luganda" #: ..\..\radiant\settings\LanguageManager.cpp:310 msgid "Limburgan" msgstr "Limburgisch" #: ..\..\radiant\settings\LanguageManager.cpp:311 msgid "Lingala" msgstr "Lingála" #: ..\..\radiant\settings\LanguageManager.cpp:312 msgid "Lao" msgstr "Lao" #: ..\..\radiant\settings\LanguageManager.cpp:313 msgid "Lithuanian" msgstr "Litauisch" #: ..\..\radiant\settings\LanguageManager.cpp:314 msgid "Luba-Katanga" msgstr "Luba-Katanga" #: ..\..\radiant\settings\LanguageManager.cpp:315 msgid "Latvian" msgstr "Lettisch" #: ..\..\radiant\settings\LanguageManager.cpp:316 msgid "Malagasy" msgstr "Malagasy" #: ..\..\radiant\settings\LanguageManager.cpp:317 msgid "Marshallese" msgstr "Marshallesisch" #: ..\..\radiant\settings\LanguageManager.cpp:318 msgid "Maori" msgstr "Maori" #: ..\..\radiant\settings\LanguageManager.cpp:319 msgid "Macedonian" msgstr "Mazedonisch" #: ..\..\radiant\settings\LanguageManager.cpp:320 msgid "Malayalam" msgstr "Malayalam" #: ..\..\radiant\settings\LanguageManager.cpp:321 msgid "Mongolian" msgstr "Mongolisch" #: ..\..\radiant\settings\LanguageManager.cpp:322 msgid "Marathi" msgstr "Marathi" #: ..\..\radiant\settings\LanguageManager.cpp:323 msgid "Malay" msgstr "Malaiisch" #: ..\..\radiant\settings\LanguageManager.cpp:324 msgid "Maltese" msgstr "Maltesisch" #: ..\..\radiant\settings\LanguageManager.cpp:325 msgid "Burmese" msgstr "Burmesisch" #: ..\..\radiant\settings\LanguageManager.cpp:326 msgid "Nauru" msgstr "Nauruisch" #: ..\..\radiant\settings\LanguageManager.cpp:327 msgid "Ndebele, North" msgstr "IsiNdebele" #: ..\..\radiant\settings\LanguageManager.cpp:328 msgid "Nepali" msgstr "Nepalesisch" #: ..\..\radiant\settings\LanguageManager.cpp:329 msgid "Ndonga" msgstr "Ndonga" #: ..\..\radiant\settings\LanguageManager.cpp:330 msgid "Dutch" msgstr "Dänisch" #: ..\..\radiant\settings\LanguageManager.cpp:331 msgid "Norwegian" msgstr "Norwegisch" #: ..\..\radiant\settings\LanguageManager.cpp:332 msgid "Ndebele, South" msgstr "Süd-Ndebele" #: ..\..\radiant\settings\LanguageManager.cpp:333 msgid "Navajo" msgstr "Navajo" #: ..\..\radiant\settings\LanguageManager.cpp:334 msgid "Chichewa" msgstr "Chichewa" #: ..\..\radiant\settings\LanguageManager.cpp:335 msgid "Occitan" msgstr "Okzitanisch" #: ..\..\radiant\settings\LanguageManager.cpp:336 msgid "Ojibwa" msgstr "Ojibwe" #: ..\..\radiant\settings\LanguageManager.cpp:337 msgid "Oromo" msgstr "Oromo" #: ..\..\radiant\settings\LanguageManager.cpp:338 msgid "Oriya" msgstr "Oriya" #: ..\..\radiant\settings\LanguageManager.cpp:339 msgid "Ossetian" msgstr "Ossetisch" #: ..\..\radiant\settings\LanguageManager.cpp:340 msgid "Panjabi" msgstr "Panjabi" #: ..\..\radiant\settings\LanguageManager.cpp:341 msgid "Pali" msgstr "Pali" #: ..\..\radiant\settings\LanguageManager.cpp:342 msgid "Polish" msgstr "Polnisch" #: ..\..\radiant\settings\LanguageManager.cpp:343 msgid "Pushto" msgstr "Paschtunisch" #: ..\..\radiant\settings\LanguageManager.cpp:344 msgid "Portuguese" msgstr "Portugiesisch" #: ..\..\radiant\settings\LanguageManager.cpp:345 msgid "Quechua" msgstr "Quechua" #: ..\..\radiant\settings\LanguageManager.cpp:346 msgid "Romansh" msgstr "Rätoromanisch" #: ..\..\radiant\settings\LanguageManager.cpp:347 msgid "Rundi" msgstr "Kirundi" #: ..\..\radiant\settings\LanguageManager.cpp:348 msgid "Romanian" msgstr "Rumänisch" #: ..\..\radiant\settings\LanguageManager.cpp:349 msgid "Russian" msgstr "Russisch" #: ..\..\radiant\settings\LanguageManager.cpp:350 msgid "Kinyarwanda" msgstr "Kinyarwanda" #: ..\..\radiant\settings\LanguageManager.cpp:351 msgid "Sanskrit" msgstr "Sanskrit" #: ..\..\radiant\settings\LanguageManager.cpp:352 msgid "Sardinian" msgstr "Sardisch" #: ..\..\radiant\settings\LanguageManager.cpp:353 msgid "Sindhi" msgstr "Sindhi" #: ..\..\radiant\settings\LanguageManager.cpp:354 msgid "Northern Sami" msgstr "Nordsamisch" #: ..\..\radiant\settings\LanguageManager.cpp:355 msgid "Sango" msgstr "Sango" #: ..\..\radiant\settings\LanguageManager.cpp:356 msgid "Sinhala" msgstr "Singhalesisch" #: ..\..\radiant\settings\LanguageManager.cpp:357 msgid "Slovak" msgstr "Slowakisch" #: ..\..\radiant\settings\LanguageManager.cpp:358 msgid "Slovenian" msgstr "Slowenisch" #: ..\..\radiant\settings\LanguageManager.cpp:359 msgid "Samoan" msgstr "Samoisch" #: ..\..\radiant\settings\LanguageManager.cpp:360 msgid "Shona" msgstr "Shona" #: ..\..\radiant\settings\LanguageManager.cpp:361 msgid "Somali" msgstr "Somali" #: ..\..\radiant\settings\LanguageManager.cpp:362 msgid "Albanian" msgstr "Albanisch" #: ..\..\radiant\settings\LanguageManager.cpp:363 msgid "Serbian" msgstr "Serbisch" #: ..\..\radiant\settings\LanguageManager.cpp:364 msgid "Swati" msgstr "Siswati" #: ..\..\radiant\settings\LanguageManager.cpp:365 msgid "Sotho, Southern" msgstr "Süd-Sotho" #: ..\..\radiant\settings\LanguageManager.cpp:366 msgid "Sundanese" msgstr "Sundanesisch" #: ..\..\radiant\settings\LanguageManager.cpp:367 msgid "Swedish" msgstr "Schwedisch" #: ..\..\radiant\settings\LanguageManager.cpp:368 msgid "Swahili" msgstr "Swahili" #: ..\..\radiant\settings\LanguageManager.cpp:369 msgid "Tamil" msgstr "Tamilisch" #: ..\..\radiant\settings\LanguageManager.cpp:370 msgid "Telugu" msgstr "Telugu" #: ..\..\radiant\settings\LanguageManager.cpp:371 msgid "Tajik" msgstr "Tadschikisch" #: ..\..\radiant\settings\LanguageManager.cpp:372 msgid "Thai" msgstr "Thailändisch" #: ..\..\radiant\settings\LanguageManager.cpp:373 msgid "Tigrinya" msgstr "Tigrinya" #: ..\..\radiant\settings\LanguageManager.cpp:374 msgid "Turkmen" msgstr "Turkmenisch" #: ..\..\radiant\settings\LanguageManager.cpp:375 msgid "Tagalog" msgstr "Tagalog" #: ..\..\radiant\settings\LanguageManager.cpp:376 msgid "Tswana" msgstr "Tswana" #: ..\..\radiant\settings\LanguageManager.cpp:377 msgid "Tonga" msgstr "Tonga" #: ..\..\radiant\settings\LanguageManager.cpp:378 msgid "Turkish" msgstr "Türkisch" #: ..\..\radiant\settings\LanguageManager.cpp:379 msgid "Tsonga" msgstr "Tsonga" #: ..\..\radiant\settings\LanguageManager.cpp:380 msgid "Tatar" msgstr "Tatarisch" #: ..\..\radiant\settings\LanguageManager.cpp:381 msgid "Twi" msgstr "Twi" #: ..\..\radiant\settings\LanguageManager.cpp:382 msgid "Tahitian" msgstr "Tahitianisch" #: ..\..\radiant\settings\LanguageManager.cpp:383 msgid "Uighur" msgstr "Uigurisch" #: ..\..\radiant\settings\LanguageManager.cpp:384 msgid "Ukrainian" msgstr "Ukrainisch" #: ..\..\radiant\settings\LanguageManager.cpp:385 msgid "Urdu" msgstr "Urdu" #: ..\..\radiant\settings\LanguageManager.cpp:386 msgid "Uzbek" msgstr "Usbekisch" #: ..\..\radiant\settings\LanguageManager.cpp:387 msgid "Venda" msgstr "Venda" #: ..\..\radiant\settings\LanguageManager.cpp:388 msgid "Vietnamese" msgstr "Vietnamesisch" #: ..\..\radiant\settings\LanguageManager.cpp:389 msgid "Volapuek" msgstr "Volapük" #: ..\..\radiant\settings\LanguageManager.cpp:390 msgid "Walloon" msgstr "Wallonisch" #: ..\..\radiant\settings\LanguageManager.cpp:391 msgid "Wolof" msgstr "Wolof" #: ..\..\radiant\settings\LanguageManager.cpp:392 msgid "Xhosa" msgstr "Xhosa" #: ..\..\radiant\settings\LanguageManager.cpp:393 msgid "Yiddish" msgstr "Yiddisch" #: ..\..\radiant\settings\LanguageManager.cpp:394 msgid "Yoruba" msgstr "Yoruba" #: ..\..\radiant\settings\LanguageManager.cpp:395 msgid "Zhuang" msgstr "Zhuang" #: ..\..\radiant\settings\LanguageManager.cpp:396 msgid "Chinese" msgstr "Chinesisch" #: ..\..\radiant\settings\LanguageManager.cpp:397 msgid "Zulu" msgstr "Zulu" #: ..\..\radiant\settings\PreferencePage.cpp:18 msgid "{0} Settings" msgstr "{0} Einstellungen" #: ..\..\radiant\textool\TexTool.cpp:38 msgid "Texture Tool" msgstr "Textur-Werkzeug" #: ..\..\radiant\ui\aas\AasControl.cpp:36 msgid "Reload AAS File" msgstr "AAS File neu laden" #: ..\..\radiant\ui\aas\AasControlDialog.cpp:30 msgid "AAS Viewer" msgstr "AAS Visualisierung" #: ..\..\radiant\ui\aas\AasControlDialog.cpp:80 msgid "Search for AAS Files" msgstr "Suche AAS Files" #: ..\..\radiant\ui\aas\AasControlDialog.cpp:84 msgid "Show Area Numbers" msgstr "AAS-Nummern anzeigen" #: ..\..\radiant\ui\aas\AasControlDialog.cpp:87 msgid "Hide distant Areas" msgstr "Weit entfernte ausblenden" #: ..\..\radiant\ui\about\AboutDialog.cpp:26 msgid "About DarkRadiant" msgstr "Über DarkRadiant" #: ..\..\radiant\ui\about\AboutDialog.cpp:61 msgid "Build date: {0}" msgstr "Kompilierungsdatum: {0}" #: ..\..\radiant\ui\about\AboutDialog.cpp:70 msgid "Version: {0:d}.{1:d}.{2:d}" msgstr "Version: {0:d}.{1:d}.{2:d}" #: ..\..\radiant\ui\about\AboutDialog.cpp:80 msgid "Vendor: {0}" msgstr "Hersteller: {0}" #: ..\..\radiant\ui\about\AboutDialog.cpp:81 msgid "Version: {0}" msgstr "Version: {0}" #: ..\..\radiant\ui\about\AboutDialog.cpp:82 msgid "Renderer: {0}" msgstr "Renderer: {0}" #: ..\..\radiant\ui\brush\QuerySidesDialog.cpp:15 msgid "Enter Number of Sides" msgstr "Anzahl der Seiten festlegen" #: ..\..\radiant\ui\brush\QuerySidesDialog.cpp:50 msgid "Number of sides: " msgstr "Anzahl der Seitenflächen:" #: ..\..\radiant\ui\commandlist\CommandList.cpp:20 msgid "Shortcut List" msgstr "Liste der Tastenkombinationen" #: ..\..\radiant\ui\commandlist\CommandList.cpp:57 #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:123 #: xml_file_content.cpp:2 msgid "Command" msgstr "Befehl" #: ..\..\radiant\ui\commandlist\CommandList.cpp:59 #: ..\..\libs\wxutil\KeyValueTable.cpp:41 msgid "Key" msgstr "Schlüssel" #: ..\..\radiant\ui\commandlist\CommandList.cpp:92 msgid "Reset to Default" msgstr "Auf Standardwerte zurücksetzen" #: ..\..\radiant\ui\commandlist\CommandList.cpp:133 msgid "Enter new Shortcut" msgstr "Neue Tastenkombination festlegen" #: ..\..\radiant\ui\commandlist\CommandList.cpp:184 #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:185 msgid "Reset to default?" msgstr "Auf Standard zurücksetzen?" #: ..\..\radiant\ui\commandlist\CommandList.cpp:185 #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:186 msgid "" "Really clear all bindings and reload\n" "them from the default settings?" msgstr "" "Wirklich alle Belegungen auf die\n" "Standardeinstellungen zurücksetzen?" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:39 msgid "Note: This shortcut is already assigned to:" msgstr "Hinweis: diese Kombination ist bereits zugeordnet:" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:49 #: ..\..\plugins\script\ScriptWindow.cpp:85 #: xml_file_content.cpp:12 #: xml_file_content.cpp:3 #: xml_file_content.cpp:7 #: xml_file_content.cpp:10 #: xml_file_content.cpp:18 #: xml_file_content.cpp:8 #: xml_file_content.cpp:6 #: xml_file_content.cpp:14 #: xml_file_content.cpp:15 msgid "OK" msgstr "OK" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:52 #: xml_file_content.cpp:2 #: xml_file_content.cpp:6 #: xml_file_content.cpp:9 #: xml_file_content.cpp:17 #: xml_file_content.cpp:1 #: xml_file_content.cpp:14 #: xml_file_content.cpp:5 #: xml_file_content.cpp:13 #: xml_file_content.cpp:20 msgid "Cancel" msgstr "Abbrechen" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:136 msgid "" "The specified shortcut is already assigned to {0}\n" "Overwrite the current setting and assign this shortcut to {1} instead?" msgstr "" "Diese Tastenkombination ist bereits {0} zugeordnet.\n" "Soll diese Assoziation aufgehoben werden und die Tastenkombination\n" "stattdessen {1} zugeordnet werden?" #: ..\..\radiant\ui\commandlist\ShortcutChooser.cpp:142 msgid "Overwrite existing shortcut?" msgstr "Bestehende Tastenkombination überschreiben?" #: ..\..\radiant\ui\common\CommandEntry.cpp:26 msgid "Go" msgstr "Los" #: ..\..\radiant\ui\common\EntityChooser.cpp:16 msgid "Select Entity" msgstr "Entity auswählen" #: ..\..\radiant\ui\common\ShaderChooser.cpp:15 msgid "Choose Shader" msgstr "Shader auswählen" #: ..\..\radiant\ui\common\ShaderDefinitionView.cpp:23 msgid "Material:" msgstr "Material:" #: ..\..\radiant\ui\common\ShaderDefinitionView.cpp:24 msgid "Defined in:" msgstr "Definiert in:" #: ..\..\radiant\ui\common\ShaderDefinitionView.cpp:41 msgid "Definition:" msgstr "Definition:" #: ..\..\radiant\ui\common\ShaderDefinitionView.cpp:91 msgid "View Shader Definition" msgstr "Shaderdefinition ansehen" #: ..\..\radiant\ui\common\ShaderSelector.cpp:192 #: ..\..\radiant\ui\common\ShaderSelector.cpp:222 #: ..\..\radiant\ui\einspector\EntityInspector.cpp:476 #: ..\..\plugins\eclasstree\EClassTree.cpp:143 #: ..\..\libs\wxutil\KeyValueTable.cpp:44 msgid "Value" msgstr "Wert" #: ..\..\radiant\ui\common\ShaderSelector.cpp:220 msgid "Attribute" msgstr "Attribut" #: ..\..\radiant\ui\common\ShaderSelector.cpp:357 #: ..\..\radiant\ui\common\ShaderSelector.cpp:375 #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:74 msgid "Defined in" msgstr "Definiert in" #: ..\..\radiant\ui\common\ShaderSelector.cpp:358 #: ..\..\radiant\ui\common\ShaderSelector.cpp:391 #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:75 #: ..\..\plugins\dm.editing\AIVocalSetChooserDialog.cpp:53 #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:133 #: xml_file_content.cpp:1 msgid "Description" msgstr "Beschreibung" #: ..\..\radiant\ui\common\ShaderSelector.cpp:366 #: ..\..\plugins\dm.conversation\CommandEditor.cpp:222 msgid "None" msgstr "Keine" #: ..\..\radiant\ui\common\ShaderSelector.cpp:374 msgid "Image map" msgstr "Bild" #: ..\..\radiant\ui\common\ShaderSelector.cpp:388 msgid "Light flags" msgstr "Licht-Flags" #: ..\..\radiant\ui\common\TexturePreviewCombo.cpp:44 msgid "Copy shader name" msgstr "Kopiere Shadernamen" #: ..\..\radiant\ui\einspector\AddPropertyDialog.cpp:27 msgid "Add property" msgstr "Eigenschaft hinzufügen" #: ..\..\radiant\ui\einspector\AddPropertyDialog.cpp:31 msgid "Custom properties defined for this entity class, if any" msgstr "Benutzerdefinierte Eigenschaften dieser Entityklasse, falls definiert" #: ..\..\radiant\ui\einspector\AddPropertyDialog.cpp:76 #: ..\..\radiant\ui\einspector\EntityInspector.cpp:471 #: ..\..\plugins\eclasstree\EClassTree.cpp:140 msgid "Property" msgstr "Eigenschaft" #: ..\..\radiant\ui\einspector\ClassnamePropertyEditor.cpp:26 msgid "Choose entity class..." msgstr "Entityklasse auswählen" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:103 msgid "Show inherited properties" msgstr "Zeige vererbte Eigenschaften" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:107 msgid "Show help" msgstr "Zeige Hilfe" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:313 msgid "Add property..." msgstr "Eigenschaft hinzufügen..." #: ..\..\radiant\ui\einspector\EntityInspector.cpp:318 msgid "Delete property" msgstr "Eigenschaft entfernen" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:326 msgid "Copy Spawnarg(s)" msgstr "Wertepaar(e) kopieren" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:331 msgid "Cut Spawnarg(s)" msgstr "Wertepaar(e) ausschneiden" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:336 msgid "Paste Spawnarg(s)" msgstr "Wertepaar(e) einfügen" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:348 #: ..\..\radiant\ui\einspector\EntityInspector.cpp:351 #: ..\..\plugins\uimanager\GroupDialog.cpp:30 msgid "Entity" msgstr "Entity" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:481 msgid "?" msgstr "?" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:1194 msgid "Entity {0}" msgstr "Entity {0}" #: ..\..\radiant\ui\einspector\EntityInspector.cpp:1205 msgid "Entity {0}, Primitive {1}" msgstr "Entity {0}, Primitiv {1}" #: ..\..\radiant\ui\einspector\EntityPropertyEditor.cpp:31 msgid "Choose target entity..." msgstr "Ziel-Entity auswählen..." #: ..\..\radiant\ui\einspector\FloatPropertyEditor.cpp:70 #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:62 msgid "Apply..." msgstr "Anwenden..." #: ..\..\radiant\ui\einspector\LightTextureChooser.cpp:48 msgid "Choose texture" msgstr "Textur auswählen" #: ..\..\radiant\ui\einspector\ModelPropertyEditor.cpp:40 msgid "Choose model..." msgstr "Modell auswählen..." #: ..\..\radiant\ui\einspector\ModelPropertyEditor.cpp:45 msgid "Choose particle..." msgstr "Partikel auswählen..." #: ..\..\radiant\ui\einspector\ModelPropertyEditor.cpp:80 msgid "Warning: " msgstr "Achtung:" #: ..\..\radiant\ui\einspector\ModelPropertyEditor.cpp:81 msgid "" "Changing this entity's model to the selected value will\n" "remove all child primitives from it:\n" msgstr "" "Wenn die \"model\"-Eigenschaft dieser Entity auf den gewählten Wert\n" "festgelegt wird, werden alle ihr untergeordneten Primitive gelöscht:\n" #: ..\..\radiant\ui\einspector\SkinChooser.cpp:26 msgid "Choose Skin" msgstr "Skin auswählen" #: ..\..\radiant\ui\einspector\SkinChooser.cpp:84 msgid "Skin" msgstr "Skin" #: ..\..\radiant\ui\einspector\SkinChooser.cpp:196 msgid "Matching skins" msgstr "Passende Skins" #: ..\..\radiant\ui\einspector\SkinChooser.cpp:219 msgid "All skins" msgstr "Alle Skins" #: ..\..\radiant\ui\einspector\SkinPropertyEditor.cpp:23 #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:110 msgid "Choose skin..." msgstr "Skin auswählen..." #: ..\..\radiant\ui\einspector\SoundPropertyEditor.cpp:23 msgid "Choose sound..." msgstr "Sound auswählen..." #: ..\..\radiant\ui\einspector\TexturePropertyEditor.cpp:23 msgid "Choose texture..." msgstr "Textur auswählen..." #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:54 msgid "X: " msgstr "X:" #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:56 msgid " Y: " msgstr "Y:" #: ..\..\radiant\ui\einspector\Vector3PropertyEditor.cpp:58 msgid " Z: " msgstr "Z:" #: ..\..\radiant\ui\entitychooser\EntityClassChooser.cpp:25 msgid "Create entity" msgstr "Neue Entity" #: ..\..\radiant\ui\entitychooser\EntityClassChooser.cpp:282 #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:374 #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:397 #: ..\..\plugins\uimanager\SoundChooser.cpp:191 msgid "Loading..." msgstr "Lade..." #: ..\..\radiant\ui\entitychooser\EntityClassChooser.cpp:293 #: ..\..\plugins\eclasstree\EClassTree.cpp:127 msgid "Classname" msgstr "Klassenname" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:21 msgid "Filter Settings" msgstr "Filtereinstellungen" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:138 msgid "enabled" msgstr "aktiviert" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:138 msgid "disabled" msgstr "deaktiviert" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:182 #: ..\..\plugins\dm.conversation\ConversationDialog.cpp:78 #: ..\..\plugins\entitylist\EntityList.cpp:72 #: xml_file_content.cpp:1 msgid "Name" msgstr "Name" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:185 msgid "State" msgstr "Zustand" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:265 msgid "NewFilter" msgstr "NeuerFilter" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:283 #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:353 msgid "Empty Filter" msgstr "Leerer Filter" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:284 msgid "No rules defined for this filter, cannot insert." msgstr "Kann diesen Filter nicht einfügen, keine Regeln definiert." #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:297 msgid "Name Conflict" msgstr "Namenskonflikt" #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:298 msgid "Cannot add, filter with same name already exists." msgstr "Kann diesen Filter nicht hinzufügen, ein weiterer Filter mit demselben Namen existiert bereits." #: ..\..\radiant\ui\filterdialog\FilterDialog.cpp:354 msgid "No rules defined for this filter. Delete it?" msgstr "Für diesen Filter sind keine Regeln definiert, soll der Filter gelöscht werden?" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:16 msgid "Edit Filter" msgstr "Filter bearbeiten" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:17 msgid "View Filter" msgstr "Filter anzeigen" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:111 #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:161 #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:306 msgid "show" msgstr "zeige" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:111 #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:162 msgid "hide" msgstr "verstecke" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:132 msgid "Index" msgstr "Index" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:145 #: ..\..\plugins\dm.objectives\ComponentsDialog.cpp:119 #: ..\..\plugins\dm.stimresponse\ClassEditor.cpp:66 #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:91 #: xml_file_content.cpp:19 msgid "Type" msgstr "Typ" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:152 msgid "Entity Key" msgstr "Entity Key" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:156 msgid "Match" msgstr "Ausdruck" #: ..\..\radiant\ui\filterdialog\FilterEditor.cpp:167 msgid "Action" msgstr "Aktion" #: ..\..\radiant\ui\findshader\FindShader.cpp:27 msgid "Find & Replace Shader" msgstr "Shader Suchen & Ersetzen" #: ..\..\radiant\ui\findshader\FindShader.cpp:28 msgid "{0:d} shader(s) replaced." msgstr "{0:d} Shader ersetzt." #: ..\..\radiant\ui\findshader\FindShader.cpp:34 msgid "" "When picking texture names, click the pick button and use {0}\n" "in the camera view to pick a material name. The picked texture will be\n" "filled in the entry box next to the activated button." msgstr "" "Um Texturen per Klick auszuwählen, aktiviere den \"Auswählen\"-Button\n" "und verwende {0} in der Kamera-Ansicht um den Materialnamen auszuwählen.\n" "Der gewählte Texturename wird dann in dem Textfeld\n" "neben dem gewählten Button eingetragen." #: ..\..\radiant\ui\findshader\FindShader.cpp:137 msgid "Picking Texures" msgstr "Texturauswahl per Mausklick" #: ..\..\radiant\ui\grid\GridManager.cpp:64 msgid "Current Grid Size" msgstr "Aktuelle Rastergröße" #: ..\..\radiant\ui\grid\GridManager.cpp:155 msgid "Settings/Grid" msgstr "Einstellungen/Raster" #: ..\..\radiant\ui\grid\GridManager.cpp:157 msgid "Default Grid Size" msgstr "Standard-Rastergröße" #: ..\..\radiant\ui\grid\GridManager.cpp:161 msgid "Lines" msgstr "Linien" #: ..\..\radiant\ui\grid\GridManager.cpp:162 msgid "Dotted Lines" msgstr "Gepunktete Linien" #: ..\..\radiant\ui\grid\GridManager.cpp:163 msgid "More Dotted Lines" msgstr "Stärker gepunktete Linien" #: ..\..\radiant\ui\grid\GridManager.cpp:164 msgid "Crosses" msgstr "Kreuze" #: ..\..\radiant\ui\grid\GridManager.cpp:165 msgid "Dots" msgstr "Punkte" #: ..\..\radiant\ui\grid\GridManager.cpp:166 msgid "Big Dots" msgstr "Große Punkte" #: ..\..\radiant\ui\grid\GridManager.cpp:167 msgid "Squares" msgstr "Quadrate" #: ..\..\radiant\ui\grid\GridManager.cpp:169 msgid "Major Grid Style" msgstr "Hauptgitter-Stil" #: ..\..\radiant\ui\grid\GridManager.cpp:170 msgid "Minor Grid Style" msgstr "Nebengitter-Stil" #: ..\..\radiant\ui\layers\LayerControl.cpp:50 msgid "Indicates whether anything among the current selection is part of this layer." msgstr "Zeigt an, ob irgendetwas aus der momentanen Auswahl Teil dieser Ebene ist." #: ..\..\radiant\ui\layers\LayerControl.cpp:70 msgid "Click to select all in layer, hold SHIFT to deselect, hold CTRL to set as active layer." msgstr "Klicken um alles in der Ebene auszuwählen, UMSCHALT beim Klicken gedrückt halten um Auswahl aufzuheben, STRG halten um die Ebene als aktive Ebene festzulegen." #: ..\..\radiant\ui\layers\LayerControl.cpp:71 msgid "Rename this layer" msgstr "Diese Ebene umbenennen" #: ..\..\radiant\ui\layers\LayerControl.cpp:72 msgid "Delete this layer" msgstr "Diese Ebene löschen" #: ..\..\radiant\ui\layers\LayerControl.cpp:73 msgid "Toggle layer visibility" msgstr "Ebenensichtbarkeit umkehren" #: ..\..\radiant\ui\layers\LayerControl.cpp:159 msgid "Do you really want to delete this layer?" msgstr "Willst Du diese Ebene wirklich löschen?" #: ..\..\radiant\ui\layers\LayerControl.cpp:163 msgid "Confirm Layer Deletion" msgstr "Ebenenlöschung bestätigen" #: ..\..\radiant\ui\layers\LayerControl.cpp:184 msgid "Rename Layer" msgstr "Ebene umbenennen" #: ..\..\radiant\ui\layers\LayerControl.cpp:185 msgid "Enter new Layer Name" msgstr "Neuen Ebenennamen eingeben" #: ..\..\radiant\ui\layers\LayerControl.cpp:206 msgid "Could not rename layer, please try again." msgstr "Konnte Ebene nicht umbenennen, bitte nochmals versuchen." #: ..\..\radiant\ui\layers\LayerControlDialog.cpp:34 #: ..\..\radiant\ui\mapinfo\LayerInfoTab.cpp:18 #: xml_file_content.cpp:51 msgid "Layers" msgstr "Ebenen" #: ..\..\radiant\ui\layers\LayerControlDialog.cpp:74 msgid "Show all" msgstr "Alle anzeigen" #: ..\..\radiant\ui\layers\LayerControlDialog.cpp:75 msgid "Hide all" msgstr "Alle ausblenden" #: ..\..\radiant\ui\layers\LayerControlDialog.cpp:81 #: xml_file_content.cpp:2 msgid "New" msgstr "Neu" #: ..\..\radiant\ui\lightinspector\LightInspector.cpp:29 msgid "Light properties" msgstr "Lichteigenschaften" #: ..\..\radiant\ui\mainframe\EmbeddedLayout.cpp:76 #: ..\..\radiant\ui\mainframe\FloatingLayout.cpp:59 #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:111 #: xml_file_content.cpp:53 msgid "Texture Browser" msgstr "Texturbrowser" #: ..\..\radiant\ui\mainframe\EmbeddedLayout.cpp:79 #: ..\..\radiant\ui\mainframe\FloatingLayout.cpp:62 #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:114 msgid "Textures" msgstr "Texturen" #: ..\..\radiant\ui\mainframe\LayoutCommand.h:56 msgid "Window Layout" msgstr "Fensterlayout" #: ..\..\radiant\ui\mainframe\LayoutCommand.h:80 msgid "Restart required" msgstr "Neustart erforderlich" #: ..\..\radiant\ui\mainframe\LayoutCommand.h:81 msgid "Restart DarkRadiant to apply changes" msgstr "DarkRadiant neu starten um die Änderungen anzuwenden" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:83 msgid "Settings/Multi Monitor" msgstr "Einstellungen/Multimonitor" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:102 msgid "Start DarkRadiant on monitor" msgstr "Starte DarkRadiant auf Monitor" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:124 msgid "Settings/Compatibility" msgstr "Einstellungen/Kompatibilität" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:126 msgid "Disable Windows Desktop Composition" msgstr "Desktopgestaltung deaktivieren" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:284 msgid "Exit DarkRadiant" msgstr "DarkRadiant beenden" #: ..\..\radiant\ui\mainframe\MainFrame.cpp:394 #: ..\..\radiant\ui\mainframe\MainFrame.cpp:397 msgid "Console" msgstr "Konsole" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:145 msgid "Camera Position" msgstr "Kamera-Position" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:148 msgid "Top Left" msgstr "Oben links" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:150 msgid "Top Right" msgstr "Oben rechts" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:152 msgid "Bottom Left" msgstr "Unten links" #: ..\..\radiant\ui\mainframe\SplitPaneLayout.cpp:154 msgid "Bottom Right" msgstr "Unten rechts" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:17 msgid "Entities" msgstr "Entities" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:48 msgid "Entity Class" msgstr "Entityklasse" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:51 #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:51 msgid "Count" msgstr "Anzahl" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:70 msgid "Brushes:" msgstr "Brushes:" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:71 msgid "Patches:" msgstr "Patches:" #: ..\..\radiant\ui\mapinfo\EntityInfoTab.cpp:72 msgid "Entities:" msgstr "Entities:" #: ..\..\radiant\ui\mapinfo\LayerInfoTab.cpp:49 msgid "Layer" msgstr "Ebene" #: ..\..\radiant\ui\mapinfo\LayerInfoTab.cpp:52 msgid "Node Count" msgstr "Knotenanzahl" #: ..\..\radiant\ui\mapinfo\MapInfoDialog.cpp:23 msgid "Map Info" msgstr "Map-Information" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:14 msgid "Models" msgstr "Modelle" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:45 msgid "Model" msgstr "Modell" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:48 msgid "Polys" msgstr "Polygone" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:54 msgid "Skins" msgstr "Skins" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:75 msgid "Models used:" msgstr "Benutzte Modelle:" #: ..\..\radiant\ui\mapinfo\ModelInfoTab.cpp:76 msgid "Named Skins used:" msgstr "Benutzte Skins:" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:18 msgid "Shaders" msgstr "Shader" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:20 #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:63 msgid "Select elements using this shader" msgstr "Alle Elemente mit diesem Shader auswählen" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:21 #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:64 msgid "Deselect elements using this shader" msgstr "Alle Elemente mit diesem Shader deselektieren" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:63 msgid "Faces" msgstr "Faces" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:66 msgid "Patches" msgstr "Patches" #: ..\..\radiant\ui\mapinfo\ShaderInfoTab.cpp:86 msgid "Shaders used:" msgstr "Benutzte Shader:" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:52 msgid "Load in Textures view" msgstr "In Texturbrowser laden" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:55 msgid "Apply to selection" msgstr "Auf Auswahl anwenden" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:58 msgid "Show Shader Definition" msgstr "Zeige Shaderdefinition" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:61 msgid "Other Materials" msgstr "Andere Shader" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:433 #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:436 msgid "Media" msgstr "Media" #: ..\..\radiant\ui\mediabrowser\MediaBrowser.cpp:529 #: ..\..\plugins\eclasstree\EClassTree.cpp:43 msgid "Loading, please wait..." msgstr "Lade, bitte warten..." #: ..\..\radiant\ui\mediabrowser\TextureDirectoryLoader.h:35 msgid "Loading textures" msgstr "Lade Texturen" #: ..\..\radiant\ui\menu\FiltersMenu.cpp:64 msgid "Activate &all Filters" msgstr "Aktiviere &alle Filter" #: ..\..\radiant\ui\menu\FiltersMenu.cpp:65 msgid "&Deactivate all Filters" msgstr "&Deaktiviere alle Filter" #: ..\..\radiant\ui\menu\FiltersMenu.cpp:68 msgid "Edit Filters..." msgstr "Filter bearbeiten..." #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:33 msgid "Model Export" msgstr "Model-Export" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:170 msgid "Empty Filename" msgstr "Leerer Dateiname" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:170 msgid "No filename specified, cannot run exporter" msgstr "Kein Zieldateipfad angegeben, exportieren nicht möglich." #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:185 msgid "Export failed" msgstr "Export fehlgeschlagen" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:266 msgid "Empty Selection" msgstr "Keine Auswahl" #: ..\..\radiant\ui\modelexport\ExportAsModelDialog.cpp:266 msgid "Nothing selected, cannot run exporter" msgstr "Nichts ausgewählt, exportieren nicht möglich" #: ..\..\radiant\ui\modelselector\MaterialsList.cpp:45 msgid "Material" msgstr "Material" #: ..\..\radiant\ui\modelselector\MaterialsList.cpp:47 #: xml_file_content.cpp:8 msgid "Visible" msgstr "Sichtbar" #: ..\..\radiant\ui\modelselector\ModelPopulator.h:98 msgid "Building tree..." msgstr "Baue Hierarchie auf..." #: ..\..\radiant\ui\modelselector\ModelPopulator.h:143 #: ..\..\radiant\ui\modelselector\ModelPopulator.h:151 msgid "{0:d} models loaded" msgstr "{0:d} Modelle geladen" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:36 msgid "Choose Model" msgstr "Modell auswählen" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:351 msgid "Model Path" msgstr "Modell-Pfad" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:445 msgid "Model name" msgstr "Modellname" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:446 msgid "Skin name" msgstr "Skinname" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:447 msgid "Total vertices" msgstr "Gesamte Vertices" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:448 msgid "Total polys" msgstr "Gesamte Polygone" #: ..\..\radiant\ui\modelselector\ModelSelector.cpp:449 msgid "Material surfaces" msgstr "Anzahl Oberflächen" #: ..\..\radiant\ui\mousetool\BindToolDialog.cpp:16 msgid "Select new Binding: {0}" msgstr "Neue Tastenbelegung wählen: {0}" #: ..\..\radiant\ui\mousetool\BindToolDialog.cpp:44 msgid "" "Please select a new button/modifier combination\n" "by clicking on the area below." msgstr "" "Bitte eine neue Maus-/Tastenkombination\n" "durch Klick auf die Fläche unten wählen." #: ..\..\radiant\ui\mousetool\BindToolDialog.cpp:51 msgid "Click here to assign" msgstr "" "Hier klicken um neue\n" "Belegung zuzuweisen" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:23 msgid "Edit Mouse Bindings" msgstr "Maustastenbelegungen ändern" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:50 msgid "Reset all mappings to default" msgstr "Alle Belegungen zurücksetzen" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:72 msgid "Double click row to edit a binding" msgstr "Doppelklicken um eine Belegung zu ändern" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:122 msgid "Tool" msgstr "Werkzeug" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:125 msgid "Modifier" msgstr "Hilfstasten" #: ..\..\radiant\ui\mousetool\ToolMappingDialog.cpp:128 msgid "Button" msgstr "Schaltfläche" #: ..\..\radiant\ui\mru\MRU.cpp:26 msgid "Recently used Maps" msgstr "Zuletzt geöffnete Maps" #: ..\..\radiant\ui\mru\MRU.cpp:109 msgid "Could not read map file: {0}" msgstr "Konnte die Mapdatei nicht lesen: {0}" #: ..\..\radiant\ui\mru\MRU.cpp:125 msgid "Settings/Map Files" msgstr "Einstellungen/Map" #: ..\..\radiant\ui\mru\MRU.cpp:127 msgid "Number of most recently used files" msgstr "Anzahl der zuletzt geöffneten Dateien" #: ..\..\radiant\ui\mru\MRU.cpp:128 msgid "Open last map on startup" msgstr "Lade zuletzt geöffnete Map beim Start" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:52 msgid "Create entity..." msgstr "Neue Entity..." #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:54 msgid "Create player start here" msgstr "Erzeuge Spielerstartpunkt hier" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:56 msgid "Move player start here" msgstr "Verschiebe Spielerstartpunkt hier" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:58 msgid "Create model..." msgstr "Neues Modell..." #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:60 msgid "Surround with monsterclip" msgstr "Mit Monsterclip einhüllen" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:62 msgid "Create light..." msgstr "Neues Licht..." #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:64 msgid "Insert prefab..." msgstr "Prefab einfügen..." #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:66 msgid "Create speaker..." msgstr "Neuer Speaker..." #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:68 msgid "Convert to func_static" msgstr "In func_static umwandeln" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:70 msgid "Reparent primitives" msgstr "Primitive neu zuordnen" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:71 msgid "Revert to worldspawn" msgstr "Zu Worldspawn umwandeln" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:72 msgid "Revert part to worldspawn" msgstr "Teil zu Worldspawn umwandeln" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:75 msgid "Merge Entities" msgstr "Entities zusammenführen" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:76 #: xml_file_content.cpp:157 msgid "Make Visportal" msgstr "Erzeuge Visportal" #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:325 msgid "Unable to create light, classname not found." msgstr "Kann Licht nicht erzeugen, Klassenname nicht gefunden." #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:352 msgid "Unable to create speaker, classname not found." msgstr "Kann Speaker nicht erzeugen, Klassenname nicht gefunden." #: ..\..\radiant\ui\ortho\OrthoContextMenu.cpp:433 msgid "Nothing must be selected for model creation" msgstr "Für diese Operation darf nichts ausgewählt sein." #: ..\..\radiant\ui\overlay\OverlayDialog.cpp:25 msgid "Background image" msgstr "Hintergrundbild" #: ..\..\radiant\ui\particles\ParticlesChooser.cpp:37 msgid "Choose particles" msgstr "Partikel auswählen" #: ..\..\radiant\ui\particles\ParticlesChooser.cpp:66 #: ..\..\plugins\particles\editor\ParticleEditor.cpp:159 msgid "Particle" msgstr "Partikel" #: ..\..\radiant\ui\patch\BulgePatchDialog.cpp:9 msgid "Bulge Patch" msgstr "Patch auswölben" #: ..\..\radiant\ui\patch\BulgePatchDialog.cpp:10 msgid "Noise:" msgstr "Amplitude:" #: ..\..\radiant\ui\patch\CapDialog.cpp:16 msgid "Create Cap Patch" msgstr "Patch-Cap erzeugen" #: ..\..\radiant\ui\patch\CapDialog.cpp:20 msgid "Bevel" msgstr "Bogen" #: ..\..\radiant\ui\patch\CapDialog.cpp:21 msgid "End Cap" msgstr "Doppelbogen" #: ..\..\radiant\ui\patch\CapDialog.cpp:22 msgid "Inverted Bevel" msgstr "Invertierter Bogen" #: ..\..\radiant\ui\patch\CapDialog.cpp:23 msgid "Inverted Endcap" msgstr "Invertierter Doppelbogen" #: ..\..\radiant\ui\patch\CapDialog.cpp:24 msgid "Cylinder" msgstr "Zylinder" #: ..\..\radiant\ui\patch\PatchCreateDialog.cpp:13 msgid "Create Flat Patch Mesh" msgstr "Neuer flacher Patch" #: ..\..\radiant\ui\patch\PatchInspector.cpp:29 msgid "Patch Inspector" msgstr "Patch Inspector" #: ..\..\radiant\ui\patch\PatchInspector.cpp:30 #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:59 #: ..\..\radiant\ui\transform\TransformDialog.cpp:38 msgid "Step:" msgstr "Schritt:" #: ..\..\radiant\ui\patch\PatchThickenDialog.cpp:14 msgid "Patch Thicken" msgstr "Patch-Extrusion" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:40 msgid "Choose Prefab" msgstr "Prefab wählen" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:98 msgid "Rescan Prefab Folders" msgstr "Prefab-Ordner neu durchsuchen" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:103 msgid "Create Group out of Prefab parts" msgstr "Eine Gruppe aus diesem Prefab erstellen" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:138 msgid "Browse mod resources" msgstr "Mod-Ressourcen durchsuchen" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:141 msgid "Select recently used path:" msgstr "Zuletzt geöffneten Pfad auswählen:" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:143 msgid "Browse custom path:" msgstr "Pfad durchsuchen:" #: ..\..\radiant\ui\prefabselector\PrefabSelector.cpp:555 msgid "" msgstr "" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:21 msgid "Game Setup" msgstr "Spiel-Einstellungen" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:33 msgid "Game Type:" msgstr "Spiel-Typ:" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:136 #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:152 msgid "Invalid Settings" msgstr "Ungültige Einstellungen" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:137 msgid "Please select a game type" msgstr "Bitte einen Spieltyp auswählen" #: ..\..\radiant\ui\prefdialog\GameSetupDialog.cpp:150 msgid "" "Warning:\n" "{0}\n" "Do you want to correct these settings?" msgstr "" "Warnung:\n" "{0}\n" "Möchtest Du diese Einstellungen korrigieren?" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:37 msgid "Engine Path" msgstr "Pfad zur Engine" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:42 msgid "Mod (fs_game)" msgstr "Mod (fs_game)" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:47 msgid "Mod Base (fs_game_base)" msgstr "Mod Base (fs_game_base)" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:70 #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:139 msgid "Engine path \"{0}\" does not exist.\n" msgstr "Engine-Pfad \"{0}\" existiert nicht.\n" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:77 msgid "The mod base path \"{0}\" does not exist.\n" msgstr "Der Mod-Base-Pfad \"{0}\" existiert nicht.\n" #: ..\..\radiant\ui\prefdialog\GameSetupPageIdTech.cpp:84 msgid "The mod path \"{0}\" does not exist.\n" msgstr "Der Mod-Pfad \"{0}\" existiert nicht.\n" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:37 msgid "DarkMod Path" msgstr "DarkMod Verzeichnis" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:45 msgid "This is the path where your TheDarkMod.exe is located." msgstr "An dieser stelle sollte sich TheDarkMod.exe befinden." #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:49 msgid "The FM folder name of the mission you want to work on, e.g. 'saintlucia'." msgstr "Der Ordnername der zu editierenden Mission, z.B. 'saintlucia'." #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:51 msgid "Mission" msgstr "Mission" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:98 msgid "" "The mission path {0} doesn't exist.\n" "Do you intend to start a new mission and create that folder?" msgstr "" "Der Missions-Ordner {0} existiert nicht.\n" "Willst Du eine neue Mission anfangen und den Ordner erzeugen?" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:101 msgid "Start a new Mission named {0}?" msgstr "Neue Mission namens {0} anlegen?" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:110 msgid "Could not create directory" msgstr "Konnte Verzeichnis nicht anlegen" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:110 msgid "Failed to create the folder {0}" msgstr "Konnte das Verzeichnis {0} nicht anlegen" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:163 msgid "The engine executable \"{0}\" could not be found in the specified folder.\n" msgstr "Die EXE-Datei \"{0}\" konnte in dem angegebenen Ordner nicht gefunden werden.\n" #: ..\..\radiant\ui\prefdialog\GameSetupPageTdm.cpp:170 msgid "The mission path \"{0}\" does not exist.\n" msgstr "Der Missions-Pfad \"{0}\" existiert nicht.\n" #: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:23 msgid "DarkRadiant Preferences" msgstr "DarkRadiant Einstellungen" #: ..\..\radiant\ui\prefdialog\PrefDialog.cpp:117 msgid "Settings/" msgstr "Einstellungen/" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:42 msgid "Surface Inspector" msgstr "Surface Inspector" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:43 msgid "Texture Properties" msgstr "Textureigenschaften" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:44 msgid "Texture Operations" msgstr "Texturoperationen" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:52 msgid "Horiz. Shift:" msgstr "Horiz. Verschiebung:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:53 msgid "Vert. Shift:" msgstr "Vert. Verschiebung:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:54 msgid "Horiz. Scale:" msgstr "Horiz. Skalierung:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:55 msgid "Vert. Scale:" msgstr "Vert. Skalierung:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:56 msgid "Rotation:" msgstr "Rotation:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:57 #: xml_file_content.cpp:15 msgid "Shader:" msgstr "Shader:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:61 msgid "Fit Texture:" msgstr "Texture einpassen:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:62 msgid "Fit" msgstr "Einpassen" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:64 msgid "Align Texture:" msgstr "Textur ausrichten:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:65 msgid "Top" msgstr "Oben" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:66 msgid "Bottom" msgstr "Unten" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:67 #: xml_file_content.cpp:16 msgid "Right" msgstr "Rechts" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:68 #: xml_file_content.cpp:15 msgid "Left" msgstr "Links" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:70 msgid "Flip Texture:" msgstr "Textur kippen:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:71 msgid "Flip Horizontal" msgstr "Horizonal kippen" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:72 msgid "Flip Vertical" msgstr "Vertikal kippen" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:74 msgid "Modify Texture:" msgstr "Texturierung bearbeiten:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:75 msgid "Natural" msgstr "Natürliche Skala" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:76 msgid "Normalise" msgstr "Normalisieren" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:78 msgid "Default Scale:" msgstr "Standardskalierung:" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:79 #: xml_file_content.cpp:9 msgid "Texture Lock" msgstr "Textur-Lock" #: ..\..\radiant\ui\surfaceinspector\SurfaceInspector.cpp:634 msgid "Both fit values must be > 0." msgstr "Beide Fit-Werte müssen größer als 0 sein." #: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:41 msgid "Seek in Media Browser" msgstr "In Media Browser zeigen" #: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:273 #: ..\..\radiant\ui\texturebrowser\TextureBrowser.cpp:816 msgid "No shader" msgstr "Kein Shader" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:61 msgid "Settings/Texture Browser" msgstr "Einstellungen/Texturbrowser" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:63 msgid "Uniform texture thumbnail size (pixels)" msgstr "Einheitliche Größe der Texturvorschaubilder (in Pixel)" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:64 msgid "Texture scrollbar" msgstr "Texturbrowser-Scrollbalken" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:65 msgid "Mousewheel Increment" msgstr "Bildlaufgeschwindgkeit beim Scrollen" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:66 msgid "Max shadername length" msgstr "Maximal dargestellte Länge für Shadernamen" #: ..\..\radiant\ui\texturebrowser\TextureBrowserManager.cpp:68 msgid "Show Texture Filter" msgstr "Zeige Textur-Filter" #: ..\..\radiant\ui\transform\TransformDialog.cpp:26 msgid "Arbitrary Transformation" msgstr "Freie Transformation" #: ..\..\radiant\ui\transform\TransformDialog.cpp:27 msgid "Rotation" msgstr "Rotation" #: ..\..\radiant\ui\transform\TransformDialog.cpp:28 msgid "Scale" msgstr "Skalierung" #: ..\..\radiant\ui\transform\TransformDialog.cpp:30 msgid "X-Axis Rotate:" msgstr "Rotation um X-Achse" #: ..\..\radiant\ui\transform\TransformDialog.cpp:31 msgid "Y-Axis Rotate:" msgstr "Rotation um Y-Achse" #: ..\..\radiant\ui\transform\TransformDialog.cpp:32 msgid "Z-Axis Rotate:" msgstr "Rotation um Z-Achse" #: ..\..\radiant\ui\transform\TransformDialog.cpp:34 msgid "X-Axis Scale:" msgstr "Skalierung X-Achse" #: ..\..\radiant\ui\transform\TransformDialog.cpp:35 msgid "Y-Axis Scale:" msgstr "Skalierung Y-Achse" #: ..\..\radiant\ui\transform\TransformDialog.cpp:36 msgid "Z-Axis Scale:" msgstr "Skalierung Z-Achse" #: ..\..\radiant\ui\UserInterfaceModule.cpp:39 msgid "Create Layer..." msgstr "Neuer Layer..." #: ..\..\radiant\ui\UserInterfaceModule.cpp:41 msgid "Add to Layer..." msgstr "Zu Layer hinzufügen..." #: ..\..\radiant\ui\UserInterfaceModule.cpp:42 msgid "Move to Layer..." msgstr "Nach Layer verschieben..." #: ..\..\radiant\ui\UserInterfaceModule.cpp:43 msgid "Remove from Layer..." msgstr "Von Layer entfernen..." #: ..\..\radiant\xyview\GlobalXYWnd.cpp:196 msgid "Settings/Orthoview" msgstr "Einstellungen/2D-Ansicht" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:198 msgid "View chases Mouse Cursor during Drags" msgstr "Ansicht folgt Mauszeiger bei Verschiebe-Operationen" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:199 msgid "Maximum Chase Mouse Speed" msgstr "Maximale Folgegeschwindigkeit" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:200 msgid "Update Views on Camera Movement" msgstr "Aktualisiere 2D-Ansichten bei Kamerabewegungen" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:201 msgid "Show Crosshairs" msgstr "Zeige Fadenkreuz" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:202 msgid "Show Grid" msgstr "Zeige Raster" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:203 msgid "Show Size Info" msgstr "Zeige Größeninformation" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:204 msgid "Show Entity Angle Arrow" msgstr "Zeige Richtungspfeile der Entities" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:205 msgid "Show Entity Names" msgstr "Zeige Namen der Entities" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:206 #: xml_file_content.cpp:81 msgid "Show Blocks" msgstr "Zeige Blöcke" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:207 msgid "Show Coordinates" msgstr "Zeige Koordinaten" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:208 #: xml_file_content.cpp:84 msgid "Show Axes" msgstr "Zeige Achsen" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:209 #: xml_file_content.cpp:83 msgid "Show Window Outline" msgstr "Zeige Fensterumrandung" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:210 #: xml_file_content.cpp:85 msgid "Show Workzone" msgstr "Zeige Arbeitsbereich" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:211 msgid "Translate Manipulator always constrained to Axis" msgstr "Translationen sind immer achsengebunden" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:212 msgid "Higher Selection Priority for Entities" msgstr "Höhere Auswahlpriorität für Entities" #: ..\..\radiant\xyview\GlobalXYWnd.cpp:666 msgid "Shows the mouse position in the orthoview" msgstr "Zeigt die aktuelle Position des Mauszeigers in der Ortho-Ansicht" #: ..\..\radiant\xyview\tools\BrushCreatorTool.cpp:25 msgid "Drag-create Brush" msgstr "Erzeuge neue Brush" #: ..\..\radiant\xyview\tools\CameraAngleTool.h:28 msgid "Point Camera" msgstr "Kamera ausrichten" #: ..\..\radiant\xyview\tools\CameraMoveTool.h:27 msgid "Drag Camera" msgstr "Kamera verschieben" #: ..\..\radiant\xyview\tools\ClipperTool.cpp:21 #: xml_file_content.cpp:151 #: xml_file_content.cpp:13 #: xml_file_content.cpp:18 msgid "Clipper" msgstr "Clipper" #: ..\..\radiant\xyview\tools\MeasurementTool.cpp:28 msgid "Measure" msgstr "Messen" #: ..\..\radiant\xyview\tools\MoveViewTool.h:27 msgid "Drag View" msgstr "Ansicht verschieben" #: ..\..\radiant\xyview\tools\ZoomTool.h:30 msgid "Zoom View" msgstr "Ansicht zoomen" #: ..\..\radiant\xyview\XYWnd.cpp:229 msgid "XY Top" msgstr "XY Oben" #: ..\..\radiant\xyview\XYWnd.cpp:232 msgid "XZ Front" msgstr "XZ Vorne" #: ..\..\radiant\xyview\XYWnd.cpp:235 msgid "YZ Side" msgstr "YZ Seite" #: ..\..\radiant\xyview\XYWnd.cpp:498 msgid "x: {0:6.1f} y: {1:6.1f} z: {2:6.1f}" msgstr "x: {0:6.1f} y: {1:6.1f} z: {2:6.1f}" #: ..\..\plugins\dm.conversation\CommandArgumentItem.cpp:118 #: ..\..\plugins\dm.conversation\CommandEditor.cpp:40 msgid "Actor {0:d} ({1})" msgstr "Akteur {0:d} ({1})" #: ..\..\plugins\dm.conversation\CommandArgumentItem.cpp:158 msgid "Browse Sound Shaders" msgstr "Sound Shader durchsuchen" #: ..\..\plugins\dm.conversation\CommandArgumentItem.cpp:215 msgid "Browse Animations" msgstr "Animationen durchsuchen" #: ..\..\plugins\dm.conversation\CommandEditor.cpp:24 msgid "Edit Command" msgstr "Befehl bearbeiten" #: ..\..\plugins\dm.conversation\ConversationDialog.cpp:30 msgid "Conversation Editor" msgstr "Konversations-Editor" #: ..\..\plugins\dm.conversation\ConversationDialog.cpp:280 msgid "Unable to create conversation Entity: class '{0}' not found." msgstr "Kann Konversationsentity nicht anlegen, Klasse '{0}' nicht gefunden." #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:27 msgid "Edit Conversation" msgstr "Konversation bearbeiten" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:92 msgid "Actor (click to edit)" msgstr "Akteur (zum Bearbeiten klicken)" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:121 #: xml_file_content.cpp:1 msgid "Actor" msgstr "Akteur" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:125 msgid "Wait" msgstr "Warten" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:232 msgid "Actor {0:d}" msgstr "Akteur {0:d}" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:234 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206 msgid "yes" msgstr "Ja" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:234 #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:206 msgid "no" msgstr "Nein" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:403 msgid "New Actor" msgstr "Neuer Akteur" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:455 msgid "The actor {0} cannot be found in the current map." msgstr "Der Akteur {0} wurde in der aktuellen Map nicht gefunden." #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:462 msgid "Actors missing" msgstr "Akteure fehlen" #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:466 msgid "All actors are correctly referring to entities in the map." msgstr "Alle Akteure beziehen sich auf gültige Entities der aktuellen Map." #: ..\..\plugins\dm.conversation\ConversationEditor.cpp:467 msgid "Actors OK" msgstr "Akteure sind OK" #: ..\..\plugins\dm.conversation\ConversationEntity.cpp:47 msgid "New Conversation" msgstr "Neue Konversation" #: ..\..\plugins\dm.conversation\ConversationEntityFinder.h:78 #: ..\..\plugins\dm.objectives\ObjectiveEntityFinder.cpp:36 msgid "{0} at [ {1} ]" msgstr "{0} bei [ {1} ]" #: ..\..\plugins\dm.conversation\plugin.cpp:50 msgid "Conversations..." msgstr "Konversationen..." #: ..\..\plugins\dm.difficulty\DifficultyDialog.cpp:26 msgid "Difficulty Editor" msgstr "Schwierigkeitsstufen-Editor" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:76 #: xml_file_content.cpp:4 msgid "Setting" msgstr "Einstellung" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:91 msgid "Assign" msgstr "Zuweisen" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:92 #: ..\..\plugins\dm.stimresponse\ClassEditor.cpp:81 #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:51 #: xml_file_content.cpp:2 #: xml_file_content.cpp:5 #: xml_file_content.cpp:8 #: xml_file_content.cpp:12 #: xml_file_content.cpp:1 #: xml_file_content.cpp:3 #: xml_file_content.cpp:17 #: xml_file_content.cpp:6 msgid "Add" msgstr "Addieren" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:93 msgid "Multiply" msgstr "Multiplizieren" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:94 msgid "Ignore" msgstr "Ignorieren" #: ..\..\plugins\dm.difficulty\DifficultyEditor.cpp:152 msgid "This default setting is overridden, cannot edit." msgstr "Diese Standardeinstellung wurde außer Kraft gesetzt, Editieren nicht möglich." #: ..\..\plugins\dm.difficulty\DifficultySettings.cpp:168 msgid " (overridden)" msgstr "(außer Kraft)" #: ..\..\plugins\dm.difficulty\plugin.cpp:58 msgid "Difficulty..." msgstr "Schwierigkeitsstufe..." #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:60 msgid "Can operate Doors" msgstr "Kann Türen bedienen" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:61 msgid "Can light Torches" msgstr "Kann Fackeln anzünden" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:62 msgid "Can operate Switch Lights" msgstr "Kann Lichtschalter betätigen" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:63 msgid "Can operate Elevators" msgstr "Kann Lifte bedienen" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:64 msgid "Can greet others" msgstr "Kann andere grüßen" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:65 msgid "Can search" msgstr "Kann suchen" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:66 msgid "AI is civilian" msgstr "AI ist Zivilist" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:67 msgid "Start sleeping" msgstr "Schläft von anfang an" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:68 msgid "Lay down to the left" msgstr "Nach links hinlegen" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:69 msgid "Start sitting" msgstr "Sitzt von anfang an" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:70 msgid "Patrol" msgstr "Patroullieren" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:71 msgid "Animal Patrol Mode" msgstr "Patrol-Modus für Tiere" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:72 msgid "Start in Alert Idle State" msgstr "Starte im \"alarmierten\" Idle-Zustand" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:73 msgid "Disable Alert Idle State" msgstr "\"Alarmierter\" Idle-Zustand nicht erlaubt" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:74 msgid "Drunk" msgstr "Betrunken" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:75 msgid "Body is shoulderable" msgstr "Kann geschultert werden" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:76 msgid "AI doesn't think outside the player PVS" msgstr "AI außerhalb des Spieler-PVS denkt nicht" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:78 msgid "AI can drown" msgstr "AI kann ertrinken" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:81 msgid "AI can be flatfooted" msgstr "AI kann kalt erwischt werden" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:82 msgid "AI is immune to KOs at high alert levels" msgstr "AI ist gegen KOs immun (auf hohem Alert-Level)" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:83 msgid "AI is immune to KOs" msgstr "AI ist immun gegen KOs" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:84 msgid "AI is immune to Gas" msgstr "AI ist immun gegen Gas" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:86 msgid "Team" msgstr "Team" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:87 msgid "Rank" msgstr "Rang" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:88 msgid "Sitting Angle" msgstr "Sitz-Winkel" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:89 msgid "Drunk Acuity Factor" msgstr "Sinnesschärfen-Faktor bei Trunkenheit" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:90 msgid "Visual Acuity" msgstr "Sehschärfe" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:91 msgid "Audio Acuity" msgstr "Hörschärfe" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:93 msgid "Horizontal FOV" msgstr "Horizontaler FOV" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:94 msgid "Vertical FOV" msgstr "Vertikaler FOV" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:96 msgid "Min. Interleave Distance" msgstr "Min. Interleaving Distanz" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:97 msgid "Max. Interleave Distance" msgstr "Max. Interleaving Distanz" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:99 msgid "Health" msgstr "Gesundheit" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:100 msgid "Critical Health" msgstr "Kritischer Gesundheitswert" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:101 msgid "Melee Range" msgstr "Reichweite für Handwaffen" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:105 msgid "Appearance" msgstr "Erscheinungsbild" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:110 msgid "Skin: " msgstr "Skin:" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:111 msgid "Head: " msgstr "Kopf:" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:111 #: ..\..\plugins\dm.editing\AIHeadPropertyEditor.cpp:31 msgid "Choose AI head..." msgstr "Kopf für AI auswählen..." #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:112 msgid "Vocal Set: " msgstr "Sprachsatz:" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:112 msgid "Choose Vocal Set..." msgstr "Sprachsatz für AI auswählen" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:117 msgid "Behaviour" msgstr "Verhalten" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:163 msgid "Abilities" msgstr "Fähigkeiten" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:178 msgid "Optimization" msgstr "Optimierung" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:190 msgid "Health / Combat" msgstr "Gesundheit / Kampf" #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:272 #: ..\..\plugins\dm.editing\AIEditingPanel.cpp:275 msgid "AI" msgstr "AI" #: ..\..\plugins\dm.editing\AIHeadChooserDialog.cpp:18 msgid "Choose AI Head" msgstr "Kopf für AI auswählen" #: ..\..\plugins\dm.editing\AIVocalSetChooserDialog.cpp:19 msgid "Choose AI Vocal Set" msgstr "Sprachsatz für AI auswählen" #: ..\..\plugins\dm.editing\AIVocalSetChooserDialog.cpp:44 msgid "Available Sets" msgstr "Verfügbare Sätze:" #: ..\..\plugins\dm.editing\AIVocalSetPreview.cpp:114 msgid "Error: File not found." msgstr "Fehler: Datei nicht gefunden." #: ..\..\plugins\dm.editing\AIVocalSetPropertyEditor.cpp:31 msgid "Select Vocal Set..." msgstr "Sprachsatz auswählen..." #: ..\..\plugins\dm.editing\DarkmodTxt.cpp:131 msgid "Order of the elements Title/Description/Author/etc. is incorrect" msgstr "Die Reihenfolge der Title/Description/Author/etc.-Elemente ist ungültig." #: ..\..\plugins\dm.editing\FixupMap.cpp:26 msgid "Fixup in progress" msgstr "Fixup läuft" #: ..\..\plugins\dm.editing\FixupMap.cpp:60 msgid "Processing line {0}..." msgstr "Verarbeite Zeile {0}..." #: ..\..\plugins\dm.editing\FixupMap.cpp:65 msgid "Fixup cancelled" msgstr "Fixup abgebrochen" #: ..\..\plugins\dm.editing\FixupMap.cpp:71 msgid "Completed" msgstr "Fertig" #: ..\..\plugins\dm.editing\FixupMap.cpp:155 #: ..\..\plugins\dm.editing\FixupMap.cpp:166 msgid "File not readable" msgstr "Datei nicht lesbar" #: ..\..\plugins\dm.editing\FixupMap.cpp:156 msgid "The specified file doesn't exist." msgstr "Die angegebene Datei existiert nicht." #: ..\..\plugins\dm.editing\FixupMap.cpp:167 msgid "The specified file can't be opened." msgstr "Die angegebene Datei kann nicht geöffnet werden." #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:15 msgid "Fixup Map" msgstr "Map-Fixup" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:16 msgid "Fixup File" msgstr "Fixup-Datei" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:47 msgid "{0} shaders replaced." msgstr "{0} Shader ersetzt." #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:48 msgid "{0} entities replaced." msgstr "{0} Entities ersetzt." #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:49 msgid "{0} models replaced." msgstr "{0} Modelle ersetzt." #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:50 msgid "{0} spawnargs replaced." msgstr "{0} Wertepaare ersetzt." #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:55 msgid "Errors occurred:" msgstr "Aufgetretene Fehler:" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:61 msgid "(Line {0}): {1}" msgstr "(Zeile {0}): {1}" #: ..\..\plugins\dm.editing\FixupMapDialog.cpp:66 msgid "Fixup Results" msgstr "Fixup Ergebnisse" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:25 msgid "Mission Info Editor (darkmod.txt)" msgstr "Missions-Info (darkmod.txt)" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:44 msgid "" "Failed to parse darkmod.txt:\n" "{0}" msgstr "" "Import der darkmod.txt-Datei fehlgeschlagen:\n" "{0}" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:109 msgid "#" msgstr "#" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:112 #: xml_file_content.cpp:17 msgid "Title" msgstr "Titel:" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:140 msgid "Add Title" msgstr "Titel hinzufügen:" #: ..\..\plugins\dm.editing\MissionInfoEditDialog.cpp:145 msgid "Delete Title" msgstr "Titel löschen" #: ..\..\plugins\dm.editing\MissionInfoTextFile.cpp:43 msgid "" "Could not write {0} contents:\n" "{1}" msgstr "" "Konnte {0} Datei nicht speichern:\n" "{1}" #: ..\..\plugins\dm.editing\MissionReadmeDialog.cpp:23 msgid "Mission Readme Editor (readme.txt)" msgstr "Missions-Readme Editor (readme.txt)" #: ..\..\plugins\dm.editing\MissionReadmeDialog.cpp:41 msgid "" "Failed to parse readme.txt:\n" "{0}" msgstr "" "Import der readme.txt-Datei fehlgeschlagen:\n" "{0}" #: ..\..\plugins\dm.editing\plugin.cpp:65 msgid "Fixup Map..." msgstr "Map-Fixup..." #: ..\..\plugins\dm.editing\plugin.cpp:75 msgid "Edit Package Info (darkmod.txt)..." msgstr "Missions-Info editieren (darkmod.txt)..." #: ..\..\plugins\dm.gui\GuiSelector.cpp:22 msgid "Choose a Gui Definition..." msgstr "GUI-Definition auswählen..." #: ..\..\plugins\dm.gui\GuiSelector.cpp:141 #: ..\..\plugins\dm.gui\GuiSelector.cpp:150 msgid "Gui Path" msgstr "GUI Pfad" #: ..\..\plugins\dm.gui\GuiSelector.cpp:146 msgid "One-Sided Readable Guis" msgstr "Einseitige Schriftstück-GUIs" #: ..\..\plugins\dm.gui\GuiSelector.cpp:155 msgid "Two-Sided Readable Guis" msgstr "Zweiseitige Schriftstück-GUIs" #: ..\..\plugins\dm.gui\plugin.cpp:90 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:43 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:542 msgid "Readable Editor" msgstr "Schriftstück-Editor" #: ..\..\plugins\dm.gui\plugin.cpp:97 msgid "Reload Readable Guis" msgstr "Schriftstück-GUIs neu laden" #: ..\..\plugins\dm.gui\plugin.cpp:107 msgid "Settings/Readable Editor" msgstr "Einstellungen/Schriftstück-Editor" #: ..\..\plugins\dm.gui\plugin.cpp:111 msgid "Mod/xdata" msgstr "Mod/xdata" #: ..\..\plugins\dm.gui\plugin.cpp:112 msgid "Mod Base/xdata" msgstr "Mod Base/xdata" #: ..\..\plugins\dm.gui\plugin.cpp:113 #: ..\..\plugins\dm.gui\plugin.cpp:117 msgid "Custom Folder" msgstr "Benutzerdefinierter Ordner" #: ..\..\plugins\dm.gui\plugin.cpp:115 msgid "XData Storage Folder" msgstr "Ordner zum Ablegen der XData-Dateien" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:45 msgid "" "Cannot run Readable Editor on this selection.\n" "Please select a single XData entity." msgstr "" "Schriftstück-Editor kann für diese Auswahl nicht ausgeführt werden.\n" "Bitte eine einzelne XData-Entity auswählen." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:227 msgid "Insert whole Page" msgstr "Ganze Seite einfügen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:228 msgid "Insert on left Side" msgstr "Links einfügen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:229 msgid "Insert on right Side" msgstr "Rechts einfügen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:235 msgid "Delete whole Page" msgstr "Ganze Seite löschen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:236 msgid "Delete on left Side" msgstr "Links löschen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:237 msgid "Delete on right Side" msgstr "Rechts löschen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:243 msgid "Append Page" msgstr "Seite anfügen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:248 msgid "Prepend Page" msgstr "Seite voranstellen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:253 msgid "Show last XData import summary" msgstr "Zeige Zusammenfassung des letzten XData-Imports" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:254 msgid "Show duplicated definitions" msgstr "Zeige doppelte Definitionen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:255 msgid "Show Gui import summary" msgstr "Zeige Zusammenfassung des letzten GUI-Imports" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:292 msgid "" msgstr "" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:317 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:644 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1400 msgid "Failed to import {0}." msgstr "Import von {0} fehlgeschlagen." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:319 msgid "Creating a new XData definition..." msgstr "Erzeuge eine neue XData-Definition..." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:321 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:646 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:665 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:716 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1402 #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:62 msgid "Do you want to open the import summary?" msgstr "Willst Du die Import-Zusammenfassung anzeigen?" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:323 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:648 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:667 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:718 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1404 #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:79 msgid "Import failed" msgstr "Import fehlgeschlagen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:379 msgid "You have imported an XData definition that is contained in a PK4, which can't be accessed for saving." msgstr "Du hast eine XData-Definition importiert, die in einem PK4-File gespeichert ist." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:381 msgid "Please rename your XData definition, so that it is stored under a different filename." msgstr "Bitte benenne Deine XData-Definition um, so dass sie unter einem neuen Namen gespeichert wird." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:398 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:419 msgid "Failed to open {0} for saving." msgstr "Konnte {0} nicht zum Speichern öffnen." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:405 msgid "Merging failed, because the length of the definition to be overwritten could not be retrieved." msgstr "Fehler beim Zusammenführen. Die Länge der zu überschreibenden Definition konnte nicht abgerufen werden." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:443 msgid "Mod path not defined. Using Base path..." msgstr "Mod-Pfad nicht definiert, verwende Base-Pfad..." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:456 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:474 msgid "Mod Base path not defined, neither is Mod path. Using Engine path..." msgstr "Mod-Base-Pfad nicht definiert, Mod-Pfad auch nicht, verwende Base-Pfad..." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:461 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:479 msgid "Mod Base path not defined. Using Mod path..." msgstr "Mod-Base-Pfad nicht definiert, verwende Mod-Pfad..." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:505 msgid "" "{0}{1} already exists in another path.\n" "\n" "XData will be stored in {2}{3}!" msgstr "" "{0}{1} existiert bereits unter anderem Pfad.\n" "\n" "XData wird in {2}{3} gespeichert!" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:663 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:714 msgid "Failed to load gui definition {0}." msgstr "Laden der GUI-Definition {0} fehlgeschlagen." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:836 msgid "Import definition?" msgstr "Definition importieren?" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:837 msgid "The definition {0} already exists. Should it be imported?" msgstr "Die Definition {0} existiert bereits, soll sie importiert werden?" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:861 msgid "Import failed:" msgstr "Import fehlgeschlagen:" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:864 msgid "Consult the import summary for further information." msgstr "Bitte sieh in der Import-Zusammenfassung nach, um weitere Informationen zu erhalten." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:887 msgid "To avoid duplicated XData definitions the current definition has been renamed to {0}." msgstr "Die aktuelle Definition wurde in {0} umbenannt, um doppelte XData-Definitionen zu vermeiden." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:891 msgid "XData has been renamed." msgstr "XData wurde umbenannt." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1159 msgid "The specified gui definition is not a readable." msgstr "Die angegebene GUI-Definition ist kein Schriftstück." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1164 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1176 msgid "The specified gui definition is not suitable for the currently chosen page-layout." msgstr "Die angegebene GUI-Definition ist für das momentan gewählte Seitenlayout nicht geeignet." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1186 msgid "Failure during import." msgstr "Fehler beim Import." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1189 msgid "The specified Definition does not exist." msgstr "Die angegebene Definition existiert nicht." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1197 msgid "Not a suitable Gui Definition!" msgstr "Keine geeignete GUI-Definition!" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1198 msgid "Start the Gui Browser?" msgstr "GUI-Browser öffnen?" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1232 msgid "Switching to default Gui..." msgstr "Schalte zu Standard-GUI um..." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1233 msgid "You didn't choose a Gui. Using the default Gui now." msgstr "Du hast keine GUI ausgewählt. Es wird die Standard-GUI verwendet." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1250 msgid "No import summary available. An XData definition has to be imported first..." msgstr "Die Importzusammenfassung ist erst nach dem Import einer XData-Definition verfügbar." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1262 msgid "XData import summary" msgstr "XData Importzusammenfassung" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1270 msgid "No import summary available. Browse Gui Definitions first." msgstr "Die Importzusammenfassung ist erst nach dem Öffnen des GUI-Browsers verfügbar." #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1281 msgid "Gui import summary" msgstr "GUI Importzusammenfassung" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1347 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1365 msgid "Please specify an XData name first!" msgstr "Bitte zuerst einen XData-Namen angeben!" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1554 #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1580 msgid "Duplicated XData definitions" msgstr "Mehrfach vorkommende XData-Definitionen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1555 msgid "There are no duplicated definitions!" msgstr "Es gibt keine mehrfachen XData-Definitionen" #: ..\..\plugins\dm.gui\ReadableEditorDialog.cpp:1574 msgid "{0} has been defined in:" msgstr "{0} ist definiert in:" #: ..\..\plugins\dm.gui\ReadablePopulator.h:35 msgid "Analysing Guis" msgstr "Analysiere GUIs" #: ..\..\plugins\dm.gui\ReadableReloader.h:30 msgid "Reloading Guis" msgstr "Neuladen der GUIs" #: ..\..\plugins\dm.gui\XData.cpp:257 #: ..\..\plugins\dm.gui\XData.cpp:285 #: ..\..\plugins\dm.gui\XData.cpp:367 #: ..\..\plugins\dm.gui\XData.cpp:382 msgid "Page Index out of bounds." msgstr "Seitenindex ungültig." #: ..\..\plugins\dm.gui\XData.h:118 #: ..\..\plugins\dm.gui\XData.h:125 msgid "GUI Page Index out of bounds." msgstr "GUI-Seitenindex ungültig" #: ..\..\plugins\dm.gui\XDataSelector.cpp:17 msgid "Choose an XData Definition..." msgstr "XData-Definition auswählen..." #: ..\..\plugins\dm.gui\XDataSelector.cpp:48 msgid "Xdata Path" msgstr "Xdata-Pfad" #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:20 msgid "Choose a file..." msgstr "Datei auswählen..." #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:60 msgid "{0} successfully imported." msgstr "{0} erfolgreich importiert" #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:64 msgid "Problems during import" msgstr "Fehler beim Import." #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:99 msgid "The requested definition has been found in multiple Files. Choose the file:" msgstr "Die angeforderte Definition wurde in mehreren Dateien gefunden, bitte die genaue Datei auswählen:" #: ..\..\plugins\dm.gui\XdFileChooserDialog.cpp:104 #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:127 msgid "File" msgstr "Datei" #: ..\..\plugins\dm.objectives\ce\AIFindBodyComponentEditor.cpp:31 msgid "Body:" msgstr "Text:" #: ..\..\plugins\dm.objectives\ce\AIFindBodyComponentEditor.cpp:36 #: ..\..\plugins\dm.objectives\ce\AlertComponentEditor.cpp:43 #: ..\..\plugins\dm.objectives\ce\DestroyComponentEditor.cpp:37 #: ..\..\plugins\dm.objectives\ce\ItemComponentEditor.cpp:37 #: ..\..\plugins\dm.objectives\ce\KillComponentEditor.cpp:38 #: ..\..\plugins\dm.objectives\ce\KnockoutComponentEditor.cpp:38 #: ..\..\plugins\dm.objectives\ce\PickpocketComponentEditor.cpp:37 msgid "Amount:" msgstr "Anzahl:" #: ..\..\plugins\dm.objectives\ce\AIFindItemComponentEditor.cpp:23 #: ..\..\plugins\dm.objectives\ce\DestroyComponentEditor.cpp:31 #: ..\..\plugins\dm.objectives\ce\ItemComponentEditor.cpp:31 #: ..\..\plugins\dm.objectives\ce\PickpocketComponentEditor.cpp:31 msgid "Item:" msgstr "Gegenstand:" #: ..\..\plugins\dm.objectives\ce\AlertComponentEditor.cpp:37 msgid "AI:" msgstr "AI:" #: ..\..\plugins\dm.objectives\ce\AlertComponentEditor.cpp:46 msgid "Minimum Alert Level:" msgstr "Minimale Alarmierungs-Stufe" #: ..\..\plugins\dm.objectives\ce\CustomClockedComponentEditor.cpp:36 msgid "Script Function:" msgstr "Skriptfunktion:" #: ..\..\plugins\dm.objectives\ce\CustomClockedComponentEditor.cpp:42 #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:68 msgid "Clock interval:" msgstr "Zeitintervall:" #: ..\..\plugins\dm.objectives\ce\CustomClockedComponentEditor.cpp:48 #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:70 msgid "seconds:" msgstr "Sekunden:" #: ..\..\plugins\dm.objectives\ce\CustomComponentEditor.cpp:17 msgid "" "A custom component requires no specifiers,\n" "the state of this component is manually controlled \n" "(i.e. by scripts or triggers)." msgstr "" "Eine benutzerdefinierte Komponente benötigt keine Spezifikatoren,\n" "der Zustand der Komponente wird anderweitig gesteuert\n" "(z.B. durch Skript oder einen Trigger)." #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:50 #: ..\..\plugins\dm.objectives\ce\InfoLocationComponentEditor.cpp:24 #: ..\..\plugins\dm.objectives\ce\LocationComponentEditor.cpp:24 msgid "Entity:" msgstr "Entity:" #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:56 msgid "Location Entity:" msgstr "Orts-Entity:" #: ..\..\plugins\dm.objectives\ce\DistanceComponentEditor.cpp:62 msgid "Distance:" msgstr "Distanz:" #: ..\..\plugins\dm.objectives\ce\InfoLocationComponentEditor.cpp:30 #: ..\..\plugins\dm.objectives\ce\LocationComponentEditor.cpp:30 msgid "Location:" msgstr "Ort:" #: ..\..\plugins\dm.objectives\ce\KillComponentEditor.cpp:32 msgid "Kill target:" msgstr "Tötungs-Ziel:" #: ..\..\plugins\dm.objectives\ce\KnockoutComponentEditor.cpp:32 msgid "Knockout target:" msgstr "KO-Ziel:" #: ..\..\plugins\dm.objectives\ce\ReadableClosedComponentEditor.cpp:22 #: ..\..\plugins\dm.objectives\ce\ReadableOpenedComponentEditor.cpp:22 #: ..\..\plugins\dm.objectives\ce\ReadablePageReachedComponentEditor.cpp:31 msgid "Readable:" msgstr "Schriftstück:" #: ..\..\plugins\dm.objectives\ce\ReadablePageReachedComponentEditor.cpp:37 msgid "Page Number:" msgstr "Seite:" #: ..\..\plugins\dm.objectives\ComponentsDialog.cpp:28 msgid "Edit Objective" msgstr "Ziel editieren:" #: ..\..\plugins\dm.objectives\ComponentType.cpp:22 msgid "AI is killed" msgstr "AI wird getötet" #: ..\..\plugins\dm.objectives\ComponentType.cpp:27 msgid "AI is knocked out" msgstr "AI wird KO geschlagen" #: ..\..\plugins\dm.objectives\ComponentType.cpp:32 msgid "AI finds an item" msgstr "AI findet einen Gegenstand" #: ..\..\plugins\dm.objectives\ComponentType.cpp:37 msgid "AI finds a body" msgstr "AI findet einen Körper" #: ..\..\plugins\dm.objectives\ComponentType.cpp:42 msgid "AI is alerted" msgstr "AI wird alarmiert" #: ..\..\plugins\dm.objectives\ComponentType.cpp:47 msgid "Object is destroyed" msgstr "Objekt wird zerstört" #: ..\..\plugins\dm.objectives\ComponentType.cpp:52 msgid "Player possesses item" msgstr "Spieler besitzt Gegenstand" #: ..\..\plugins\dm.objectives\ComponentType.cpp:57 msgid "Player pickpockets AI" msgstr "Spieler bestiehlt AI" #: ..\..\plugins\dm.objectives\ComponentType.cpp:62 msgid "Item is in location" msgstr "Gegenstand ist am/im Ort" #: ..\..\plugins\dm.objectives\ComponentType.cpp:67 msgid "Item is in info_location" msgstr "Gegenstand ist am/im Ort (info_location)" #: ..\..\plugins\dm.objectives\ComponentType.cpp:72 msgid "Custom script" msgstr "Benutzerdefiniertes Skript" #: ..\..\plugins\dm.objectives\ComponentType.cpp:78 msgid "Custom script queried periodically" msgstr "Benutzerdefiniertes Skript wird periodisch abgefragt" #: ..\..\plugins\dm.objectives\ComponentType.cpp:85 msgid "Two entities are within a radius of each other" msgstr "Zwei Entities befinden sich innerhalb eines bestimmten Radius" #: ..\..\plugins\dm.objectives\ComponentType.cpp:93 msgid "Readable is opened." msgstr "Ein Schriftstück wird geöffnet" #: ..\..\plugins\dm.objectives\ComponentType.cpp:101 msgid "Readable is closed." msgstr "Ein Schriftstück wird geschlossen" #: ..\..\plugins\dm.objectives\ComponentType.cpp:109 msgid "A certain page of a readable is reached." msgstr "Eine bestimmte Seite eines Schriftstücks wird angezeigt." #: ..\..\plugins\dm.objectives\DifficultyPanel.cpp:21 msgid "All Levels" msgstr "All Stufen" #: ..\..\plugins\dm.objectives\DifficultyPanel.cpp:30 msgid "Level 1: Easy" msgstr "Level 1: Leicht" #: ..\..\plugins\dm.objectives\DifficultyPanel.cpp:31 msgid "Level 2: Hard" msgstr "Level 1: Hart" #: ..\..\plugins\dm.objectives\DifficultyPanel.cpp:32 msgid "Level 3: Expert" msgstr "Level 1: Experte" #: ..\..\plugins\dm.objectives\LogicEditor.cpp:24 msgid "Success Logic:" msgstr "Logik für Erfüllung:" #: ..\..\plugins\dm.objectives\LogicEditor.cpp:25 msgid "Failure Logic:" msgstr "Logik für Fehlschlag:" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:18 msgid "Edit Mission Logic" msgstr "Missions-Logik bearbeiten" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:21 msgid "This is the standard logic for all difficulty levels" msgstr "Das ist die Standard-Logik für alle Schwierigkeitsstufen." #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:24 msgid "" "These logics override the standard logic for the given difficulty level\n" "if the logic string is non-empty." msgstr "" "Diese Logik überschreibt die Standard-Logik für die angegebene Schwierigkeitsstufe\n" "wenn das Logik-Eingabefeld nicht leer ist." #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:45 msgid "Default Logic" msgstr "Standard-Logik" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:57 msgid "Difficulty-specific Logic" msgstr "Logik für bestimmte Schwierigkeitsstufe" #: ..\..\plugins\dm.objectives\MissionLogicDialog.cpp:66 msgid "Logic for Difficulty Level {0:d}" msgstr "Logik für Schwierigkeitsstufe {0:d}" #: ..\..\plugins\dm.objectives\Objective.h:73 msgid "INCOMPLETE" msgstr "NICHT ERFÜLLT" #: ..\..\plugins\dm.objectives\Objective.h:74 msgid "COMPLETE" msgstr "ERFÜLLT" #: ..\..\plugins\dm.objectives\Objective.h:75 msgid "FAILED" msgstr "FEHLGESCHLAGEN" #: ..\..\plugins\dm.objectives\Objective.h:76 msgid "INVALID" msgstr "UNGÜLTIG" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:24 #: xml_file_content.cpp:13 msgid "Edit Objective Conditions" msgstr "Missionsziel-Bedingungen bearbeiten" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:145 msgid "Change Objective State" msgstr "Setze Status eines Missionsziels" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:147 msgid "Change Visibility" msgstr "Setze Misionsziel auf sichtbar/unsichtbar" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:149 msgid "Change Mandatory Flag" msgstr "Setze die 'obligatorisch'-Eigenschaft" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:211 #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:212 #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:213 #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:214 msgid "Set state to {0}" msgstr "Setze Status auf {0}" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:225 msgid "Set Invisible" msgstr "Unsichtbar machen" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:226 msgid "Set Visible" msgstr "Sichtbar machen" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:237 msgid "Clear mandatory flag" msgstr "Nicht obligatorisch machen" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:238 msgid "Set mandatory flag" msgstr "Obligatorisch machen" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:463 msgid "Condition affecting objective {0:d}" msgstr "Bedingung betreffend Missionsziel {0:d}" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:493 msgid "If Objective {0} in Mission {1} is in state '{2}' do the following: " msgstr "Wenn das Missionsziel {0} in Mission Nr. {1} den Status '{2}' hat:" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:504 msgid "Set State on Objective {0} to {1}" msgstr "Setze den Status von Missionsziel {0} auf {1}" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:511 msgid "Make Objective {0} visible" msgstr "Missionsziel {0} sichtbar machen" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:515 msgid "Make Objective {0} invisible" msgstr "Missionsziel {0} unsichtbar machen" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:522 msgid "Make Objective {0} mandatory" msgstr "Missionsziel {0} obligatorisch machen" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:526 msgid "Make Objective {0} not mandatory" msgstr "Missionsziel {0} nicht obligatorisch machen" #: ..\..\plugins\dm.objectives\ObjectiveConditionsDialog.cpp:537 msgid "This condition is not valid or complete yet." msgstr "Diese Bedingung ist noch nicht gültig oder vollständig." #: ..\..\plugins\dm.objectives\ObjectiveEntity.cpp:281 msgid "New objective {0:d}" msgstr "Neues Ziel {0:d}" #: ..\..\plugins\dm.objectives\objectives.cpp:67 msgid "Objectives..." msgstr "Missionsziele..." #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:33 msgid "Mission Objectives" msgstr "Missionsziele" #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:97 msgid "Start" msgstr "Start" #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:135 msgid "Diff." msgstr "Schw." #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:260 msgid "Exception occurred: " msgstr "Ausnahmefehler:" #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:437 msgid "Unable to create Objective Entity: classes not defined in registry." msgstr "Kann Ziel-Entity nicht erzeugen: die Entityklassen sind nicht in der Registierung definiert." #: ..\..\plugins\dm.objectives\ObjectivesEditor.cpp:466 msgid "Unable to create Objective Entity: class '{0}' not found." msgstr "Kann Ziel-Entity nicht erzeugen: EntityKlasse '{0}' nicht gefunden." #: ..\..\plugins\dm.objectives\Specifier.cpp:20 msgid "entity" msgstr "Entity" #: ..\..\plugins\dm.objectives\Specifier.cpp:20 msgid "entities" msgstr "Entities" #: ..\..\plugins\dm.objectives\Specifier.cpp:43 msgid "" msgstr "" #: ..\..\plugins\dm.objectives\Specifier.cpp:46 msgid "entity " msgstr "entity" #: ..\..\plugins\dm.objectives\Specifier.cpp:54 msgid "{0:d} loot in gold" msgstr "{0:d} Loot in Gold" #: ..\..\plugins\dm.objectives\Specifier.cpp:57 msgid "{0:d} loot in goods" msgstr "{0:d} Loot in Waren" #: ..\..\plugins\dm.objectives\Specifier.cpp:60 msgid "{0:d} loot in jewels" msgstr "{0:d} Loot in Edelsteinen" #: ..\..\plugins\dm.objectives\Specifier.cpp:63 msgid "{0:d} loot" msgstr "{0:d} Loot" #: ..\..\plugins\dm.objectives\Specifier.cpp:66 msgid "{0:d} of \"{1}\"" msgstr "{0:d} von \"{1}\"" #: ..\..\plugins\dm.objectives\Specifier.cpp:70 msgid "{0} of type {1}" msgstr "{0} vom Typ {1}" #: ..\..\plugins\dm.objectives\Specifier.cpp:73 msgid "{0} of spawnclass {1}" msgstr "{0} der Spawnklasse {1}" #: ..\..\plugins\dm.objectives\Specifier.cpp:76 msgid "{0} AI of type {1}" msgstr "{0} AI vom Typ {1}" #: ..\..\plugins\dm.objectives\Specifier.cpp:79 msgid "{0} AI of team {1}" msgstr "{0} AI vom Team {1}" #: ..\..\plugins\dm.objectives\Specifier.cpp:82 msgid "{0} AI of {1}" msgstr "{0} AI von {1}" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:16 msgid "No specifier" msgstr "Kein Spezifikator" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:20 msgid "Name of single entity" msgstr "Name der Entity" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:24 msgid "Overall (component-specific)" msgstr "Insgesamt (komponentenspezifisch)" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:29 msgid "Group identifier (component-specific)" msgstr "Gruppenidentifizierer (komponentenspezifisch)" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:34 msgid "Any entity of specified class" msgstr "Irgendeine Entity der angegebenen Klasse" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:39 msgid "Any entity with SDK-level spawnclass" msgstr "Irgendeine Entity der angegebenen SDK Klasse" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:44 msgid "Any AI of specified type" msgstr "Irgendeine AI des angegebenen Typs" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:48 msgid "Any AI on specified team" msgstr "Irgendeine AI des angegebenen Teams" #: ..\..\plugins\dm.objectives\SpecifierType.cpp:53 msgid "Any AI with specified combat status" msgstr "Irgendeine AI mit dem angegebenen Kampfzustand" #: ..\..\plugins\dm.stimresponse\ClassEditor.cpp:62 msgid "S/R" msgstr "S/R" #: ..\..\plugins\dm.stimresponse\ClassEditor.cpp:82 #: xml_file_content.cpp:7 msgid "Remove" msgstr "Entfernen" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:53 #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:397 #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:412 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:405 #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:92 #: xml_file_content.cpp:181 #: xml_file_content.cpp:3 #: xml_file_content.cpp:7 #: xml_file_content.cpp:9 #: xml_file_content.cpp:16 #: xml_file_content.cpp:2 #: xml_file_content.cpp:5 #: xml_file_content.cpp:6 #: xml_file_content.cpp:18 msgid "Delete" msgstr "Löschen" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:106 #: xml_file_content.cpp:2 msgid "Name:" msgstr "Name:" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:117 msgid "" "Note: Please beware that deleting custom stims may\n" "affect other entities as well. So check before you delete." msgstr "" "Hinweis: Bitte beachte, dass sich das Löschen benutzerdefinierter Stims auf\n" "andere Entities auswirken kann. Gehe deshalb sorgfältig vor." #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:129 msgid "Add Stim Type" msgstr "Stimtyp hinzufügen" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:130 msgid "Remove Stim Type" msgstr "Stimtyp entfernen" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:185 #: ..\..\plugins\dm.stimresponse\StimTypes.cpp:294 msgid "Custom Stim" msgstr "Benutzerdefinierter Stim" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:209 msgid "Delete Custom Stim" msgstr "Benutzerdefinierten Stim entfernen" #: ..\..\plugins\dm.stimresponse\CustomStimEditor.cpp:210 msgid "" "Beware that other entities might still be using this stim type.\n" "Do you really want to delete this custom stim?" msgstr "" "Beachte, dass andere Entities diesen Stimtyp noch verwenden könnten.\n" "Möchtest du diesen benutzerdefinierten Stim wirklich löschen?" #: ..\..\plugins\dm.stimresponse\EffectEditor.cpp:22 msgid "Edit Response Effect" msgstr "Response-Effekt editieren" #: ..\..\plugins\dm.stimresponse\EffectEditor.cpp:108 msgid "Effect:" msgstr "Effekt:" #: ..\..\plugins\dm.stimresponse\EffectEditor.cpp:115 #: xml_file_content.cpp:2 msgid "Active" msgstr "Aktiv" #: ..\..\plugins\dm.stimresponse\EffectEditor.cpp:120 msgid "Arguments" msgstr "Argumente" #: ..\..\plugins\dm.stimresponse\plugin.cpp:59 msgid "Stim/Response..." msgstr "Stim/Response..." #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:231 msgid "Effect" msgstr "Effekt" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:234 msgid "Details (double-click to edit)" msgstr "Details (doppelklicken zum Editieren)" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:391 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:399 msgid "Activate" msgstr "Aktivieren" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:393 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:401 msgid "Deactivate" msgstr "Deaktivieren" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:395 #: ..\..\plugins\dm.stimresponse\StimEditor.cpp:403 #: xml_file_content.cpp:11 msgid "Duplicate" msgstr "Duplizieren" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:402 msgid "Add new Effect" msgstr "Neuen Effekt hinzufügen" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:404 #: xml_file_content.cpp:6 #: xml_file_content.cpp:13 #: xml_file_content.cpp:4 #: xml_file_content.cpp:8 msgid "Edit" msgstr "Bearbeiten" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:407 #: xml_file_content.cpp:4 msgid "Move Up" msgstr "Nach oben" #: ..\..\plugins\dm.stimresponse\ResponseEditor.cpp:409 #: xml_file_content.cpp:5 msgid "Move Down" msgstr "Nach unten" #: ..\..\plugins\dm.stimresponse\ResponseEffect.cpp:195 msgid "Error: eclass pointer invalid." msgstr "Fehler: Entityklassenzeiger ungültig." #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:26 msgid "Stim/Response Editor" msgstr "Stim/Response-Editor" #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:31 msgid "A single entity must be selected to edit Stim/Response properties." msgstr "Um Stim/Response-Eigenschaften zu editieren muss genau eine Entity ausgewählt sein." #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:97 msgid "Stims" msgstr "Stims" #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:104 msgid "Responses" msgstr "Responses" #: ..\..\plugins\dm.stimresponse\StimResponseEditor.cpp:111 msgid "Custom Stims" msgstr "Benutzerdefinierte Stims" #: ..\..\plugins\eclassmgr\EClassManager.cpp:335 #: ..\..\plugins\eclassmgr\EClassManager.cpp:336 msgid "Reloading Defs" msgstr "Neuladen der Definitionen" #: ..\..\plugins\eclasstree\EClassTree.cpp:23 msgid "Entity Class Tree" msgstr "Entity-Klassenbaum" #: ..\..\plugins\eclasstree\plugin.cpp:51 msgid "Entity Class Tree..." msgstr "Entity-Klassenbaum..." #: ..\..\plugins\entity\EntityCreator.cpp:38 msgid "createNodeForEntity(): cannot create entity for NULL entityclass." msgstr "createNodeForEntity: Entity für Entity-Klasse NULL kann nicht erstellt werden." #: ..\..\plugins\entitylist\EntityList.cpp:23 #: xml_file_content.cpp:60 msgid "Entity List" msgstr "Entity-Liste" #: ..\..\plugins\entitylist\EntityList.cpp:86 msgid "Focus camera on selected entity" msgstr "Kamera auf ausgewählte Entity richten" #: ..\..\plugins\entitylist\EntityList.cpp:87 msgid "List visible nodes only" msgstr "Nur sichtbare Elemente anzeigen" #: ..\..\plugins\eventmanager\MouseToolGroup.cpp:23 msgid "XY View" msgstr "XY-Ansicht" #: ..\..\plugins\eventmanager\MouseToolGroup.cpp:25 #: xml_file_content.cpp:50 msgid "Camera View" msgstr "Kamera" #: ..\..\plugins\eventmanager\MouseToolGroup.cpp:27 msgid "Unknown" msgstr "Unbekannt" #: ..\..\plugins\filetypes\FileTypeRegistry.cpp:12 msgid "All Files" msgstr "Alle Dateien" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:49 #: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:44 msgid "" "Failed parsing entity {0:d}:\n" "{1}" msgstr "" "Fehler beim Parsen der Entity {0:d}\n" "{1}" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:94 #: ..\..\plugins\mapdoom3\Quake4MapReader.cpp:47 msgid "Unable to parse map version (parse exception)." msgstr "Konnte Map-Version nicht parsen (Parse-Exception)" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:102 #: ..\..\plugins\mapdoom3\Quake4MapReader.cpp:54 msgid "Could not recognise map version number format." msgstr "Nicht erkennbares Versionsnummernformat" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:110 #: ..\..\plugins\mapdoom3\Quake4MapReader.cpp:62 msgid "Incorrect map version: required {0:f}, found {1:f}" msgstr "Ungültige Map-Version: erwartet: {0:f}, gefunden: {1:f}" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:143 #: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:101 msgid "Primitive #{0:d}: parse error" msgstr "Primitiv #{0:d}: Parserfehler" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:153 #: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:111 msgid "Primitive #{0:d}: parse exception {1}" msgstr "Primitiv #{0:d}: Parserfehler {1}" #: ..\..\plugins\mapdoom3\Doom3MapReader.cpp:244 #: ..\..\plugins\mapdoom3\Quake3MapReader.cpp:202 msgid "Parsed invalid value '{0}' for key '{1}'" msgstr "Ungültigen Wert '{0}' für Schlüssel '{1}' eingelesen" #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:125 #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef.cpp:234 msgid "BrushDefParser: invalid token '{0}'" msgstr "BrushDefParser: Ungültiges Token '{0}'" #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef3.cpp:115 msgid "BrushDef3Parser: invalid token '{0}'" msgstr "BrushDef3Parser: ungültiges Token '{0}'" #: ..\..\plugins\mapdoom3\primitiveparsers\BrushDef3.cpp:186 msgid "BrushDef3ParserQuake4: invalid token '{0}'" msgstr "BrushDef3ParserQuake4: ungültiges Token '{0}'" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:44 msgid "Particle Editor" msgstr "Partikeleditor" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:208 msgid "Stage" msgstr "Stufe" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1034 msgid "Note: changes will be written to the file {0}" msgstr "Hinweis: alle Änderungen werden in den File {0} geschrieben" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1341 msgid "Save Changes" msgstr "Änderungen Speichern" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1342 msgid "" "Do you want to save the changes\n" "you made to the particle {0}?" msgstr "Änderungen am Partikel {0} speichern?" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1375 msgid "" "Error saving particle definition:\n" "{0}" msgstr "" "Fehler beim Speichern der Partikeldefinition:\n" "{0}" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1505 msgid "Select .prt file" msgstr "Eine .prt-Datei auswählen" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1539 msgid "Enter Particle Name" msgstr "Partikelnamen eingeben" #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1553 msgid "Cannot create particle with an empty name." msgstr "Es ist nicht möglich einen Partikel ohne Namen zu erstellen." #: ..\..\plugins\particles\editor\ParticleEditor.cpp:1568 msgid "This name is already in use." msgstr "Dieser Name ist bereits in Verwendung." #: ..\..\plugins\particles\editor\ParticleEditorModule.h:56 msgid "Particle Editor..." msgstr "Partikeleditor..." #: ..\..\plugins\particles\ParticlesManager.cpp:284 msgid "Cannot save particle, it has not been registered yet." msgstr "Kann Partikeldefinition nicht speichern, sie wurde nicht registriert." #: ..\..\plugins\particles\ParticlesManager.cpp:364 msgid "Cannot open file for reading: {0}" msgstr "Kann die Datei nicht im Lesemodus öffnen: {0}" #: ..\..\plugins\particles\ParticlesManager.cpp:411 msgid "Could not remove the file {0}" msgstr "Kann die Datei nicht entfernen: {0}" #: ..\..\plugins\particles\ParticlesManager.cpp:425 msgid "Could not rename the temporary file {0}" msgstr "Konnte die temporäre Datei nicht umbenennen: {0}" #: ..\..\plugins\script\ScriptingSystem.cpp:239 #: ..\..\plugins\script\ScriptingSystem.cpp:242 msgid "Script" msgstr "Skript" #: ..\..\plugins\script\ScriptingSystem.cpp:483 msgid "Reload Scripts" msgstr "Skripts neu laden" #: ..\..\plugins\script\ScriptMenu.cpp:60 msgid "No scripts available" msgstr "Keine Skripts verfügbar" #: ..\..\plugins\script\ScriptWindow.cpp:37 msgid "Python Script Input" msgstr "Python Skripteingabe" #: ..\..\plugins\script\ScriptWindow.cpp:39 msgid "Run Script" msgstr "Starte Skript" #: ..\..\plugins\shaders\Doom3ShaderSystem.cpp:323 msgid "Loading Shaders" msgstr "Lade Shader" #: ..\..\plugins\shaders\ShaderFileLoader.cpp:95 msgid "Parsing material file {0}" msgstr "Lade Material Datei {0}" #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:16 #: xml_file_content.cpp:141 msgid "MD5 Animation Viewer" msgstr "MD5-Animationsvorschau" #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:83 #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:103 msgid "Model Definition" msgstr "Model-Definition" #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:86 msgid "Available Animations" msgstr "Verfügbare Animationen" #: ..\..\plugins\uimanager\animationpreview\MD5AnimationViewer.cpp:125 msgid "Animation" msgstr "Animation" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:24 msgid "Edit Colour Schemes" msgstr "Farbschemas bearbeiten" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:81 #: xml_file_content.cpp:5 msgid "Colour" msgstr "Farbe" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:93 #: xml_file_content.cpp:4 msgid "Copy" msgstr "Kopieren" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:268 msgid "Copy Colour Scheme" msgstr "Farbschema kopieren" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:268 msgid "Enter a name for the new scheme:" msgstr "Namen für das neue Schema eingeben:" #: ..\..\plugins\uimanager\colourscheme\ColourSchemeEditor.cpp:278 msgid "A Scheme with that name already exists." msgstr "Es existiert bereits ein Schema mit diesem Namen." #: ..\..\plugins\uimanager\SoundChooser.cpp:140 msgid "Choose sound" msgstr "Sound auswählen" #: ..\..\plugins\uimanager\SoundChooser.cpp:170 msgid "Soundshader" msgstr "Soundshader" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:28 msgid "Sound Files" msgstr "Sounddateien" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:57 msgid "Play" msgstr "Abspielen" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:60 msgid "Play and loop" msgstr "Loopen" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:63 msgid "Stop" msgstr "Stop" #: ..\..\plugins\uimanager\SoundShaderPreview.cpp:209 msgid "Error: File not found." msgstr "Fehler: Datei nicht gefunden." #: ..\..\plugins\uimanager\UIManager.cpp:119 msgid "Describes available Mouse Commands" msgstr "Listet verfügbare Mauskommandos auf" #: ..\..\plugins\undo\UndoSystem.cpp:379 msgid "Settings/Undo System" msgstr "Einstellungen/Undo" #: ..\..\plugins\undo\UndoSystem.cpp:380 msgid "Undo Queue Size" msgstr "Größe des Undo-Stapels" #: ..\..\libs\wxutil\dialog\MessageBox.cpp:20 #: xml_file_content.cpp:8 #: xml_file_content.cpp:10 #: xml_file_content.cpp:13 #: xml_file_content.cpp:4 #: xml_file_content.cpp:3 #: xml_file_content.cpp:21 msgid "Save" msgstr "Speichern" #: ..\..\libs\wxutil\dialog\MessageBox.cpp:20 msgid "Close without saving" msgstr "Schließen ohne zu speichern" #: ..\..\libs\wxutil\FileChooser.cpp:58 msgid "Open File" msgstr "Datei öffnen" #: ..\..\libs\wxutil\FileChooser.cpp:58 msgid "Save File" msgstr "Datei speichern" #: ..\..\libs\wxutil\FileChooser.cpp:121 msgid "All Files (*.*)" msgstr "Alle Dateien (*.*)" #: ..\..\libs\wxutil\ModalProgressDialog.cpp:20 #: ..\..\libs\wxutil\ModalProgressDialog.cpp:30 msgid "Operation cancelled by user" msgstr "Abbruch durch Benutzer" #: ..\..\libs\wxutil\Modifier.h:146 msgid "Alt" msgstr "Alt" #: ..\..\libs\wxutil\Modifier.h:147 msgid "Ctrl" msgstr "Strg" #: ..\..\libs\wxutil\Modifier.h:148 msgid "Shift" msgstr "Umschalt" #: ..\..\libs\wxutil\PathEntry.cpp:97 msgid "Choose File" msgstr "Datei auswählen" #: ..\..\libs\wxutil\PathEntry.cpp:118 msgid "Choose Directory" msgstr "Verzeichnis auswählen" #: ..\..\libs\wxutil\preview\ModelPreview.cpp:154 #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:182 msgid "" "Unable to setup the preview,\n" "could not find the entity class {0}" msgstr "" "Vorschau nicht möglich,\n" "Entityklasse {0} nicht vorhanden." #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:51 msgid "Show coordinate axes" msgstr "Zeige Koordinatenachsen" #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:57 msgid "Show wireframe" msgstr "Zeige Wireframe" #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:61 #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:63 msgid "Auto Loop" msgstr "Auto Loop" #: ..\..\libs\wxutil\preview\ParticlePreview.cpp:67 msgid "Reload Particle Defs" msgstr "Partikel-Definitionen neu laden" #: ..\..\libs\wxutil\preview\RenderPreview.cpp:102 #: ..\..\libs\wxutil\preview\RenderPreview.cpp:104 #: xml_file_content.cpp:1 msgid "Filters" msgstr "Filter" #: xml_file_content.cpp:1 msgid "&File" msgstr "&Datei" #: xml_file_content.cpp:2 msgid "&New Map" msgstr "&Neue Map" #: xml_file_content.cpp:3 msgid "&Open..." msgstr "&Öffnen" #: xml_file_content.cpp:4 msgid "&Import map..." msgstr "&Map importieren" #: xml_file_content.cpp:5 msgid "Import &prefab..." msgstr "&Prefab importieren" #: xml_file_content.cpp:6 msgid "&Save" msgstr "&Speichern" #: xml_file_content.cpp:7 msgid "Save &as..." msgstr "Speichern &als..." #: xml_file_content.cpp:8 msgid "Save © as..." msgstr "&Kopie speichern als..." #: xml_file_content.cpp:9 msgid "&Save selected as Map..." msgstr "&Auswahl als Map speichern..." #: xml_file_content.cpp:10 msgid "&Save selected as prefab..." msgstr "Auswahl als &Prefab speichern..." #: xml_file_content.cpp:11 msgid "&Export selected as model..." msgstr "Auswahl als Model exportieren..." #: xml_file_content.cpp:12 msgid "Save selected as Collision Model..." msgstr "_Auswahl als Kollisionsmodell speichern..." #: xml_file_content.cpp:13 msgid "Save ®ion..." msgstr "&Region speichern" #: xml_file_content.cpp:14 msgid "&Reload Models" msgstr "&Modelle neu laden" #: xml_file_content.cpp:15 msgid "Reload Selected Models" msgstr "Ausgewählte Modelle neu laden" #: xml_file_content.cpp:16 msgid "Reload S&kins" msgstr "S&kins neu laden" #: xml_file_content.cpp:17 #: xml_file_content.cpp:28 msgid "Reload Materials" msgstr "Materials neu laden" #: xml_file_content.cpp:18 msgid "Reload Defs" msgstr "Defs neu laden" #: xml_file_content.cpp:19 msgid "Reload Particles" msgstr "Partikel neu laden" #: xml_file_content.cpp:20 msgid "&Game/Project Setup..." msgstr "Spiel/Projekteinstellungen..." #: xml_file_content.cpp:21 msgid "&Pointfile" msgstr "Pointfile" #: xml_file_content.cpp:22 msgid "E&xit" msgstr "B&eenden" #: xml_file_content.cpp:23 msgid "&Edit" msgstr "Bearbeiten" #: xml_file_content.cpp:24 msgid "&Undo" msgstr "Rückgängig" #: xml_file_content.cpp:25 msgid "&Redo" msgstr "Wiede&rherstellen" #: xml_file_content.cpp:26 msgid "&Copy" msgstr "&Kopieren" #: xml_file_content.cpp:27 msgid "&Paste" msgstr "&Einfügen" #: xml_file_content.cpp:28 msgid "Paste to Camera" msgstr "Bei Kameraposition einfügen" #: xml_file_content.cpp:29 msgid "&Duplicate" msgstr "&Duplizieren" #: xml_file_content.cpp:30 msgid "D&elete" msgstr "&Löschen" #: xml_file_content.cpp:31 msgid "Rep&arent Primitives" msgstr "Primitive neu zuordnen (Elternknoten ändern)" #: xml_file_content.cpp:32 msgid "Reparent Primitives to &Worldspawn" msgstr "Primitive Worldspawn zuordnen" #: xml_file_content.cpp:33 msgid "Merge selected Entities" msgstr "Ausgewählte Entities zusammenführen" #: xml_file_content.cpp:34 msgid "Copy Shader" msgstr "Shader kopieren" #: xml_file_content.cpp:35 msgid "Paste Shader" msgstr "Shader einfügen" #: xml_file_content.cpp:36 msgid "Paste Shader (Natural)" msgstr "Shader einfügen (Natürliche Skalierung)" #: xml_file_content.cpp:37 msgid "C&lear selection" msgstr "Auswahl aufheben" #: xml_file_content.cpp:38 msgid "&Invert selection" msgstr "Auswahl umkehren" #: xml_file_content.cpp:39 msgid "Select complete t&all" msgstr "Inneres auswählen (Projektion)" #: xml_file_content.cpp:40 msgid "Select i&nside" msgstr "Inneres auswählen" #: xml_file_content.cpp:41 msgid "Select &touching" msgstr "Berührtes auswählen" #: xml_file_content.cpp:42 msgid "Select Children" msgstr "Kinder auswählen" #: xml_file_content.cpp:43 msgid "Expand selection to all Siblings" msgstr "Auswahl auf Geschwister ausweiten" #: xml_file_content.cpp:44 msgid "Select all of type" msgstr "Alles vom selben Typ auswählen" #: xml_file_content.cpp:45 msgid "Keyboard Shortcuts..." msgstr "Tastenkombinationen..." #: xml_file_content.cpp:46 msgid "Mouse Bindings..." msgstr "Maustastenbelegungen..." #: xml_file_content.cpp:47 msgid "Pre&ferences..." msgstr "Einstellungen..." #: xml_file_content.cpp:48 msgid "V&iew" msgstr "Ansicht" #: xml_file_content.cpp:49 msgid "New XY view" msgstr "Neue XY-Ansicht" #: xml_file_content.cpp:52 msgid "Console View" msgstr "Konsole" #: xml_file_content.cpp:54 msgid "Media Browser" msgstr "Mediabrowser" #: xml_file_content.cpp:55 msgid "Entity Inspector" msgstr "Entity Inspector" #: xml_file_content.cpp:56 msgid "&Light Inspector" msgstr "Light Inspector" #: xml_file_content.cpp:57 msgid "&Surface Inspector" msgstr "Surface Inspector" #: xml_file_content.cpp:58 msgid "&Patch Inspector" msgstr "Patch Inspector" #: xml_file_content.cpp:59 msgid "&Texture Tool" msgstr "Textur-Werkzeug" #: xml_file_content.cpp:61 msgid "AAS Area Viewer" msgstr "AAS Visualisierung" #: xml_file_content.cpp:63 msgid "&Center" msgstr "Zentrieren" #: xml_file_content.cpp:64 msgid "&Up Floor" msgstr "Ein Stockwerk höher" #: xml_file_content.cpp:65 msgid "&Down Floor" msgstr "Ein Stockwerk tiefer" #: xml_file_content.cpp:66 msgid "Far Clip Plane In" msgstr "Entfernte Clip-Ebene heran" #: xml_file_content.cpp:67 msgid "Far Clip Plane Out" msgstr "Entfernte Clip-Ebene hinaus" #: xml_file_content.cpp:68 msgid "Next leak spot" msgstr "Nächster Leak-Spot" #: xml_file_content.cpp:69 msgid "Previous leak spot" msgstr "Vorheriger Leak-Spot" #: xml_file_content.cpp:70 msgid "Orthographic" msgstr "2D-Ansicht" #: xml_file_content.cpp:71 msgid "Next (XY, XZ, YZ)" msgstr "Nächste Ansicht (XY, XZ, YZ)" #: xml_file_content.cpp:72 msgid "XY (Top)" msgstr "XY (Oben)" #: xml_file_content.cpp:73 msgid "YZ" msgstr "YZ" #: xml_file_content.cpp:74 msgid "XZ" msgstr "XZ" #: xml_file_content.cpp:75 msgid "&XY 100%" msgstr "XY 100%" #: xml_file_content.cpp:76 msgid "&XY Zoom In" msgstr "XY Einzoomen" #: xml_file_content.cpp:77 msgid "&XY Zoom Out" msgstr "XY Auszoomen" #: xml_file_content.cpp:78 msgid "Show" msgstr "Zeigen" #: xml_file_content.cpp:79 msgid "Show &Angles" msgstr "Zeige Winkel" #: xml_file_content.cpp:80 msgid "Show &Names" msgstr "Zeige Namen" #: xml_file_content.cpp:82 msgid "Show C&oordinates" msgstr "Zeige Koordinaten" #: xml_file_content.cpp:86 msgid "Show size info" msgstr "Zeige Größeninformation" #: xml_file_content.cpp:87 msgid "Hide/Show" msgstr "Verbergen/Zeigen" #: xml_file_content.cpp:88 msgid "Hide Selected" msgstr "Auswahl verbergen" #: xml_file_content.cpp:89 msgid "Hide Deselected" msgstr "Nicht ausgewähltes verbergen" #: xml_file_content.cpp:90 msgid "Show hidden" msgstr "Zeige Verborgenes" #: xml_file_content.cpp:92 msgid "&Switch off" msgstr "Ausschalten" #: xml_file_content.cpp:93 msgid "Set from &XY view" msgstr "Über XY-Ansicht festlegen" #: xml_file_content.cpp:94 msgid "Set from &Brush" msgstr "Über Brush festlegen" #: xml_file_content.cpp:95 msgid "Set from Se&lection" msgstr "Über Auswahl festlegen" #: xml_file_content.cpp:96 msgid "Colours..." msgstr "Farben..." #: xml_file_content.cpp:97 msgid "Background Image..." msgstr "Hintergrundbild..." #: xml_file_content.cpp:98 msgid "Mo&dify" msgstr "Komponentenmodus" #: xml_file_content.cpp:99 #: xml_file_content.cpp:16 msgid "Components" msgstr "Komponenten" #: xml_file_content.cpp:100 msgid "&Edges" msgstr "Edges" #: xml_file_content.cpp:101 msgid "&Vertices" msgstr "Vertices" #: xml_file_content.cpp:102 msgid "&Faces" msgstr "Faces" #: xml_file_content.cpp:103 msgid "En&tities" msgstr "Entities" #: xml_file_content.cpp:104 msgid "Nudge" msgstr "Verschieben" #: xml_file_content.cpp:105 msgid "Nudge Left" msgstr "Verschiebe nach links" #: xml_file_content.cpp:106 msgid "Nudge Right" msgstr "Verschiebe nach rechts" #: xml_file_content.cpp:107 msgid "Nudge Up" msgstr "Verschiebe nach oben" #: xml_file_content.cpp:108 msgid "Nudge Down" msgstr "Verschiebe nach unten" #: xml_file_content.cpp:109 #: xml_file_content.cpp:16 msgid "Rotate" msgstr "Drehen" #: xml_file_content.cpp:110 msgid "Rotate X" msgstr "Drehe um X-Achse" #: xml_file_content.cpp:111 msgid "Rotate Y" msgstr "Drehe um Y-Achse" #: xml_file_content.cpp:112 msgid "Rotate Z" msgstr "Drehe um Z-Achse" #: xml_file_content.cpp:113 msgid "Mirror" msgstr "Spiegeln" #: xml_file_content.cpp:114 msgid "Mirror &X" msgstr "Spiegeln um X-Achse" #: xml_file_content.cpp:115 msgid "Mirror &Y" msgstr "Spiegeln um Y-Achse" #: xml_file_content.cpp:116 msgid "Mirror &Z" msgstr "Spiegeln um Z-Achse" #: xml_file_content.cpp:117 #: xml_file_content.cpp:10 msgid "Rotate Objects independently" msgstr "Objekte unabhängig voneinander drehen" #: xml_file_content.cpp:118 msgid "Rotate and scale..." msgstr "Drehen und skalieren..." #: xml_file_content.cpp:119 msgid "&Grid" msgstr "Raster" #: xml_file_content.cpp:120 msgid "Snap selected to grid" msgstr "Auswahl am Raster ausrichten" #: xml_file_content.cpp:121 msgid "Grid0.125" msgstr "Grid0.125" #: xml_file_content.cpp:122 msgid "Grid0.25" msgstr "Grid0.25" #: xml_file_content.cpp:123 msgid "Grid0.5" msgstr "Grid0.5" #: xml_file_content.cpp:124 msgid "Grid1" msgstr "Grid1" #: xml_file_content.cpp:125 msgid "Grid2" msgstr "Grid2" #: xml_file_content.cpp:126 msgid "Grid4" msgstr "Grid4" #: xml_file_content.cpp:127 msgid "Grid8" msgstr "Grid8" #: xml_file_content.cpp:128 msgid "Grid16" msgstr "Grid16" #: xml_file_content.cpp:129 msgid "Grid32" msgstr "Grid32" #: xml_file_content.cpp:130 msgid "Grid64" msgstr "Grid64" #: xml_file_content.cpp:131 msgid "Grid128" msgstr "Grid128" #: xml_file_content.cpp:132 msgid "Grid256" msgstr "Grid256" #: xml_file_content.cpp:133 msgid "M&ap" msgstr "Map" #: xml_file_content.cpp:134 msgid "Find brush..." msgstr "Brush suchen..." #: xml_file_content.cpp:135 msgid "Find and replace textures..." msgstr "Texturen suchen & ersetzen..." #: xml_file_content.cpp:136 msgid "Map info..." msgstr "Map-Information" #: xml_file_content.cpp:137 msgid "E&ntity" msgstr "Entity" #: xml_file_content.cpp:138 msgid "&Revert group to worldspawn" msgstr "Gruppe zu Worldspawn umwandeln" #: xml_file_content.cpp:139 msgid "&Connect selected entities" msgstr "Ausgewählte Entities verbinden" #: xml_file_content.cpp:140 msgid "&Bind selected entities" msgstr "Ausgewählte Entities verknüpfen" #: xml_file_content.cpp:142 msgid "B&rush" msgstr "Brush" #: xml_file_content.cpp:143 msgid "Prism..." msgstr "Prisma..." #: xml_file_content.cpp:144 msgid "Cone..." msgstr "Kegel..." #: xml_file_content.cpp:145 msgid "Sphere..." msgstr "Kugel..." #: xml_file_content.cpp:146 msgid "CSG" msgstr "CSG" #: xml_file_content.cpp:147 msgid "Make &Hollow" msgstr "Aushöhlen" #: xml_file_content.cpp:148 msgid "Make &Room" msgstr "Raum erstellen" #: xml_file_content.cpp:149 msgid "CSG &Subtract" msgstr "CSG Subtraktion" #: xml_file_content.cpp:150 msgid "CSG &Merge" msgstr "CSG Zusammenfügen" #: xml_file_content.cpp:152 msgid "Clip Selection" msgstr "Auswahl clippen" #: xml_file_content.cpp:153 msgid "Split Selection" msgstr "Auswahl splitten" #: xml_file_content.cpp:154 msgid "Flip Clip Orientation" msgstr "Clip-Orientierung umkehren" #: xml_file_content.cpp:155 msgid "Texture lock" msgstr "Textur-Lock" #: xml_file_content.cpp:156 msgid "Create Decal Patches" msgstr "Decal-Patches erstellen" #: xml_file_content.cpp:158 msgid "Make Detail" msgstr "Als Detail markieren" #: xml_file_content.cpp:159 msgid "Make Structural" msgstr "Als Struktur markieren" #: xml_file_content.cpp:160 msgid "&Patch" msgstr "Patch" #: xml_file_content.cpp:161 msgid "Create Simple Patch Mesh" msgstr "Neuer einfacher Patch" #: xml_file_content.cpp:162 msgid "Create End cap" msgstr "Endcap erstellen" #: xml_file_content.cpp:163 msgid "Create Bevel" msgstr "Bogen erstellen" #: xml_file_content.cpp:164 msgid "Create Cone" msgstr "Kegel erstellen" #: xml_file_content.cpp:165 msgid "Create Cylinder" msgstr "Zylinder erstellen" #: xml_file_content.cpp:166 msgid "Create Sphere" msgstr "Kugel erzeugen" #: xml_file_content.cpp:167 msgid "More cylinders" msgstr "Mehr Zylinderarten" #: xml_file_content.cpp:168 msgid "Create Dense Cylinder" msgstr "Erstelle \"dichten\" Zylinder" #: xml_file_content.cpp:169 msgid "Create Very Dense Cylinder" msgstr "Erstelle \"sehr dichten\" Zylinder" #: xml_file_content.cpp:170 msgid "Create Square Cylinder" msgstr "Erstelle quadratischen Zylinder" #: xml_file_content.cpp:171 msgid "Insert" msgstr "Einfügen" #: xml_file_content.cpp:172 msgid "Insert 2 Columns at the beginning" msgstr "Zwei Spalten am Anfang einfügen" #: xml_file_content.cpp:173 msgid "Insert 2 Columns at the end" msgstr "Zwei Spalten am Ende einfügen" #: xml_file_content.cpp:174 msgid "Insert 2 Rows at the beginning" msgstr "Zwei Zeilen am Anfang einfügen" #: xml_file_content.cpp:175 msgid "Insert 2 Rows at the end" msgstr "Zwei Zeilen am Ende einfügen" #: xml_file_content.cpp:176 msgid "Append" msgstr "Anfügen" #: xml_file_content.cpp:177 msgid "Append 2 columns at the beginning" msgstr "Zwei Spalten am Anfang anfügen" #: xml_file_content.cpp:178 msgid "Append 2 columns at the end" msgstr "Zwei Spalten am Ende anfügen" #: xml_file_content.cpp:179 msgid "Append 2 rows at the beginning" msgstr "Zwei Zeilen am Anfang anfügen" #: xml_file_content.cpp:180 msgid "Append 2 rows at the end" msgstr "Zwei Zeilen am Ende anfügen" #: xml_file_content.cpp:182 msgid "Delete 2 columns from the beginning" msgstr "Zwei Spalten am Anfang löschen" #: xml_file_content.cpp:183 msgid "Delete 2 columns from the end" msgstr "Zwei Spalten am Ende löschen" #: xml_file_content.cpp:184 msgid "Delete 2 rows from the beginning" msgstr "Zwei Zeilen am Anfang löschen" #: xml_file_content.cpp:185 msgid "Delete 2 rows from the end" msgstr "Zwei Spalten am Ende löschen" #: xml_file_content.cpp:186 msgid "Matrix" msgstr "Matrix" #: xml_file_content.cpp:187 msgid "Invert" msgstr "Invertieren" #: xml_file_content.cpp:188 msgid "Re-disperse" msgstr "Neu verteilen" #: xml_file_content.cpp:189 msgid "Rows" msgstr "Zeilen" #: xml_file_content.cpp:190 msgid "Columns" msgstr "Spalten" #: xml_file_content.cpp:191 msgid "Transpose" msgstr "Transponieren" #: xml_file_content.cpp:192 msgid "Thicken Selected Patches" msgstr "Ausgewählte Patches extrudieren" #: xml_file_content.cpp:193 msgid "Cap Selection" msgstr "Cap für Auswahl erstellen" #: xml_file_content.cpp:194 msgid "Stitch Patch Textures" msgstr "Patch-Texturen zusammenfügen" #: xml_file_content.cpp:195 msgid "Bulge patch" msgstr "Patch auswölben" #: xml_file_content.cpp:196 msgid "&Curve" msgstr "Kurve" #: xml_file_content.cpp:197 msgid "Create NURBS Curve" msgstr "NURBS-Kurve erstellen" #: xml_file_content.cpp:198 msgid "Create CatmullRom Curve" msgstr "CatmullRom-Kurve erstellen" #: xml_file_content.cpp:199 msgid "Convert NURBS <-> CatmullRom" msgstr "Konversion NURBS <-> CatmullRom" #: xml_file_content.cpp:200 msgid "Append Curve Control Point" msgstr "Kurvenkontrollpunkt anfügen" #: xml_file_content.cpp:201 msgid "Insert Curve Control Points" msgstr "Kurvenkontrollpunkt einfügen" #: xml_file_content.cpp:202 msgid "Remove Curve Control Points" msgstr "Kurvenkontrollpunkt entfernen" #: xml_file_content.cpp:203 msgid "&Help" msgstr "Hilfe" #: xml_file_content.cpp:204 msgid "&About" msgstr "Über DarkRadiant" #: xml_file_content.cpp:1 msgid "Texture Background" msgstr "Texturbrowser-Hintergrund" #: xml_file_content.cpp:2 msgid "Grid Background" msgstr "Rasterhintergrund" #: xml_file_content.cpp:3 msgid "Grid Major" msgstr "Grobes Raster" #: xml_file_content.cpp:4 msgid "Grid Minor" msgstr "Feines Raster" #: xml_file_content.cpp:5 msgid "Grid Text" msgstr "Rastertext" #: xml_file_content.cpp:6 msgid "Grid Block" msgstr "Rasterblock" #: xml_file_content.cpp:7 msgid "Brushes" msgstr "Brushes" #: xml_file_content.cpp:8 msgid "Entities (default)" msgstr "Standard-Entities" #: xml_file_content.cpp:9 msgid "Camera Background" msgstr "Kamera-Hintergrund" #: xml_file_content.cpp:10 msgid "Selected Items" msgstr "Ausgewählte Elemente" #: xml_file_content.cpp:11 msgid "Selected Group Items" msgstr "Ausgewählte Gruppenelemente" #: xml_file_content.cpp:12 msgid "Selected Items (Camera)" msgstr "Ausgewählte Elemente (Kamera)" #: xml_file_content.cpp:14 msgid "Active View Name" msgstr "Name der aktiven Ansicht" #: xml_file_content.cpp:15 msgid "X-Axis" msgstr "X-Achse" #: xml_file_content.cpp:16 msgid "Y-Axis" msgstr "Y-Achse" #: xml_file_content.cpp:17 msgid "Z-Axis" msgstr "Z-Achse" #: xml_file_content.cpp:18 msgid "Workzone" msgstr "Arbeitsbereich" #: xml_file_content.cpp:19 msgid "Camera Icon" msgstr "Kamera-Symbol" #: xml_file_content.cpp:20 msgid "Brush Size Info" msgstr "Brush-Größeninformation" #: xml_file_content.cpp:21 msgid "Light Volumes" msgstr "Lichtvolumina" #: xml_file_content.cpp:22 msgid "Brush Vertices" msgstr "Brush-Vertices" #: xml_file_content.cpp:23 msgid "Patch Inner Vertex" msgstr "Patch-Vertices (innen)" #: xml_file_content.cpp:24 msgid "Patch Corner Vertex" msgstr "Patch-Vertices (Ecke)" #: xml_file_content.cpp:25 msgid "Light Vertices (deselected)" msgstr "Licht-Vertices (nicht ausgewählt)" #: xml_file_content.cpp:26 msgid "Light Vertices (selected)" msgstr "Licht-Vertices (ausgewählt)" #: xml_file_content.cpp:27 msgid "Light Vertices (normal)" msgstr "Licht-Vertices (normal)" #: xml_file_content.cpp:28 msgid "Light Start/End (selected)" msgstr "Licht-Anfangs- und End-Vertices (ausgewählt)" #: xml_file_content.cpp:29 msgid "Light Start/End (deselected)" msgstr "Licht-Anfangs- und End-Vertices (nicht ausgewählt)" #: xml_file_content.cpp:30 msgid "XYView Crosshairs" msgstr "Fadenkreuz (XY-Ansicht)" #: xml_file_content.cpp:31 msgid "Drag-Selection Box" msgstr "Auswahl-Rechteck" #: xml_file_content.cpp:1 msgid "Open a map file" msgstr "Map öffnen" #: xml_file_content.cpp:2 msgid "Save the current map" msgstr "Die aktuelle Map speichern" #: xml_file_content.cpp:3 msgid "Change views" msgstr "Ansicht wechseln" #: xml_file_content.cpp:4 msgid "Select complete tall" msgstr "Inneres auswählen (Projektion)" #: xml_file_content.cpp:5 msgid "Select touching" msgstr "Berührtes auswählen" #: xml_file_content.cpp:6 msgid "Select inside" msgstr "Inneres auswählen" #: xml_file_content.cpp:7 msgid "Create Decals for selected Faces" msgstr "Decal-Patches für ausgewählte Faces erstellen" #: xml_file_content.cpp:8 msgid "x-axis Mirror" msgstr "An X-Achse Spiegeln" #: xml_file_content.cpp:9 msgid "x-axis Rotate" msgstr "Um X-Achse rotieren" #: xml_file_content.cpp:10 msgid "y-axis Mirror" msgstr "An Y-Achse Spiegeln" #: xml_file_content.cpp:11 msgid "y-axis Rotate" msgstr "Um Y-Achse rotieren" #: xml_file_content.cpp:12 msgid "z-axis Mirror" msgstr "An Z-Achse Spiegeln" #: xml_file_content.cpp:13 msgid "z-axis Rotate" msgstr "Um Z-Achse rotieren" #: xml_file_content.cpp:14 msgid "Snap selection to grid" msgstr "Auswahl am Raster ausrichten" #: xml_file_content.cpp:15 msgid "Floor selection" msgstr "Auswahl auf den Boden setzen" #: xml_file_content.cpp:16 msgid "CSG Subtract" msgstr "CSG Subtraktion" #: xml_file_content.cpp:17 msgid "CSG Merge" msgstr "CSG Zusammenfügen" #: xml_file_content.cpp:18 msgid "Hollow" msgstr "Aushöhlen" #: xml_file_content.cpp:19 msgid "Make Room" msgstr "Raum erstellen" #: xml_file_content.cpp:20 msgid "Group selected items" msgstr "Ausgewählte Elemente gruppieren" #: xml_file_content.cpp:21 msgid "Ungroup selected items" msgstr "Gruppierte Elemente auflösen" #: xml_file_content.cpp:22 msgid "Put caps on the current patch" msgstr "Cap-Patches für Auswahl erstellen" #: xml_file_content.cpp:23 msgid "Creates a NURBS curve" msgstr "NURBS-Kurve erstellen" #: xml_file_content.cpp:24 msgid "Convert the selected curve (NURBS <-> CatmullRom)" msgstr "Konvertiert die ausgewählte Kurve NURBS <-> CatmullRom" #: xml_file_content.cpp:25 msgid "Appends a control point to the selected curves" msgstr "Einen Kurvenkontrollpunkt zur ausgewählten Kurve hinzufügen" #: xml_file_content.cpp:26 msgid "Inserts a curve control point before the selected ones" msgstr "Fügt einen Kurvenkontrollpunkt vor dem ausgewählten Punkt ein" #: xml_file_content.cpp:27 msgid "Removes the selected curve control points" msgstr "Ausgewählte Kurvenkontrollpunkte entfernen" #: xml_file_content.cpp:29 msgid "Find & Replace" msgstr "Suchen & Ersetzen" #: xml_file_content.cpp:30 msgid "Decrease Grid Size" msgstr "Rastergröße verkleinern" #: xml_file_content.cpp:31 msgid "Increase Grid Size" msgstr "Rastergröße vergrößern" #: xml_file_content.cpp:32 msgid "Snap to Grid" msgstr "Am Raster ausrichten" #: xml_file_content.cpp:33 msgid "Merge Selection" msgstr "Auswahl zusammenführen" #: xml_file_content.cpp:34 msgid "Flip Selection Horiz (S-Axis)" msgstr "Auswahl Horizontal kippen (S-Achse)" #: xml_file_content.cpp:35 msgid "Flip Selection Vertical (T-Axis)" msgstr "Auswahl vertikal kippen (T-Achse)" #: xml_file_content.cpp:36 msgid "Select Related Items" msgstr "Verbundene Objekte auswählen" #: xml_file_content.cpp:1 msgid "Cubic clip the camera view" msgstr "Far-Clip-Ebene verwenden" #: xml_file_content.cpp:2 msgid "Select Vertices" msgstr "Vertices auswählen" #: xml_file_content.cpp:3 msgid "Select Edges" msgstr "Kanten auswählen" #: xml_file_content.cpp:5 msgid "Select Entities" msgstr "Entities auswählen" #: xml_file_content.cpp:6 msgid "Select Group Parts" msgstr "Teile einer Gruppe auswähen" #: xml_file_content.cpp:7 msgid "Show/hide all light volumes" msgstr "Zeige/verberge alle Lichtvolumina" #: xml_file_content.cpp:8 msgid "Show/hide all speaker volumes" msgstr "Zeige/verberge alle Speaker-Volumina" #: xml_file_content.cpp:11 msgid "Rotate func_* Entities around origin" msgstr "Drehe func_*-Entities um deren Ursprung" #: xml_file_content.cpp:12 msgid "Snap Rotation Pivot to Grid" msgstr "Rotationspivot am Raster ausrichten" #: xml_file_content.cpp:13 msgid "Drag-resize entities symmetrically (leave origin unchanged)" msgstr "Symmetrisches Verändern von Entities beim Ziehen (Ursprung bleibt unverändert)" #: xml_file_content.cpp:14 msgid "Offset cloned objects by one grid unit to the right and downwards." msgstr "Duplizierte Objekte um eine Rastereinheit nach rechts unten verschieben" #: xml_file_content.cpp:15 msgid "Translate" msgstr "Translahieren" #: xml_file_content.cpp:17 msgid "Resize" msgstr "Größe ändern" #: xml_file_content.cpp:19 msgid "Model Scaler" msgstr "Modell-Skalierung" #: xml_file_content.cpp:20 msgid "Hide Unused" msgstr "Nicht Benutztes verbergen" #: xml_file_content.cpp:21 msgid "Toggle Grid" msgstr "Raster anzeigen/verbergen" #: xml_file_content.cpp:22 msgid "Center Pivot when scaling faces" msgstr "Pivotpunkt beim Skalieren von Faces zentrieren" #: xml_file_content.cpp:1 msgid "DarkRadiant x.y.z" msgstr "DarkRadiant x.y.z" #: xml_file_content.cpp:2 msgid "Build date: 2014-08-04" msgstr "Kompilierungsdatum: %s" #: xml_file_content.cpp:3 msgid "" "http://www.darkradiant.net/\n" "\n" "This product contains software technology\n" "from id Software, Inc. ('id Technology').\n" "id Technology 2000 id Software,Inc.\n" "DarkRadiant is originally based \n" "on the GPL version of GtkRadiant.\n" "\n" "The Dark Mod (www.thedarkmod.com)" msgstr "" "http://www.darkradiant.net/\n" "\n" "Dieses Produkt enthält teilweise Software \n" "der Firma id Software, Inc. ('id Technology').\n" "id Technology 2000 id Software,Inc.\n" "\n" "DarkRadiant basiert auf der GPL version\n" "von GtkRadiant (www.qeradiant.com)\n" "\n" "The Dark Mod (www.thedarkmod.com)" #: xml_file_content.cpp:4 msgid "wxWidgets Properties" msgstr "wxWidgets Eigenschaften" #: xml_file_content.cpp:5 #: xml_file_content.cpp:8 msgid "Version:" msgstr "Version:" #: xml_file_content.cpp:6 msgid "OpenGL Properties" msgstr "OpenGL Eigenschaften" #: xml_file_content.cpp:7 msgid "Vendor:" msgstr "Hersteller:" #: xml_file_content.cpp:9 msgid "Renderer:" msgstr "Renderer:" #: xml_file_content.cpp:10 msgid "OpenGL Extensions" msgstr "OpenGL Erweiterungen" #: xml_file_content.cpp:11 msgid "DarkRadiant Modules" msgstr "DarkRadiant Module" #: xml_file_content.cpp:1 msgid "wireframeBtn" msgstr "wireframeBtn" #: xml_file_content.cpp:2 msgid "flatShadeBtn" msgstr "flatShadeBtn" #: xml_file_content.cpp:3 msgid "texturedBtn" msgstr "texturedBtn" #: xml_file_content.cpp:4 msgid "lightingBtn" msgstr "lightingBtn" #: xml_file_content.cpp:5 #: xml_file_content.cpp:1 msgid "startTimeButton" msgstr "startTimeButton" #: xml_file_content.cpp:6 #: xml_file_content.cpp:3 msgid "stopTimeButton" msgstr "stopTimeButton" #: xml_file_content.cpp:7 msgid "clipPlaneInButton" msgstr "clipPlaneInButton" #: xml_file_content.cpp:8 msgid "clipPlaneOutButton" msgstr "clipPlaneOutButton" #: xml_file_content.cpp:3 msgid "Command Arguments" msgstr "Befehlsargumente" #: xml_file_content.cpp:4 msgid "Command Properties" msgstr "Befehlseigenschaften" #: xml_file_content.cpp:5 msgid "Wait until finished" msgstr "Warten bis Befehl fertig ausgeführt wurde" #: xml_file_content.cpp:1 msgid "Conversation Entities" msgstr "Konversations-Entities" #: xml_file_content.cpp:4 msgid "Conversations" msgstr "Konversationen" #: xml_file_content.cpp:8 #: xml_file_content.cpp:7 msgid "Clear" msgstr "Löschen" #: xml_file_content.cpp:1 msgid "Properties" msgstr "Eigenschaften" #: xml_file_content.cpp:3 msgid "Actors must be within talk distance" msgstr "Akteur müssen in Sprechweite sein" #: xml_file_content.cpp:4 msgid "Actors always face each other while talking" msgstr "Akteure sehen sich beim Sprechen gegenseitig an" #: xml_file_content.cpp:5 msgid "Let this conversation play" msgstr "Diese Konversation wird maximal" #: xml_file_content.cpp:6 msgid "times at maximum" msgstr "Mal(e) abgespielt" #: xml_file_content.cpp:7 msgid "Actors" msgstr "Akteure" #: xml_file_content.cpp:10 msgid "Validate all" msgstr "Alle überprüfen" #: xml_file_content.cpp:11 msgid "Commands" msgstr "Befehle" #: xml_file_content.cpp:14 #: xml_file_content.cpp:9 msgid "Move up" msgstr "Nach oben" #: xml_file_content.cpp:15 #: xml_file_content.cpp:10 msgid "Move down" msgstr "Nach unten" #: xml_file_content.cpp:3 msgid "Refresh" msgstr "Neu laden" #: xml_file_content.cpp:5 msgid "Classname:" msgstr "Klassenname:" #: xml_file_content.cpp:6 msgid "Spawnarg:" msgstr "Wertepaar:" #: xml_file_content.cpp:7 msgid "Argument:" msgstr "Argument:" #: xml_file_content.cpp:1 msgid "Output File" msgstr "Zieldatei" #: xml_file_content.cpp:2 msgid "Output Format:" msgstr "Export-Format:" #: xml_file_content.cpp:3 msgid "File Path:" msgstr "Dateipfad:" #: xml_file_content.cpp:4 msgid "Export Options" msgstr "Export-Optionen" #: xml_file_content.cpp:5 msgid "Center Objects around Origin" msgstr "Objekte um den Ursprung anordnen" #: xml_file_content.cpp:6 msgid "Skip Surfaces textured with Caulk" msgstr "Caulk-Flächen nicht exportieren" #: xml_file_content.cpp:7 msgid "Replace Selection with exported Model" msgstr "Auswahl durch exportiertes Modelle ersetzen" #: xml_file_content.cpp:8 msgid "Use Entity Origin as export Origin" msgstr "Verwende den Entity-Origin als Ursprung" #: xml_file_content.cpp:10 msgid "Export" msgstr "Export" #: xml_file_content.cpp:3 #: xml_file_content.cpp:68 msgid "View" msgstr "Ansicht" #: xml_file_content.cpp:2 msgid "Rules" msgstr "Regeln" #: xml_file_content.cpp:7 msgid "" "Filter rules are applied in the shown order.\n" "\"Match\" is accepting regular expressions.\n" "\"Object\"-type filters can be used to match \"patch\" or \"brush\"." msgstr "" "Filterregeln werden in der angegebenen Reihenfolge angewandt.\n" "Das Ausdruck-Feld akzeptiert reguläre Ausdrücke (RegEx).\n" "Für Filter vom Typ Objekt können die Schlüsselwörter patch oder brush verwendet werden." #: xml_file_content.cpp:1 msgid "Find:" msgstr "Suchen:" #: xml_file_content.cpp:2 msgid "Replace:" msgstr "Ersetzen:" #: xml_file_content.cpp:3 msgid "Search current selection only" msgstr "Durchsuche nur die aktuelle Auswahl" #: xml_file_content.cpp:4 msgid "x shader(s) replaced." msgstr "x shader ersetzt." #: xml_file_content.cpp:5 msgid "Find and Replace" msgstr "Suchen & Ersetzen" #: xml_file_content.cpp:6 #: xml_file_content.cpp:13 #: xml_file_content.cpp:88 msgid "Close" msgstr "Schließen" #: xml_file_content.cpp:1 msgid "Light Volume" msgstr "Lichtvolumina" #: xml_file_content.cpp:2 msgid "Omni" msgstr "Omni" #: xml_file_content.cpp:3 msgid "Projected" msgstr "Projektion" #: xml_file_content.cpp:4 msgid "Use start/end" msgstr "Verwende Start/End" #: xml_file_content.cpp:6 #: xml_file_content.cpp:9 msgid "Options" msgstr "Optionen" #: xml_file_content.cpp:7 msgid "Parallel" msgstr "Parallel" #: xml_file_content.cpp:8 msgid "Do not cast shadows (fast)" msgstr "Wirft keine Schatten (schnell)." #: xml_file_content.cpp:9 msgid "Skip specular lighting" msgstr "Kein Specularmap-Rendering" #: xml_file_content.cpp:10 msgid "Skip diffuse lighting" msgstr "Kein Diffusemap-Rendering" #: xml_file_content.cpp:11 msgid "Light Texture" msgstr "Lichttextur" #: xml_file_content.cpp:1 msgid "Mission Info" msgstr "Missions-Info" #: xml_file_content.cpp:2 msgid "Title:" msgstr "Titel:" #: xml_file_content.cpp:3 msgid "Author:" msgstr "Autor:" #: xml_file_content.cpp:4 msgid "Description:" msgstr "Beschreibung:" #: xml_file_content.cpp:6 msgid "Required TDM Version:" msgstr "Erforderliche TDM-Version:" #: xml_file_content.cpp:7 msgid "(optional, e.g. \"2.03\")" msgstr "(option, z.B. \"2.03\")" #: xml_file_content.cpp:8 msgid "Mission Titles:" msgstr "Missions-Titel:" #: xml_file_content.cpp:9 msgid "" "Optional. Fill in if this \n" "package is a campaign \n" "containing several maps" msgstr "" "Optional. Nur ausfüllen\n" "bei Kampagnen bestehend\n" "aus mehreren Missionen." #: xml_file_content.cpp:10 #: xml_file_content.cpp:2 msgid "Output Path:" msgstr "Zielpfad:" #: xml_file_content.cpp:11 #: xml_file_content.cpp:3 msgid "C:GamesDarkmodfmsgathers" msgstr "C:\\Games\\Darkmod\\fms\\gathers" #: xml_file_content.cpp:12 msgid "Edit readme.txt..." msgstr "Bearbeite readme.txt Datei..." #: xml_file_content.cpp:1 msgid "Mission Readme" msgstr "Missions-Readme" #: xml_file_content.cpp:1 msgid "Options:" msgstr "Optionen:" #: xml_file_content.cpp:2 msgid "Surround with monsterclip brush" msgstr "Mit Monsterclip einhüllen" #: xml_file_content.cpp:3 msgid "Reload Skins" msgstr "Skins neu laden" #: xml_file_content.cpp:4 msgid "Reload Models" msgstr "Modelle neu laden" #: xml_file_content.cpp:2 msgid "Difficulty" msgstr "Schwierigkeitsgrad" #: xml_file_content.cpp:3 msgid "Iniital State" msgstr "Anfangszustand" #: xml_file_content.cpp:4 #: xml_file_content.cpp:20 msgid "Flags" msgstr "Flags" #: xml_file_content.cpp:5 msgid "Mandatory" msgstr "Obgligatorisches Ziel" #: xml_file_content.cpp:6 msgid "Ongoing" msgstr "Laufend" #: xml_file_content.cpp:7 #: xml_file_content.cpp:22 msgid "Irreversible" msgstr "Irreversibel" #: xml_file_content.cpp:9 msgid "Enabling Objectives" msgstr "Vorausgesetzte Ziele" #: xml_file_content.cpp:10 msgid "Sucess Logic" msgstr "Logik für Erfüllung" #: xml_file_content.cpp:11 msgid "Failure Logic" msgstr "Logik für Fehlschlag" #: xml_file_content.cpp:12 msgid "Completion Script" msgstr "Skript bei Erfüllung" #: xml_file_content.cpp:13 msgid "Failure Script" msgstr "Skript bei Fehlschlag" #: xml_file_content.cpp:14 msgid "Completion Target" msgstr "Target bei Erfüllung" #: xml_file_content.cpp:15 msgid "Failure Target" msgstr "Target bei Fehlschlag" #: xml_file_content.cpp:21 msgid "Satisfied at start" msgstr "Bei Missionsstart erfüllt" #: xml_file_content.cpp:23 msgid "Boolean NOT" msgstr "Negieren" #: xml_file_content.cpp:24 msgid "Player responsible" msgstr "Spieler ist verantwortlich" #: xml_file_content.cpp:25 msgid "&OK" msgstr "OK" #: xml_file_content.cpp:26 msgid "&Cancel" msgstr "Abbrechen" #: xml_file_content.cpp:1 msgid "Objective Conditions" msgstr "Missionsziel-Bedingungen" #: xml_file_content.cpp:4 msgid "Condition" msgstr "Bedingung" #: xml_file_content.cpp:5 msgid "Source Mission:" msgstr "Quellmission:" #: xml_file_content.cpp:6 msgid "Source Objective:" msgstr "Quell-Missionsziel:" #: xml_file_content.cpp:7 msgid "Source Objective State:" msgstr "Status des Quell-Missionsziels:" #: xml_file_content.cpp:8 msgid "Target Objective:" msgstr "Missionsziel:" #: xml_file_content.cpp:9 msgid "Action:" msgstr "Aktion:" #: xml_file_content.cpp:10 msgid "Action Value:" msgstr "Wert:" #: xml_file_content.cpp:11 msgid "Sentence" msgstr "In Worten" #: xml_file_content.cpp:12 msgid "-" msgstr "-" #: xml_file_content.cpp:1 msgid "Objective Entities" msgstr "Missionsziel-Entities" #: xml_file_content.cpp:4 msgid "Objectives" msgstr "Missionsziele" #: xml_file_content.cpp:11 msgid "Success Logic" msgstr "Logik für Erfüllung:" #: xml_file_content.cpp:12 msgid "Edit Mission Success Logic" msgstr "Logik für Erfüllung bearbeiten" #: xml_file_content.cpp:1 msgid "Use background image" msgstr "Verwende Hintergrundbild" #: xml_file_content.cpp:2 msgid "Image file" msgstr "Bilddatei" #: xml_file_content.cpp:3 msgid "Transparency" msgstr "Transparenz" #: xml_file_content.cpp:4 msgid "transparent" msgstr "transparent" #: xml_file_content.cpp:5 msgid "opaque" msgstr "intransparent" #: xml_file_content.cpp:6 msgid "Image scale" msgstr "Bildskalierung" #: xml_file_content.cpp:7 msgid "Horizontal offset" msgstr "Horizontale Verschiebung" #: xml_file_content.cpp:8 msgid "Vertical offset" msgstr "Vertikale Verschiebung" #: xml_file_content.cpp:10 msgid "Keep aspect ratio" msgstr "Seitenverhältnis beibehalten" #: xml_file_content.cpp:11 msgid "Zoom image with viewport" msgstr "Bild zusammen mit 2D-Ansicht zoomen" #: xml_file_content.cpp:12 msgid "Pan image with viewport" msgstr "Bild zusammen mit 2D-Ansicht verschieben" #: xml_file_content.cpp:1 msgid "Particle Definitions" msgstr "Partikeldefinitionen" #: xml_file_content.cpp:5 msgid "Particle Stages" msgstr "Partikelstufen" #: xml_file_content.cpp:8 msgid "Toggle Visibility" msgstr "Anzeigen/Verstecken" #: xml_file_content.cpp:9 msgid "Up" msgstr "Nach oben" #: xml_file_content.cpp:10 msgid "Down" msgstr "Nach unten" #: xml_file_content.cpp:12 msgid "Depth Hack:" msgstr "Depth Hack:" #: xml_file_content.cpp:13 msgid "Stage Settings" msgstr "Stufeneinstellungen" #: xml_file_content.cpp:16 msgid "Colour:" msgstr "Farbe:" #: xml_file_content.cpp:17 msgid "Fade Colour:" msgstr "Fade-Farbe" #: xml_file_content.cpp:18 msgid "Use Entity Colour" msgstr "Entity-Farbe verwenden" #: xml_file_content.cpp:19 msgid "Fade In Fraction:" msgstr "Fade In Fraction:" #: xml_file_content.cpp:20 msgid "Fade Out Fraction:" msgstr "Fade Out Fraction:" #: xml_file_content.cpp:21 msgid "Fade Index Fraction:" msgstr "Fade Index Fraction:" #: xml_file_content.cpp:22 msgid "Animation:" msgstr "Animation:" #: xml_file_content.cpp:23 msgid "Frames:" msgstr "Frames:" #: xml_file_content.cpp:24 msgid "Rate:" msgstr "Abspielrate:" #: xml_file_content.cpp:25 msgid "FPS" msgstr "FPS" #: xml_file_content.cpp:26 msgid "Count / Time" msgstr "Anzahl / Zeit" #: xml_file_content.cpp:27 msgid "Count:" msgstr "Anzahl:" #: xml_file_content.cpp:28 msgid "Duration / sec:" msgstr "Dauer / Sek.:" #: xml_file_content.cpp:29 msgid "Bunching:" msgstr "Häufung:" #: xml_file_content.cpp:30 msgid "Cycles:" msgstr "Durchläufe:" #: xml_file_content.cpp:31 msgid "Time Offset / sec:" msgstr "Zeitversatz / Sek." #: xml_file_content.cpp:32 msgid "Dead Time / sec:" msgstr "Totzeit / Sek.:" #: xml_file_content.cpp:33 msgid "Size / Speed" msgstr "Größe / Geschw." #: xml_file_content.cpp:34 msgid "Speed:" msgstr "Geschw.:" #: xml_file_content.cpp:35 #: xml_file_content.cpp:38 #: xml_file_content.cpp:41 #: xml_file_content.cpp:44 msgid "From:" msgstr "Von:" #: xml_file_content.cpp:36 #: xml_file_content.cpp:39 #: xml_file_content.cpp:42 #: xml_file_content.cpp:45 msgid "To:" msgstr "Bis:" #: xml_file_content.cpp:37 msgid "Size:" msgstr "Größe:" #: xml_file_content.cpp:40 msgid "Rotation Speed:" msgstr "Rotationsgeschwindigkeit:" #: xml_file_content.cpp:43 msgid "Aspect Ratio:" msgstr "Seitenverhältnis:" #: xml_file_content.cpp:46 msgid "Gravity:" msgstr "Schwerkraft:" #: xml_file_content.cpp:47 msgid "Use World Gravity" msgstr "Welt-Schwerkraftrichtung verwenden" #: xml_file_content.cpp:48 msgid "Bounds Expansion:" msgstr "Bounds Expansion:" #: xml_file_content.cpp:49 msgid "Distribution" msgstr "Verteilung" #: xml_file_content.cpp:50 msgid "Shape:" msgstr "Form:" #: xml_file_content.cpp:51 msgid "Rectangular" msgstr "Rechteckig" #: xml_file_content.cpp:52 msgid "Cylindric" msgstr "Zylindrisch" #: xml_file_content.cpp:53 msgid "Spherical" msgstr "Sphärisch" #: xml_file_content.cpp:54 msgid "X Size:" msgstr "X-Größe:" #: xml_file_content.cpp:55 msgid "Y Size:" msgstr "Y-Größe:" #: xml_file_content.cpp:56 msgid "Z Size:" msgstr "Z-Größe:" #: xml_file_content.cpp:57 msgid "Ring Size:" msgstr "Ringgröße:" #: xml_file_content.cpp:58 msgid "Offset:" msgstr "Abstand:" #: xml_file_content.cpp:59 msgid "Randomness:" msgstr "Zufälligkeit:" #: xml_file_content.cpp:60 msgid "Distribute Particles randomly within Volume" msgstr "Verteile Polygone zufällig im angegebenen Volumen" #: xml_file_content.cpp:61 msgid "Direction / Orientation" msgstr "Richtung / Orientierung" #: xml_file_content.cpp:62 msgid "Direction:" msgstr "Richtung:" #: xml_file_content.cpp:63 msgid "Cone" msgstr "Kegel" #: xml_file_content.cpp:64 msgid "Outward" msgstr "Auswärts " #: xml_file_content.cpp:65 msgid "Cone Angle:" msgstr "Öffnungswinkel:" #: xml_file_content.cpp:66 msgid "Upward Bias:" msgstr "Aufwärtsdrift:" #: xml_file_content.cpp:67 msgid "Orientation:" msgstr "Orientierung:" #: xml_file_content.cpp:69 msgid "Aimed" msgstr "Ausgerichtet" #: xml_file_content.cpp:70 msgid "X" msgstr "X" #: xml_file_content.cpp:71 msgid "Y" msgstr "Y" #: xml_file_content.cpp:72 msgid "Z" msgstr "Z" #: xml_file_content.cpp:73 msgid "Trails:" msgstr "Spuren:" #: xml_file_content.cpp:74 msgid "Time:" msgstr "Zeit:" #: xml_file_content.cpp:75 msgid "Initial Angle:" msgstr "Anfangswinkel:" #: xml_file_content.cpp:76 msgid "Path" msgstr "Pfad" #: xml_file_content.cpp:77 msgid "Path Type:" msgstr "Pfad-Typ:" #: xml_file_content.cpp:78 msgid "Standard" msgstr "Standard" #: xml_file_content.cpp:79 msgid "Flies" msgstr "Fliegen" #: xml_file_content.cpp:80 msgid "Helix" msgstr "Helix" #: xml_file_content.cpp:81 msgid "Radial Speed:" msgstr "Radiale Geschwindigkeit:" #: xml_file_content.cpp:82 msgid "Axial Speed:" msgstr "Axiale Geschw.:" #: xml_file_content.cpp:83 msgid "Sphere Radius:" msgstr "Kugelradius:" #: xml_file_content.cpp:84 msgid "Cylinder Size X:" msgstr "Zylindergröße X:" #: xml_file_content.cpp:85 msgid "Cylinder Size Y:" msgstr "Zylindergröße Y:" #: xml_file_content.cpp:86 msgid "Cylinder Size Z:" msgstr "Zylindergröße Z:" #: xml_file_content.cpp:87 msgid "Note: changes will be written to the file ....." msgstr "Hinweis: alle Änderungen werden in den File ... geschrieben" #: xml_file_content.cpp:1 msgid "Create simple Patch Mesh" msgstr "Neuer einfacher Patch" #: xml_file_content.cpp:2 msgid "Width:" msgstr "Breite:" #: xml_file_content.cpp:3 msgid "Height:" msgstr "Höhe:" #: xml_file_content.cpp:4 msgid "Remove selected Brush" msgstr "Entferne die ausgewählte Brush" #: xml_file_content.cpp:1 msgid "Patch Control Vertices" msgstr "Patch-Kontrollpunkte" #: xml_file_content.cpp:2 msgid "Row:" msgstr "Reihe:" #: xml_file_content.cpp:3 msgid "Column:" msgstr "Spalte:" #: xml_file_content.cpp:4 msgid "Coordinates" msgstr "Koordinaten" #: xml_file_content.cpp:5 msgid "Patch Tesselation" msgstr "Patch-Tesselierung" #: xml_file_content.cpp:6 msgid "Fixed Subdivisions" msgstr "Fixe Tesselierung" #: xml_file_content.cpp:7 msgid "Horizontal:" msgstr "Horizontal:" #: xml_file_content.cpp:8 msgid "Vertical:" msgstr "Vertikal:" #: xml_file_content.cpp:1 msgid "Thicken selected Patches" msgstr "Ausgewählte Patches extrudieren" #: xml_file_content.cpp:2 msgid "Extrude along Vertex Normals" msgstr "Extrusion entlang der Vertex-Normalen" #: xml_file_content.cpp:3 msgid "Extrude along X-Axis" msgstr "Extrusion entlang der X-Achse" #: xml_file_content.cpp:4 msgid "Extrude along Y-Axis" msgstr "Extrusion entlang der Y-Achse" #: xml_file_content.cpp:5 msgid "Extrude along Z-Axis" msgstr "Extrusion entlang der Z-Achse" #: xml_file_content.cpp:6 msgid "Thickness (units):" msgstr "Tiefe (in Rastereinheiten):" #: xml_file_content.cpp:7 msgid "Create Seams (\"side walls\")" msgstr "Umschließen (erzeuge \"Seitenwände\")" #: xml_file_content.cpp:1 msgid "General Properties" msgstr "Allgemeine Eigenschaften" #: xml_file_content.cpp:2 msgid "Inventory Name:" msgstr "Name im Inventory:" #: xml_file_content.cpp:3 msgid "XData Name:" msgstr "XData-Name:" #: xml_file_content.cpp:4 msgid "Number of Pages:" msgstr "Anzahl der Seiten:" #: xml_file_content.cpp:5 msgid "Layout:" msgstr "Layout:" #: xml_file_content.cpp:6 msgid "One-sided" msgstr "Einseitig" #: xml_file_content.cpp:7 msgid "Two-sided" msgstr "Zweiseitig" #: xml_file_content.cpp:8 msgid "Pageturn Sound:" msgstr "Sound beim Umblättern:" #: xml_file_content.cpp:9 msgid "Page Editing" msgstr "Textbearbeitung" #: xml_file_content.cpp:10 msgid "Insert Page" msgstr "Seite einfügen" #: xml_file_content.cpp:11 msgid "Current Page:" msgstr "Aktuelle Seite:" #: xml_file_content.cpp:12 msgid "1" msgstr "1" #: xml_file_content.cpp:13 msgid "Remove Page" msgstr "Seite Entfernen" #: xml_file_content.cpp:14 msgid "GUI Definition:" msgstr "GUI-Definition:" #: xml_file_content.cpp:18 msgid "Body" msgstr "Text:" #: xml_file_content.cpp:19 msgid "Tools" msgstr "Werkzeuge" #: xml_file_content.cpp:22 msgid "Save and Close" msgstr "Speichern & Schließen" #: xml_file_content.cpp:2 msgid "pauseTimeButton" msgstr "pauseTimeButton" #: xml_file_content.cpp:4 msgid "prevButton" msgstr "prevButton" #: xml_file_content.cpp:5 msgid "nextButton" msgstr "nextButton" #: xml_file_content.cpp:6 msgid "texturedModeButton" msgstr "texturedModeButton" #: xml_file_content.cpp:7 msgid "lightingModeButton" msgstr "lightingModeButton" #: xml_file_content.cpp:8 msgid "gridButton" msgstr "Grid" #: xml_file_content.cpp:1 msgid "Type:" msgstr "Typ:" #: xml_file_content.cpp:3 #: xml_file_content.cpp:22 msgid "Chance:" msgstr "Wahrscheinlichkeit:" #: xml_file_content.cpp:4 msgid "Random Effects:" msgstr "Zufällige Effekte:" #: xml_file_content.cpp:5 msgid "Response Effects" msgstr "Response-Effekte" #: xml_file_content.cpp:3 msgid "Activation Timer:" msgstr "Aktivierungs-Timer:" #: xml_file_content.cpp:4 msgid "h" msgstr "h" #: xml_file_content.cpp:5 msgid "m" msgstr "m" #: xml_file_content.cpp:6 msgid "s" msgstr "s" #: xml_file_content.cpp:7 #: xml_file_content.cpp:13 #: xml_file_content.cpp:15 msgid "ms" msgstr "ms" #: xml_file_content.cpp:8 msgid "Timer restarts after firing" msgstr "Timer startet neu nach Ablauf" #: xml_file_content.cpp:9 msgid "Timer reloads" msgstr "Timer-Neustarts" #: xml_file_content.cpp:10 msgid "times" msgstr "Mal(e)" #: xml_file_content.cpp:11 msgid "Timer waits for start (when disabled: starts at spawn time)" msgstr "Timer wartet auf Start (wenn Option deaktiviert: Timer startet zur Spawn-Zeit)" #: xml_file_content.cpp:12 msgid "Time interval:" msgstr "Zeitintervall:" #: xml_file_content.cpp:14 msgid "Duration:" msgstr "Dauer:" #: xml_file_content.cpp:16 msgid "Radius" msgstr "Radius" #: xml_file_content.cpp:17 msgid "Use bounds" msgstr "Boundingbox benutzen" #: xml_file_content.cpp:18 msgid "Radius changes over time to:" msgstr "Radius verändert sich über die Zeit zu:" #: xml_file_content.cpp:19 msgid "Magnitude:" msgstr "Stärke:" #: xml_file_content.cpp:20 msgid "Falloff Exponent:" msgstr "Falloff-Exponent:" #: xml_file_content.cpp:21 msgid "Max Fire Count:" msgstr "Max. Auslösevorgänge:" #: xml_file_content.cpp:23 msgid "Velocity:" msgstr "Geschwindigkeit:" #: xml_file_content.cpp:24 msgid "Bounds:" msgstr "Boundingbox:" #: xml_file_content.cpp:25 msgid "Min:" msgstr "Min:" #: xml_file_content.cpp:26 msgid "Max:" msgstr "Max:" #~ msgid "Select a Game:" #~ msgstr "Spiel auswählen:" #~ msgid "The fs_game folder \"{0}\" does not exist.\n" #~ msgstr "Das fs_game Verzeichnis \"{0}\" existiert nicht.\n" #~ msgid "The fs_game_base folder \"{0}\" does not exist.\n" #~ msgstr "Das fs_game_base Verzeichnis \"{0}\" existiert nicht.\n" #~ msgid "" #~ "Note: You will have to restart DarkRadiant\n" #~ "for the changes to take effect." #~ msgstr "" #~ "Hinweis: Du musst DarkRadiant neu starten,\n" #~ "damit die Änderungen wirksam werden." #~ msgid "Select &Game..." #~ msgstr "Spiel auswählen..." #~ msgid "%s Settings" #~ msgstr "%s Einstellungen" #~ msgid "Vendor: %s" #~ msgstr "Hersteller: %s" #~ msgid "Version: %s" #~ msgstr "Version: %s" #~ msgid "Renderer: %s" #~ msgstr "Renderer: %s" #~ msgid "Entity %d" #~ msgstr "Entity %d" #~ msgid "%d of \"%s\"" #~ msgstr "%d \"%s\"" #~ msgid "%s AI of %s" #~ msgstr "%s AI von %s" #, fuzzy #~ msgid "capsule" #~ msgstr "Skalierung" #, fuzzy #~ msgid "bool" #~ msgstr "Werkzeug" #, fuzzy #~ msgid "Tuple[" #~ msgstr "Typ" #, fuzzy #~ msgid "*args" #~ msgstr "Flags" #, fuzzy #~ msgid "complex" #~ msgstr "Fertig" #, fuzzy #~ msgid "n" #~ msgstr "Nein" #, fuzzy #~ msgid "Set[" #~ msgstr "Auswählen" #, fuzzy #~ msgid "List[" #~ msgstr "Linien" #, fuzzy #~ msgid "Optional[" #~ msgstr "Optionen" #~ msgid "Could not remove the file: %s" #~ msgstr "Kann die Datei nicht entfernen: %s" #~ msgid "Page Editing:" #~ msgstr "Textbearbeitung" #~ msgid "General Properties:" #~ msgstr "Allgemeine Eigenschaften:" #~ msgid "Settings/Media Browser" #~ msgstr "Einstellungen/Mediabrowser" #~ msgid "Load media tree at startup" #~ msgstr "Lade Medienbaum während des Startvorgangs" #~ msgid "_Filters" #~ msgstr "_Filter" #~ msgid "Selected Brush" #~ msgstr "Ausgewählter Brush" #~ msgid "Cycle Cap Texture" #~ msgstr "Cap-Textur durchrotieren" #~ msgid "Save as Obj" #~ msgstr "Als OBJ speichern" #~ msgid "Loading models" #~ msgstr "Lade Modelle" #~ msgid "Searching" #~ msgstr "Durchsuchen" #~ msgid "Texture Thumbnail Scale" #~ msgstr "Skalierung der Texturvorschaubilder" #~ msgid "Free Model Rotation" #~ msgstr "Freie Modelldrehung" #~ msgid "Clamp texture thumbnails to constant size" #~ msgstr "Textur-Vorschaubilder mit fixer Größe anzeigen" #~ msgid "Nothing" #~ msgstr "Nichts" #~ msgid "Move View" #~ msgstr "Ansicht verschieben" #~ msgid "Toggle Selection" #~ msgstr "Auswählen/abwählen" #~ msgid "Toggle Face Selection" #~ msgstr "Face auswählen/abwählen" #~ msgid "Copy Texture" #~ msgstr "Textur kopieren" #~ msgid "PasteTexture Natural" #~ msgstr "Tetxur einfügen (Natürliche Skalierung)" #~ msgid "Paste Texture Name only" #~ msgstr "Nur Texturnamen einfügen" #~ msgid "Creating Preference Dialog" #~ msgstr "Erzeuge Einstellungsdialog" #~ msgid "Constructing Menu" #~ msgstr "Erzeuge Menü" #~ msgid "Initialising MediaBrowser" #~ msgstr "Initialisiere Medienbrowser" #~ msgid "Starting MainFrame" #~ msgstr "Starte Mainframe" #~ msgid "DarkRadiant Startup Complete" #~ msgstr "DarkRadiant Startphase abgeschlossen" #~ msgid "%d shaders found." #~ msgstr "%d Shader gefunden." #~ msgid "Settings/Source View" #~ msgstr "Einstellungen/Quellansicht" #~ msgid "%s" #~ msgstr "%s" #~ msgid "GTK+ Properties" #~ msgstr "GTK+ Eigenschaften" #~ msgid "gtkmm Version: %d.%d.%d" #~ msgstr "gtkmm Version: %d.%d.%d" #~ msgid "Light volume" #~ msgstr "Lichtvolumen" #~ msgid "Choose image" #~ msgstr "Bild auswählen" #~ msgid "Category" #~ msgstr "Kategorie" #~ msgid "Constant size" #~ msgstr "Fixe Größe" #~ msgid "Description:" #~ msgstr "Beschreibung:" #~ msgid "ID" #~ msgstr "ID" #~ msgid "Do you want to replace it?" #~ msgstr "Willst Du sie ersetzen?" #~ msgid "_File" #~ msgstr "_Datei" #~ msgid "_New Map" #~ msgstr "_Neue Map" #~ msgid "_Edit" #~ msgstr "_Bearbeiten" #~ msgid "_Duplicate" #~ msgstr "_Duplizieren" #~ msgid "D_elete" #~ msgstr "Lösch_en" #~ msgid "Select complete t_all" #~ msgstr "Inneres auswählen (Projektion)" #~ msgid "Select i_nside" #~ msgstr "Inneres auswählen" #~ msgid "Select _touching" #~ msgstr "Berührtes auswählen" #~ msgid "V_iew" #~ msgstr "Ans_icht" #~ msgid "_Surface Inspector" #~ msgstr "_Surface Inspector" #~ msgid "_Patch Inspector" #~ msgstr "_Patch Inspector" #~ msgid "_Texture Tool" #~ msgstr "_Textur-Werkzeug" #~ msgid "Show C_oordinates" #~ msgstr "Zeige Koordinaten" #~ msgid "_Faces" #~ msgstr "_Faces" #~ msgid "En_tities" #~ msgstr "En_tities" #~ msgid "M_ap" #~ msgstr "K_arte" #~ msgid "E_ntity" #~ msgstr "E_ntity" #~ msgid "Make _Room" #~ msgstr "Raum erstellen" #~ msgid "CSG _Subtract" #~ msgstr "CSG _Subtraktion" #~ msgid "CSG _Merge" #~ msgstr "CSG _Zusammenfügen" #~ msgid "_Patch" #~ msgstr "_Patch" #~ msgid "Populating MediaBrowser" #~ msgstr "Lade Medienbrowser" #~ msgid "Advanced" #~ msgstr "Erweitert" #~ msgid "Create MonsterClip Brush" #~ msgstr "Erzeuge Monsterclip-Brush" #~ msgid "Active materials" #~ msgstr "Aktive Shader" #~ msgid "Doom 3 map" #~ msgstr "Doom 3 Map" #~ msgid "Doom 3 region" #~ msgstr "Doom 3 Region" #~ msgid "Doom 3 prefab" #~ msgstr "Doom 3 Prefab" #~ msgid "Quake 4 map" #~ msgstr "Quake 4 Map" #~ msgid "Quake 4 region" #~ msgstr "Quake 4 Region" #~ msgid "Quake 4 prefab" #~ msgstr "Quake 4 Prefab" #~ msgid "Pause time" #~ msgstr "Zeit anhalten" #~ msgid "Stop time" #~ msgstr "Zeit stoppen" #~ msgid "Next frame" #~ msgstr "Nächster Frame" #~ msgid "Previous frame" #~ msgstr "Vorheriger Frame" #~ msgid "Reload Shaders" #~ msgstr "Shader neu laden" #~ msgid "Flush & Reload Shaders" #~ msgstr "Shader neu laden" #~ msgid "Start Render Time" #~ msgstr "Render-Zeit starten" #~ msgid "Stop Render Time" #~ msgstr "Render-Zeit stoppen" #~ msgid "Clip plane in" #~ msgstr "Entfernte Clip-Ebene heran" #~ msgid "Clip plane out" #~ msgstr "Entfernte Clip-Ebene hinaus" #~ msgid "Success logic" #~ msgstr "Logik für Erfüllung" #~ msgid "Edit objective conditions" #~ msgstr "Missionsziel-Bedingung bearbeiten" #~ msgid "Material to render onto each quad." #~ msgstr "Der Shader zum Rendern der Polygone dieses Partikels." #~ msgid "Render Colour" #~ msgstr "Renderfarbe" #~ msgid "Shader: " #~ msgstr "Shader:" #~ msgid "Either 0 0 0 0 for additive, or 1 1 1 0 for blended materials." #~ msgstr "Entweder 0 0 0 0 für additive, oder 1 1 1 0 für Blend-Shader." #~ msgid "Force colour from render entity (Fade Colour is still valid)" #~ msgstr "Farbe der Entity übernehmen (Die Fade-Farbe ist weiterhin gültig)" #~ msgid "In 0.0 to 1.0 range, causes later index smokes to be more faded." #~ msgstr "" #~ "Im Bereich 0.0 bis 1.0, bewirkt dass spätere Partikel-Polygone stärker " #~ "\"gefadet\" erscheinen." #~ msgid "If > 1, subdivide the texture S axis into frames and crossfade." #~ msgstr "" #~ "Wenn > 1, wird die Textur-Achse S je nach Anzahl der Frames zerteilt und " #~ "überblendet." #~ msgid "Frames per second" #~ msgstr "Frames pro Sekunde" #~ msgid "" #~ "Total number of particles, although some may be invisible at a given time." #~ msgstr "" #~ "Gesamtanzahl der Partikelpolygone, möglicherweise sind nicht alle zu " #~ "jedem Zeitpunkt sichtbar." #~ msgid "" #~ "Allows things to oneShot (1 cycle) or run for a set number of cycles on a " #~ "per stage basis." #~ msgstr "Gibt an wie oft die Stage abgespielt wird." #~ msgid "Time offset from system start for the first particle to spawn." #~ msgstr "" #~ "Zeitversatz, vom Start weg gerechnet, bevor das erste Polygon gerendert " #~ "wird." #~ msgid "" #~ "0.0 = all come out at first instant, 1.0 = evenly spaced over cycle time" #~ msgstr "" #~ "0.0 = alle Polygone werden auf einmal erzeugt, 1.0 = Polygone werden " #~ "zeitlich gleichmäßig über die Stage-Lebensdauer verteilt." #~ msgid "Time after particleLife before respawning." #~ msgstr "Pause nach einem Stage-Durchgang" #~ msgid "Values > 1 make the T axis longer." #~ msgstr "Werte > 1 verlängern die Textur-Achse T." #~ msgid "Can be negative to let particles float upwards." #~ msgstr "Kann negativ sein, um Polygone aufwärts driften zu lassen." #~ msgid "" #~ "Half the particles will have negative rotation speeds, this is measured " #~ "in degrees/sec." #~ msgstr "" #~ "Die halben Polygone drehen sich in negativer Drehrichtung, wird in Grad/" #~ "Sek.gemessen" #~ msgid "User tweak to fix poorly calculated bounds." #~ msgstr "User-Fix zur Behebung von schlecht berechneten Partikel-Bounds." #~ msgid "" #~ "Apply gravity in world space. If this flag is set use -z as gravity " #~ "direction as in the game world, otherwise use the reverse emitter " #~ "direction." #~ msgstr "" #~ "Wendet die Schwerkraft so an wie sie auch im Spiel wirkt. Wenn dieses " #~ "Flag gesetzt ist wird die Z-Achse des Partikelsystems nach unten zeigen, " #~ "ist es gelöscht wirkt es in der entgegengesetzten Richtung des Emitters." #~ msgid "" #~ "Offset from Origin to spawn all Particles, also applies to Custom Paths." #~ msgstr "" #~ "Abstand zur Entity, wird auch bei Speziellen Pfadoptionen angewandt. " #~ msgid "" #~ "Randomly orient the quad on emission. If random distribution is off, " #~ "particles get spawned at ." #~ msgstr "" #~ "Polygone werden mit zufälliger Orientierung erzeugt. Wenn diese " #~ "Einstellung abgeschaltet ist, erscheinen die Polygone bei ." #~ msgid "" #~ "Some tests showed that for the cylinder type the fourth parameter " #~ "(\"ringfraction\") is only effective if >1, it effectively scales the " #~ "elliptic shape by that factor.\n" #~ "Values < 1.0 didn't have any effect." #~ msgstr "" #~ "Tests haben gezeigt dass der 4. Parameter der \"Zylinder\"-Einstellung " #~ "(\"Ring Fraction\") nur bei Werten > 1 etwas Sichtbares bewirkt: die " #~ "elliptische Form wird dann um diesen Faktor skaliert.\n" #~ "Werte < 1 haben keinen sichtbaren Effekt." #~ msgid "In degrees, random angle is used if zero (default)." #~ msgstr "" #~ "Wird in Grad gemessen, bei einem Wert von 0 werden die Polygone zufällig " #~ "gedreht." #~ msgid "" #~ "\"Flies\" particles are moving on the surface of a sphere of the given " #~ "radius.\n" #~ "The radial and axial speeds are chosen at random (but never 0) and are " #~ "constant during the lifetime of a particle.\n" #~ "Starting position appears to be random, but different to the " #~ "\"distribution sphere\" type (i.e. it is not evenly distributed, instead " #~ "the particles seem to bunch themselves at the poles)." #~ msgstr "" #~ "\"Fliegen\"-Partikel bewegen sich auf einer Kugel mit dem angegebenen " #~ "Radius.\n" #~ "Die radialen und axialen Geschwindigkeiten werden zufällig gewählt (sind " #~ "aber immer ungleich 0) und bleiben konstant während der Lebensdauer eines " #~ "Polygons.\n" #~ "Die Startposition scheint zufällig gewählt zu werden, ist aber (im " #~ "Gegensatz zur Verteilung vom Typ \"Kugel\") nicht gleichmäßig über die " #~ "Kugeloberfläche verteilt (leichte Häufung an den Polen)." #~ msgid "" #~ "Helical movement is describing an elliptic cylinder, its shape is " #~ "determined by sizeX, sizeY and sizeZ.\n" #~ "Particles are spawned randomly on that cylinder surface, their velocities " #~ "(radial and axial) are also random (both negative and positive velocities " #~ "are allowed)." #~ msgstr "" #~ "Die helikale Bewegung beschreibt einen elliptischen Zylinder, die Form " #~ "wird durch die drei Größenparameter X, Y und Z bestimmt.\n" #~ "Die Polygone werden an einem zufälligen Ort auf der Zylinderoberfläche " #~ "erzeugt, ihre Geschwindigkeiten (sowohl radial als auch axial) sind " #~ "ebenfalls zufällig, positiv wie negativ." #~ msgid "" #~ "Error while creating backup:\n" #~ "%s" #~ msgstr "" #~ "Fehler beim Erstellen des Backups:\n" #~ "%s" DarkRadiant-2.5.0/install/input.xml000066400000000000000000000300361321750546400172420ustar00rootroot00000000000000 DarkRadiant-2.5.0/install/menu.xml000066400000000000000000000446351321750546400170610ustar00rootroot00000000000000 DarkRadiant-2.5.0/install/scripts/000077500000000000000000000000001321750546400170465ustar00rootroot00000000000000DarkRadiant-2.5.0/install/scripts/brushtest.py000066400000000000000000000013101321750546400214360ustar00rootroot00000000000000# Some interface tests # Test brush manipulation class BrushManipulator(SceneNodeVisitor) : def pre(self, node): # Try to get a brush from this node brush = node.getBrush() if not brush.isNull(): print('Brush information:') print('Number of faces: ' + str(brush.getNumFaces())) i = 0 while i < brush.getNumFaces(): face = brush.getFace(i) print('Face #' + str(i) +' has shader ' + face.getShader()) winding = face.getWinding() for w in winding: print('w.vertex=' + str(w.vertex.x()) + ',' + str(w.vertex.y()) + ',' + str(w.vertex.z())) face.fitTexture(1,1) i += 1 return 1 walker = BrushManipulator() GlobalSceneGraph.root().traverse(walker) print('') DarkRadiant-2.5.0/install/scripts/commands/000077500000000000000000000000001321750546400206475ustar00rootroot00000000000000DarkRadiant-2.5.0/install/scripts/commands/ase_export.py000066400000000000000000000364721321750546400234060ustar00rootroot00000000000000# ***** BEGIN GPL LICENSE BLOCK ***** # # 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 2 # 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, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** #TODO: # Set the command name so that DarkRadiant recognises this file __commandName__ = 'aseExport' __commandDisplayName__ = 'Export ASE...' # The actual algorithm called by DarkRadiant is contained in the execute() function def execute(): script = "Dark Radiant ASCII Scene Export (*.ase)" author = "Richard Bartlett, some additions by greebo and tels" version = "0.9" import darkradiant as dr # Check if we have a valid selection selectionInfo = GlobalSelectionSystem.getSelectionInfo() # Don't allow empty selections or selected components only if selectionInfo.totalCount == 0 or selectionInfo.totalCount == selectionInfo.componentCount: errMsg = GlobalDialogManager.createMessageBox('No selection', 'Nothing selected, cannot run exporter.', dr.Dialog.ERROR) errMsg.run() return shaderlist = [] geomlist = [] # simple linear triangulation of n-sided poly def triangulate(pointset): tris = [] for count in range(1, len(pointset) - 1): tris.append([pointset[0], pointset[count], pointset[count + 1]]) return tris # skin patch matrix with tris def skinmatrix(pointset, width, height): tris = [] for h in range(height-1): for w in range(width-1): tris.append([pointset[w+(h*width)], pointset[w+1+(h*width)], pointset[w+width+(h*width)]]) tris.append([pointset[w+1+(h*width)], pointset[w+1+width+(h*width)], pointset[w+width+(h*width)]]) return tris def processBrush(brushnode): verts = [] faces = [] numfaces = brushnode.getNumFaces() for index in range(numfaces): facenode = brushnode.getFace(index) shader = facenode.getShader() # Tels: skip if caulk and no caulk should be exported if (shader == 'textures/common/caulk') and (int(GlobalRegistry.get('user/scripts/aseExport/exportcaulk'))) == 0: continue if not shader in shaderlist: shaderlist.append(shader) winding = facenode.getWinding() tris = triangulate([x+len(verts) for x in range(len(winding))]) for x in tris: x.append(shaderlist.index(shader)) faces.append(x) for x in reversed(winding): verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()]) geomlist.append([verts, faces]) return def processPatch(patchnode): verts = [] faces = [] shader = patchnode.getShader() # Tels: skip if caulk and no caulk should be exported if shader == 'textures/common/caulk' and int(GlobalRegistry.get('user/scripts/aseExport/exportcaulk')) == 0: return if not shader in shaderlist: shaderlist.append(shader) mesh = patchnode.getTesselatedPatchMesh() for x in mesh.vertices: verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()]) tris = skinmatrix([x for x in range(len(verts))], mesh.width, mesh.height) for x in tris: x.append(shaderlist.index(shader)) faces.append(x) geomlist.append([verts, faces]) return # Branch on primitive nodes def processPrimitive(scenenode): if scenenode.isBrush(): processBrush(scenenode.getBrush()) elif scenenode.isPatch(): processPatch(scenenode.getPatch()) return # Traversor class to visit child primitives of entities class nodeVisitor(dr.SceneNodeVisitor): def pre(self, scenenode): # Brush? if scenenode.isBrush(): processBrush(scenenode.getBrush()) # Patch? elif scenenode.isPatch(): processPatch(scenenode.getPatch()) # Traverse all child nodes, regardless of type return 1 class dataCollector(dr.SelectionVisitor): def visit(self, scenenode): if scenenode.isBrush() or scenenode.isPatch(): processPrimitive(scenenode) elif scenenode.isEntity(): # greebo: Found an entity, this could be a func_static or similar # Traverse children of this entity using a new walker nodewalker = nodeVisitor() scenenode.traverse(nodewalker) else: print('WARNING: unsupported node type selected. Skipping: ' + scenenode.getNodeType()) # Dialog dialog = GlobalDialogManager.createDialog(script + 'v' + version) # Add an entry box and remember the handle fileHandle = dialog.addEntryBox("Filename:") dialog.setElementValue(fileHandle, GlobalRegistry.get('user/scripts/aseExport/recentFilename')) # Add an entry box and remember the handle pathHandle = dialog.addPathEntry("Save path:", True) dialog.setElementValue(pathHandle, GlobalRegistry.get('user/scripts/aseExport/recentPath')) # Add a checkbox centerObjectsHandle = dialog.addCheckbox("Center objects at 0,0,0 origin") dialog.setElementValue(centerObjectsHandle, GlobalRegistry.get('user/scripts/aseExport/centerObjects')) # Add another checkbox exportCaulkHandle = dialog.addCheckbox("Export caulked faces") dialog.setElementValue(exportCaulkHandle, GlobalRegistry.get('user/scripts/aseExport/exportcaulk')) if dialog.run() == dr.Dialog.OK: fullpath = dialog.getElementValue(pathHandle) + '/' + dialog.getElementValue(fileHandle) if not fullpath.endswith('.ase'): fullpath = fullpath + '.ase' # Save the path for later use GlobalRegistry.set('user/scripts/aseExport/recentFilename', dialog.getElementValue(fileHandle)) GlobalRegistry.set('user/scripts/aseExport/recentPath', dialog.getElementValue(pathHandle)) GlobalRegistry.set('user/scripts/aseExport/centerObjects', dialog.getElementValue(centerObjectsHandle)) GlobalRegistry.set('user/scripts/aseExport/exportcaulk', dialog.getElementValue(exportCaulkHandle)) try: file = open(fullpath, 'r') file.close() prompt = GlobalDialogManager.createMessageBox('Warning', 'The file ' + fullpath + ' already exists. Do you wish to overwrite it?', dr.Dialog.ASK) if prompt.run() == dr.Dialog.YES: overwrite = True else: overwrite = False except IOError: overwrite = True if overwrite: # Tels: Only collect the data if we are going to export it walker = dataCollector() GlobalSelectionSystem.foreachSelected(walker) # greebo: Check if we should center objects at the 0,0,0 origin if int(dialog.getElementValue(centerObjectsHandle)) == 1: #center objects at 0,0,0 xlist = [] ylist = [] zlist = [] for item in geomlist: for vert in item[0]: xlist.append(vert[0]) ylist.append(vert[1]) zlist.append(vert[2]) xcenter=(max(xlist)+min(xlist))/2 ycenter=(max(ylist)+min(ylist))/2 zcenter=(max(zlist)+min(zlist))/2 for item in geomlist: for vert in item[0]: vert[0] = vert[0] - xcenter vert[1] = vert[1] - ycenter vert[2] = vert[2] - zcenter # split objects that do not share the same texture on all faces newgeomlist = [] for x in geomlist: texlist = [] for data in x[1]: texlist.append(data[3]) if len(set(texlist)) > 1: temp = [] for texture in set(texlist): vertlist = [] facelist = [] for data in x[1]: if data[3] == texture: facelist.append(data) usedverts = [] for face in facelist: usedverts.extend(face[0:3]) usedverts = list(set(usedverts)) for index in usedverts: vertlist.append(x[0][index]) newfacelist = [] for face in facelist: newfacelist.append([vertlist.index(x[0][face[0]]),vertlist.index(x[0][face[1]]),vertlist.index(x[0][face[2]]),face[3]]) newgeomlist.append([vertlist, newfacelist]) #del geomlist[geomlist.index(x)] #geomlist.extend(temp) #newgeomlist.append(temp) else: newgeomlist.append(x) geomlist = newgeomlist scene = '''\t*SCENE_FILENAME "{0}" \t*SCENE_FIRSTFRAME 0 \t*SCENE_LASTFRAME 100 \t*SCENE_FRAMESPEED 30 \t*SCENE_TICKSPERFRAME 160 \t*SCENE_BACKGROUND_STATIC 0.0000\t0.0000\t0.0000 \t*SCENE_AMBIENT_STATIC 0.0000\t0.0000\t0.0000'''.format(GlobalMap.getMapName()) materials = str() for x in shaderlist: materials = materials + '''\t*MATERIAL {0} {{ \t\t*MATERIAL_NAME "{1}" \t\t*MATERIAL_CLASS "Standard" \t\t*MATERIAL_AMBIENT 0.5882\t0.5882\t0.5882 \t\t*MATERIAL_DIFFUSE 0.5882\t0.5882\t0.5882 \t\t*MATERIAL_SPECULAR 0.9000\t0.9000\t0.9000 \t\t*MATERIAL_SHINE 0.1000 \t\t*MATERIAL_SHINESTRENGTH 0.0000 \t\t*MATERIAL_TRANSPARENCY 0.0000 \t\t*MATERIAL_WIRESIZE 1.0000 \t\t*MATERIAL_SHADING Blinn \t\t*MATERIAL_XP_FALLOFF 0.0000 \t\t*MATERIAL_SELFILLUM 0.0000 \t\t*MATERIAL_FALLOFF In \t\t*MATERIAL_XP_TYPE Filter \t\t*MAP_DIFFUSE {{ \t\t\t*MAP_NAME "{2}" \t\t\t*MAP_CLASS "Bitmap" \t\t\t*MAP_SUBNO 1 \t\t\t*MAP_AMOUNT 1.0000 \t\t\t*BITMAP "\\\\purgatory\\purgatory\\doom\\base\{2}" \t\t\t*MAP_TYPE Screen \t\t\t*UVW_U_OFFSET 0.0000 \t\t\t*UVW_V_OFFSET 0.0000 \t\t\t*UVW_U_TILING 1.0000 \t\t\t*UVW_V_TILING 1.0000 \t\t\t*UVW_ANGLE 0.0000 \t\t\t*UVW_BLUR 1.0000 \t\t\t*UVW_BLUR_OFFSET 0.0000 \t\t\t*UVW_NOUSE_AMT 1.0000 \t\t\t*UVW_NOISE_SIZE 1.0000 \t\t\t*UVW_NOISE_LEVEL 1 \t\t\t*UVW_NOISE_PHASE 0.0000 \t\t\t*BITMAP_FILTER Pyramidal \t\t}} \t}} '''.format(shaderlist.index(x), x, x.replace('/','\\')) geomobjects = str() for x in geomlist: # x[0] = vertices # vert[0] - vert[2] = vertex coords # vert[3] - vert[4] = texture coords # vert[5] - vert[7] = normal # x[1] = faces vertlist = str() for count, data in enumerate(x[0]): vertlist = vertlist + '''\t\t\t*MESH_VERTEX {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, data[0], data[1], data[2]) facelist = str() for count, data in enumerate(x[1]): facelist = facelist + '''\t\t\t*MESH_FACE {0}: A: {1} B: {2} C: {3} AB: 0 BC: 0 CA: 0\t *MESH_SMOOTHING 1 \t*MESH_MTLID {4}\n'''.format(count, data[0], data[1], data[2], data[3]) tvertlist = str() for count, data in enumerate(x[0]): tvertlist = tvertlist + '''\t\t\t*MESH_TVERT {0}\t{1: 10.4f}\t{2: 10.4f}\t0.0000\n'''.format(count, data[3], data[4]) tfacelist = str() for count, data in enumerate(x[1]): tfacelist = tfacelist + '''\t\t\t*MESH_TFACE {0}\t{1}\t{2}\t{3}\n'''.format(count, data[0], data[1], data[2]) cfacelist = str() for count, data in enumerate(x[1]): cfacelist = cfacelist + '''\t\t\t*MESH_CFACE {0}\t0\t0\t0\n'''.format(count) normals = str() for count, data in enumerate(x[1]): normals += '''\t\t\t*MESH_FACENORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7]) # greebo: use first vertex normal as face normal normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[0], x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7]) normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[1], x[0][data[1]][5], x[0][data[1]][6], x[0][data[1]][7]) normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[2], x[0][data[2]][5], x[0][data[2]][6], x[0][data[2]][7]) if len(x[1]) == 0: continue geomobjects = geomobjects + '''*GEOMOBJECT {{ \t*NODE_NAME "{0}" \t*NODE_TM {{ \t\t*NODE_NAME "{0}" \t\t*INHERIT_POS 0 0 0 \t\t*INHERIT_ROT 0 0 0 \t\t*INHERIT_SCL 0 0 0 \t\t*TM_ROW0 1.0000\t0.0000\t0.0000 \t\t*TM_ROW1 0.0000\t1.0000\t0.0000 \t\t*TM_ROW2 0.0000\t0.0000\t1.0000 \t\t*TM_ROW3 0.0000\t0.0000\t0.0000 \t\t*TM_POS 0.0000\t0.0000\t0.0000 \t\t*TM_ROTAXIS 0.0000\t0.0000\t0.0000 \t\t*TM_ROTANGLE 0.0000 \t\t*TM_SCALE 1.0000\t1.0000\t1.0000 \t\t*TM_SCALEAXIS 0.0000\t0.0000\t0.0000 \t\t*TM_SCALEAXISANG 0.0000 \t}} \t*MESH {{ \t\t*TIMEVALUE 0 \t\t*MESH_NUMVERTEX {1} \t\t*MESH_NUMFACES {2} \t\t*MESH_VERTEX_LIST {{ {3}\t\t}} \t\t*MESH_FACE_LIST {{ {4}\t\t}} \t\t*MESH_NUMTVERTEX {5} \t\t*MESH_TVERTLIST {{ {6}\t\t}} \t\t*MESH_NUMTVFACES {7} \t\t*MESH_TFACELIST {{ {8}\t\t}} \t\t*MESH_NUMCVERTEX 1 \t\t*MESH_CVERTLIST {{ \t\t\t*MESH_VERTCOL 0\t1.0000\t1.0000\t1.0000 \t\t}} \t\t*MESH_NUMCVFACES {9} \t\t*MESH_CFACELIST {{ {10}\t\t}} \t\t*MESH_NORMALS {{ {11}\t\t}} \t}} \t*PROP_MOTIONBLUR 0 \t*PROP_CASTSHADOW 1 \t*PROP_RECVSHADOW 1 \t*MATERIAL_REF {12} }}\n'''.format('mesh' + str(geomlist.index(x)), \ len(x[0]), \ len(x[1]), \ vertlist, \ facelist, \ len(x[0]), \ tvertlist, \ len(x[1]), \ tfacelist, \ len(x[1]), \ cfacelist, \ normals, \ x[1][0][3]) # material reference from first face data = '''*3DSMAX_ASCIIEXPORT\t200 *COMMENT "{0} v{1}" *SCENE {{ {2} }} *MATERIAL_LIST {{ \t*MATERIAL_COUNT {3} {4}}} {5}'''.format(script, version, scene, len(shaderlist), materials, geomobjects) # Write the compiled data to the output file file = open(fullpath, 'w') file.write(data) file.close() # __executeCommand__ evaluates to true after DarkRadiant has successfully initialised if __executeCommand__: execute() DarkRadiant-2.5.0/install/scripts/commands/ase_export_blend.py000066400000000000000000000433411321750546400245430ustar00rootroot00000000000000# ***** BEGIN GPL LICENSE BLOCK ***** # # 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 2 # 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, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** #TODO: # Set the command name so that DarkRadiant recognises this file __commandName__ = 'aseExportBlend' __commandDisplayName__ = 'Export blended ASE...' # The actual algorithm called by DarkRadiant is contained in the execute() function def execute(): script = "Dark Radiant ASCII Scene Export (*.ase)" author = "Richard Bartlett, some additions by greebo and tels" version = "0.71" import darkradiant as dr # Check if we have a valid selection #import time selectionInfo = GlobalSelectionSystem.getSelectionInfo() # Don't allow empty selections or selected components only if selectionInfo.totalCount == 0 or selectionInfo.totalCount == selectionInfo.componentCount: errMsg = GlobalDialogManager.createMessageBox('No selection', 'Nothing selected, cannot run exporter.', dr.Dialog.ERROR) errMsg.run() return shaderlist = [] geomlist = [] blendlist = [] xcenter = 0 ycenter = 0 zcenter = 0 def randfloat(a,b): c=time.clock() return a+(b-a)*((c*1000000)%1) # used for getting the blend height def getBlendHeight(x,p,size): min = 0 max = 0 for i in range(size-1): y = x[i] y[2] if y[2]max: max=y[2] return max*float(p)+min*(1-float(p)) # method checking whether a point is inside an aabb def isInside(x,ab): origin = ab.origin ext = ab.extents min = origin - ext max = origin + ext if (x[0]max.x()-xcenter): return 0 if (x[1]max.y()-ycenter): return 0 if (x[2]max.z()-zcenter): return 0 return 1 # which texture to choose def getColor(x): for bl in blendlist: if (isInside(x,bl)): return 1 return 0 # simple linear triangulation of n-sided poly def triangulate(pointset): tris = [] for count in range(1, len(pointset) - 1): tris.append([pointset[0], pointset[count], pointset[count + 1]]) return tris # skin patch matrix with tris def skinmatrix(pointset, width, height): tris = [] for h in range(height-1): for w in range(width-1): tris.append([pointset[w+(h*width)], pointset[w+1+(h*width)], pointset[w+width+(h*width)]]) tris.append([pointset[w+1+(h*width)], pointset[w+1+width+(h*width)], pointset[w+width+(h*width)]]) return tris # Obsttorte: Method to get the AABB of this Brush def processBlendBrush(brushnode): #ab = AABB(brushnode) origin = brushnode.getFace(0).getWinding()[0].vertex ab = AABB(origin,origin-origin) numfaces = brushnode.getNumFaces() for index in range(numfaces): facenode = brushnode.getFace(index) winding = facenode.getWinding() for x in winding: ab.includePoint(x.vertex) #print(ab.getRadius()) blendlist.append(ab) def processBrush(brushnode): verts = [] faces = [] # Obsttorte: if blend, process seperately if (brushnode.hasShader('textures/common/blend')): processBlendBrush(brushnode) return numfaces = brushnode.getNumFaces() for index in range(numfaces): facenode = brushnode.getFace(index) shader = facenode.getShader() # Tels: skip if caulk and no caulk should be exported if (shader == 'textures/common/caulk') and (int(GlobalRegistry.get('user/scripts/aseExport/exportcaulk'))) == 0: continue if not shader in shaderlist: shaderlist.append(shader) winding = facenode.getWinding() tris = triangulate([x+len(verts) for x in range(len(winding))]) for x in tris: x.append(shaderlist.index(shader)) faces.append(x) for x in reversed(winding): verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()]) geomlist.append([verts, faces]) return def processPatch(patchnode): verts = [] faces = [] shader = patchnode.getShader() # Tels: skip if caulk and no caulk should be exported if shader == 'textures/common/caulk' and int(GlobalRegistry.get('user/scripts/aseExport/exportcaulk')) == 0: return if not shader in shaderlist: shaderlist.append(shader) mesh = patchnode.getTesselatedPatchMesh() for x in mesh.vertices: verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()]) tris = skinmatrix([x for x in range(len(verts))], mesh.width, mesh.height) for x in tris: x.append(shaderlist.index(shader)) faces.append(x) geomlist.append([verts, faces]) return # Branch on primitive nodes def processPrimitive(scenenode): if scenenode.isBrush(): processBrush(scenenode.getBrush()) elif scenenode.isPatch(): processPatch(scenenode.getPatch()) return # Traversor class to visit child primitives of entities class nodeVisitor(dr.SceneNodeVisitor): def pre(self, scenenode): # Brush? if scenenode.isBrush(): processBrush(scenenode.getBrush()) # Patch? elif scenenode.isPatch(): processPatch(scenenode.getPatch()) # Traverse all child nodes, regardless of type return 1 class dataCollector(dr.SelectionVisitor): def visit(self, scenenode): if scenenode.isBrush() or scenenode.isPatch(): processPrimitive(scenenode) elif scenenode.isEntity(): # greebo: Found an entity, this could be a func_static or similar # Traverse children of this entity using a new walker nodewalker = nodeVisitor() scenenode.traverse(nodewalker) else: print('WARNING: unsupported node type selected. Skipping: ' + scenenode.getNodeType()) # Dialog dialog = GlobalDialogManager.createDialog(script + 'v' + version) # Add an entry box and remember the handle fileHandle = dialog.addEntryBox("Filename:") dialog.setElementValue(fileHandle, GlobalRegistry.get('user/scripts/aseExport/recentFilename')) # Add an entry box and remember the handle pathHandle = dialog.addPathEntry("Save path:", True) dialog.setElementValue(pathHandle, GlobalRegistry.get('user/scripts/aseExport/recentPath')) # Add a checkbox centerObjectsHandle = dialog.addCheckbox("Center objects at 0,0,0 origin") dialog.setElementValue(centerObjectsHandle, GlobalRegistry.get('user/scripts/aseExport/centerObjects')) # Add another checkbox exportCaulkHandle = dialog.addCheckbox("Export caulked faces") dialog.setElementValue(exportCaulkHandle, GlobalRegistry.get('user/scripts/aseExport/exportcaulk')) # Add a Spin Button for blending and one for random height #blendHeightHandle = dialog.addSpinButton("Level of blending in percent",0,1,0.05,2) #blendRandomnessHandle = dialog.addSpinButton("Randomness",0,1,0.05,2) if dialog.run() == dr.Dialog.OK: fullpath = dialog.getElementValue(pathHandle) + '/' + dialog.getElementValue(fileHandle) if not fullpath.endswith('.ase'): fullpath = fullpath + '.ase' # Save the path for later use GlobalRegistry.set('user/scripts/aseExport/recentFilename', dialog.getElementValue(fileHandle)) GlobalRegistry.set('user/scripts/aseExport/recentPath', dialog.getElementValue(pathHandle)) GlobalRegistry.set('user/scripts/aseExport/centerObjects', dialog.getElementValue(centerObjectsHandle)) GlobalRegistry.set('user/scripts/aseExport/exportcaulk', dialog.getElementValue(exportCaulkHandle)) try: file = open(fullpath, 'r') file.close() prompt = GlobalDialogManager.createMessageBox('Warning', 'The file ' + fullpath + ' already exists. Do you wish to overwrite it?', dr.Dialog.ASK) if prompt.run() == dr.Dialog.YES: overwrite = True else: overwrite = False except IOError: overwrite = True if overwrite: # Tels: Only collect the data if we are going to export it walker = dataCollector() GlobalSelectionSystem.foreachSelected(walker) # greebo: Check if we should center objects at the 0,0,0 origin if int(dialog.getElementValue(centerObjectsHandle)) == 1: #center objects at 0,0,0 xlist = [] ylist = [] zlist = [] for item in geomlist: for vert in item[0]: xlist.append(vert[0]) ylist.append(vert[1]) zlist.append(vert[2]) xcenter=(max(xlist)+min(xlist))/2 ycenter=(max(ylist)+min(ylist))/2 zcenter=(max(zlist)+min(zlist))/2 for item in geomlist: for vert in item[0]: vert[0] = vert[0] - xcenter vert[1] = vert[1] - ycenter vert[2] = vert[2] - zcenter # split objects that do not share the same texture on all faces newgeomlist = [] for x in geomlist: texlist = [] for data in x[1]: texlist.append(data[3]) if len(set(texlist)) > 1: temp = [] for texture in set(texlist): vertlist = [] facelist = [] for data in x[1]: if data[3] == texture: facelist.append(data) usedverts = [] for face in facelist: usedverts.extend(face[0:3]) usedverts = list(set(usedverts)) for index in usedverts: vertlist.append(x[0][index]) newfacelist = [] for face in facelist: newfacelist.append([vertlist.index(x[0][face[0]]),vertlist.index(x[0][face[1]]),vertlist.index(x[0][face[2]]),face[3]]) newgeomlist.append([vertlist, newfacelist]) #del geomlist[geomlist.index(x)] #geomlist.extend(temp) #newgeomlist.append(temp) else: newgeomlist.append(x) geomlist = newgeomlist scene = '''\t*SCENE_FILENAME "{0}" \t*SCENE_FIRSTFRAME 0 \t*SCENE_LASTFRAME 100 \t*SCENE_FRAMESPEED 30 \t*SCENE_TICKSPERFRAME 160 \t*SCENE_BACKGROUND_STATIC 0.0000\t0.0000\t0.0000 \t*SCENE_AMBIENT_STATIC 0.0000\t0.0000\t0.0000'''.format(GlobalMap.getMapName()) materials = str() for x in shaderlist: materials = materials + '''\t*MATERIAL {0} {{ \t\t*MATERIAL_NAME "{1}" \t\t*MATERIAL_CLASS "Standard" \t\t*MATERIAL_AMBIENT 0.5882\t0.5882\t0.5882 \t\t*MATERIAL_DIFFUSE 0.5882\t0.5882\t0.5882 \t\t*MATERIAL_SPECULAR 0.9000\t0.9000\t0.9000 \t\t*MATERIAL_SHINE 0.1000 \t\t*MATERIAL_SHINESTRENGTH 0.0000 \t\t*MATERIAL_TRANSPARENCY 0.0000 \t\t*MATERIAL_WIRESIZE 1.0000 \t\t*MATERIAL_SHADING Blinn \t\t*MATERIAL_XP_FALLOFF 0.0000 \t\t*MATERIAL_SELFILLUM 0.0000 \t\t*MATERIAL_FALLOFF In \t\t*MATERIAL_XP_TYPE Filter \t\t*MAP_DIFFUSE {{ \t\t\t*MAP_NAME "{2}" \t\t\t*MAP_CLASS "Bitmap" \t\t\t*MAP_SUBNO 1 \t\t\t*MAP_AMOUNT 1.0000 \t\t\t*BITMAP "\\\\purgatory\\purgatory\\doom\\base\{2}" \t\t\t*MAP_TYPE Screen \t\t\t*UVW_U_OFFSET 0.0000 \t\t\t*UVW_V_OFFSET 0.0000 \t\t\t*UVW_U_TILING 1.0000 \t\t\t*UVW_V_TILING 1.0000 \t\t\t*UVW_ANGLE 0.0000 \t\t\t*UVW_BLUR 1.0000 \t\t\t*UVW_BLUR_OFFSET 0.0000 \t\t\t*UVW_NOUSE_AMT 1.0000 \t\t\t*UVW_NOISE_SIZE 1.0000 \t\t\t*UVW_NOISE_LEVEL 1 \t\t\t*UVW_NOISE_PHASE 0.0000 \t\t\t*BITMAP_FILTER Pyramidal \t\t}} \t}} '''.format(shaderlist.index(x), x, x.replace('/','\\')) geomobjects = str() for x in geomlist: # x[0] = vertices # vert[0] - vert[2] = vertex coords # vert[3] - vert[4] = texture coords # vert[5] - vert[7] = normal # x[1] = faces vertlist = str() #bh = getBlendHeight(x[0],dialog.getElementValue(blendHeightHandle),len(x[0])) clist = [] for data in x[0]: clist.append(getColor(data)) for count, data in enumerate(x[0]): vertlist = vertlist + '''\t\t\t*MESH_VERTEX {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, data[0], data[1], data[2]) facelist = str() for count, data in enumerate(x[1]): facelist = facelist + '''\t\t\t*MESH_FACE {0}: A: {1} B: {2} C: {3} AB: 0 BC: 0 CA: 0\t *MESH_SMOOTHING 1 \t*MESH_MTLID {4}\n'''.format(count, data[0], data[1], data[2], data[3]) tvertlist = str() for count, data in enumerate(x[0]): tvertlist = tvertlist + '''\t\t\t*MESH_TVERT {0}\t{1: 10.4f}\t{2: 10.4f}\t0.0000\n'''.format(count, data[3], data[4]) tfacelist = str() for count, data in enumerate(x[1]): tfacelist = tfacelist + '''\t\t\t*MESH_TFACE {0}\t{1}\t{2}\t{3}\n'''.format(count, data[0], data[1], data[2]) cfacelist = str() for count, data in enumerate(x[1]): cfacelist = cfacelist + '''\t\t\t*MESH_CFACE {0}\t{1}\t{2}\t{3}\n'''.format(count,clist[data[0]],clist[data[1]],clist[data[2]]) normals = str() for count, data in enumerate(x[1]): normals += '''\t\t\t*MESH_FACENORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7]) # greebo: use first vertex normal as face normal normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[0], x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7]) normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[1], x[0][data[1]][5], x[0][data[1]][6], x[0][data[1]][7]) normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[2], x[0][data[2]][5], x[0][data[2]][6], x[0][data[2]][7]) if len(x[1]) == 0: continue geomobjects = geomobjects + '''*GEOMOBJECT {{ \t*NODE_NAME "{0}" \t*NODE_TM {{ \t\t*NODE_NAME "{0}" \t\t*INHERIT_POS 0 0 0 \t\t*INHERIT_ROT 0 0 0 \t\t*INHERIT_SCL 0 0 0 \t\t*TM_ROW0 1.0000\t0.0000\t0.0000 \t\t*TM_ROW1 0.0000\t1.0000\t0.0000 \t\t*TM_ROW2 0.0000\t0.0000\t1.0000 \t\t*TM_ROW3 0.0000\t0.0000\t0.0000 \t\t*TM_POS 0.0000\t0.0000\t0.0000 \t\t*TM_ROTAXIS 0.0000\t0.0000\t0.0000 \t\t*TM_ROTANGLE 0.0000 \t\t*TM_SCALE 1.0000\t1.0000\t1.0000 \t\t*TM_SCALEAXIS 0.0000\t0.0000\t0.0000 \t\t*TM_SCALEAXISANG 0.0000 \t}} \t*MESH {{ \t\t*TIMEVALUE 0 \t\t*MESH_NUMVERTEX {1} \t\t*MESH_NUMFACES {2} \t\t*MESH_VERTEX_LIST {{ {3}\t\t}} \t\t*MESH_FACE_LIST {{ {4}\t\t}} \t\t*MESH_NUMTVERTEX {5} \t\t*MESH_TVERTLIST {{ {6}\t\t}} \t\t*MESH_NUMTVFACES {7} \t\t*MESH_TFACELIST {{ {8}\t\t}} \t\t*MESH_NUMCVERTEX 2 \t\t*MESH_CVERTLIST {{ \t\t\t*MESH_VERTCOL 0\t1.0000\t1.0000\t1.0000 \t\t\t*MESH_VERTCOL 1\t0.0000\t0.0000\t0.0000 \t\t}} \t\t*MESH_NUMCVFACES {9} \t\t*MESH_CFACELIST {{ {10}\t\t}} \t\t*MESH_NORMALS {{ {11}\t\t}} \t}} \t*PROP_MOTIONBLUR 0 \t*PROP_CASTSHADOW 1 \t*PROP_RECVSHADOW 1 \t*MATERIAL_REF {12} }}\n'''.format('mesh' + str(geomlist.index(x)), \ len(x[0]), \ len(x[1]), \ vertlist, \ facelist, \ len(x[0]), \ tvertlist, \ len(x[1]), \ tfacelist, \ len(x[1]), \ cfacelist, \ normals, \ x[1][0][3]) # material reference from first face data = '''*3DSMAX_ASCIIEXPORT\t200 *COMMENT "{0} v{1}" *SCENE {{ {2} }} *MATERIAL_LIST {{ \t*MATERIAL_COUNT {3} {4}}} {5}'''.format(script, version, scene, len(shaderlist), materials, geomobjects) # Write the compiled data to the output file file = open(fullpath, 'w') file.write(data) file.close() # __executeCommand__ evaluates to true after DarkRadiant has successfully initialised if __executeCommand__: execute() DarkRadiant-2.5.0/install/scripts/commands/check_for_invalid_visportals.py000066400000000000000000000057151321750546400271500ustar00rootroot00000000000000"""Find any visportal brushes in the map with more than exactly one face assigned to the textures/editor/visportal shader. Tracker item: #4397 (http://bugs.thedarkmod.com/view.php?id=4397) Author: greebo Version: 1.1 """ __commandName__ = 'check_invalid_visportals' # should not contain spaces __commandDisplayName__ = 'Test for invalid Visportals ' def execute(): # Returns 1 if the brush is a valid visportal, 0 otherwise def testVisportalBrush(brush, visportalShader): numVisportalFaces = 0 for index in range(brush.getNumFaces()): if brush.getFace(index).getShader() == visportalShader: numVisportalFaces += 1 if numVisportalFaces != 1: return 0 # brush is not OK return 1 # brush is ok class VisportalResults(object): numVisportals = 0 invalidPortals = [] portalsNotChildOfWorldspawn = [] results = VisportalResults() visportalShader = 'textures/editor/visportal' import darkradiant as dr class VisportalChecker(dr.SceneNodeVisitor): def pre(self, node): if node.isBrush(): brush = node.getBrush() if brush.hasShader(visportalShader): results.numVisportals += 1 # Check the parent of this brush (must be child of worldspawn) if brush.getParent().isEntity(): if brush.getParent().getEntity().getKeyValue('classname') != 'worldspawn': results.invalidPortals.append(brush) results.portalsNotChildOfWorldspawn.append(brush) if not testVisportalBrush(brush, visportalShader): results.invalidPortals.append(brush) return 0 # don't traverse brushes return 1 # Instantiate a new walker object and check brushes walker = VisportalChecker() GlobalSceneGraph.root().traverse(walker) # Notify the user about the results msg = '%d visportals checked, %d have errors' % (results.numVisportals, len(results.invalidPortals)) + '\n\n' if results.numVisportals == 0: msg = 'There are no visportals in this map.' if len(results.invalidPortals) > 0: # Unselect everything in the scene before highlighting the problematic portals GlobalSelectionSystem.setSelectedAll(0) # Highlight the brushes one by one for brush in results.invalidPortals: brush.setSelected(1) if len(results.portalsNotChildOfWorldspawn) > 0: msg += '%d visportal brushes are not parented to worldspawn\n' % (len(results.portalsNotChildOfWorldspawn)) msg += 'The problematic visportals have been highlighted.' GlobalDialogManager.createMessageBox('Visportal Test Results', msg, dr.Dialog.CONFIRM).run() if __executeCommand__: execute() DarkRadiant-2.5.0/install/scripts/commands/convert_to_ase_and_replace.py000066400000000000000000000376601321750546400265640ustar00rootroot00000000000000# ***** BEGIN GPL LICENSE BLOCK ***** # # 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 2 # 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, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** #TODO: # Set the command name so that DarkRadiant recognises this file __commandName__ = 'aseConvertAndReplace' __commandDisplayName__ = 'Convert to ASE...' # The actual algorithm called by DarkRadiant is contained in the execute() function def execute(): script = "Dark Radiant ASCII Scene Export (*.ase)" author = "Richard Bartlett, some additions by greebo" version = "0.7" import darkradiant as dr # Check if we have a valid selection selectionInfo = GlobalSelectionSystem.getSelectionInfo() # Don't allow empty selections or selected components only if selectionInfo.totalCount == 0 or selectionInfo.totalCount == selectionInfo.componentCount: errMsg = GlobalDialogManager.createMessageBox('No selection', 'Nothing selected, cannot run exporter.', dr.Dialog.ERROR) errMsg.run() return if selectionInfo.entityCount != 1: errMsg = GlobalDialogManager.createMessageBox('Wrong selection', 'Please select exactly one func_static.', dr.Dialog.ERROR) errMsg.run() return if not GlobalRegistry.get('user/scripts/aseExport/initialUserWarning') == '1': GlobalRegistry.set('user/scripts/aseExport/initialUserWarning', '1') warningMsg = GlobalDialogManager.createMessageBox('Careful', 'This option will export the selected func_static to ASE and replace your entity with the newly created model.\nThe currently selected func_static will be DELETED. You can undo this operation, but know what you''re doing!', dr.Dialog.WARNING) warningMsg.run() shaderlist = [] geomlist = [] # simple linear triangulation of n-sided poly def triangulate(pointset): tris = [] for count in range(1, len(pointset) - 1): tris.append([pointset[0], pointset[count], pointset[count + 1]]) return tris # skin patch matrix with tris def skinmatrix(pointset, width, height): tris = [] for h in range(height-1): for w in range(width-1): tris.append([pointset[w+(h*width)], pointset[w+1+(h*width)], pointset[w+width+(h*width)]]) tris.append([pointset[w+1+(h*width)], pointset[w+1+width+(h*width)], pointset[w+width+(h*width)]]) return tris def processBrush(brushnode): verts = [] faces = [] numfaces = brushnode.getNumFaces() for index in range(numfaces): facenode = brushnode.getFace(index) shader = facenode.getShader() if not shader in shaderlist: shaderlist.append(shader) winding = facenode.getWinding() tris = triangulate([x+len(verts) for x in range(len(winding))]) for x in tris: x.append(shaderlist.index(shader)) faces.append(x) for x in reversed(winding): verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()]) geomlist.append([verts, faces]) return def processPatch(patchnode): verts = [] faces = [] shader = patchnode.getShader() if not shader in shaderlist: shaderlist.append(shader) mesh = patchnode.getTesselatedPatchMesh() for x in mesh.vertices: verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()]) tris = skinmatrix([x for x in range(len(verts))], mesh.width, mesh.height) for x in tris: x.append(shaderlist.index(shader)) faces.append(x) geomlist.append([verts, faces]) return # Branch on primitive nodes def processPrimitive(scenenode): if scenenode.isBrush(): processBrush(scenenode.getBrush()) elif scenenode.isPatch(): processPatch(scenenode.getPatch()) return # Traversor class to visit child primitives of entities class nodeVisitor(dr.SceneNodeVisitor): def pre(self, scenenode): # Brush? if scenenode.isBrush(): processBrush(scenenode.getBrush()) # Patch? elif scenenode.isPatch(): processPatch(scenenode.getPatch()) # Traverse all child nodes, regardless of type return 1 class dataCollector(dr.SelectionVisitor): from darkradiant import Vector3 fs_origin = Vector3(0,0,0) fs = 0 def visit(self, scenenode): if scenenode.isBrush() or scenenode.isPatch(): processPrimitive(scenenode) elif scenenode.isEntity(): import re from darkradiant import Vector3 # greebo: Found an entity, this could be a func_static or similar # Traverse children of this entity using a new walker nodewalker = nodeVisitor() scenenode.traverse(nodewalker) entitynode = scenenode.getEntity() if not entitynode.getKeyValue("origin") == '': origin = entitynode.getKeyValue("origin"); coords = re.findall(r'([\-\d.]+)', origin) self.fs_origin = Vector3(float(coords[0]), float(coords[1]), float(coords[2])) self.fs = scenenode else: print('WARNING: unsupported node type selected. Skipping: ' + scenenode.getNodeType()) walker = dataCollector() GlobalSelectionSystem.foreachSelected(walker) found_func_static_origin = walker.fs_origin found_func_static = walker.fs # Dialog dialog = GlobalDialogManager.createDialog(script + 'v' + version) # Add an entry box and remember the handle fileHandle = dialog.addEntryBox("Filename:") #dialog.setElementValue(fileHandle, GlobalRegistry.get('user/scripts/aseExport/recentFilename')) suggestedName = found_func_static.getEntity().getKeyValue("name") dialog.setElementValue(fileHandle, suggestedName + ".ase") # Add an entry box and remember the handle pathHandle = dialog.addPathEntry("Save path:", True) dialog.setElementValue(pathHandle, GlobalRegistry.get('user/scripts/aseExport/recentPath')) if dialog.run() == dr.Dialog.OK: fullpath = dialog.getElementValue(pathHandle) + '/' + dialog.getElementValue(fileHandle) if not fullpath.endswith('.ase'): fullpath = fullpath + '.ase' # Save the path for later use GlobalRegistry.set('user/scripts/aseExport/recentFilename', dialog.getElementValue(fileHandle)) GlobalRegistry.set('user/scripts/aseExport/recentPath', dialog.getElementValue(pathHandle)) try: file = open(fullpath, 'r') file.close() prompt = GlobalDialogManager.createMessageBox('Warning', 'The file ' + fullpath + ' already exists. Do you wish to overwrite it?', dr.Dialog.ASK) if prompt.run() == dr.Dialog.YES: overwrite = True else: overwrite = False except IOError: overwrite = True if overwrite: xlist = [] ylist = [] zlist = [] # center objects at found func_static origin xcenter = found_func_static_origin.x() ycenter = found_func_static_origin.y() zcenter = found_func_static_origin.z() for item in geomlist: for vert in item[0]: vert[0] = vert[0] - xcenter vert[1] = vert[1] - ycenter vert[2] = vert[2] - zcenter # split objects that do not share the same texture on all faces for x in geomlist: texlist = [] for data in x[1]: texlist.append(data[3]) if len(set(texlist)) > 1: temp = [] for texture in set(texlist): vertlist = [] facelist = [] for data in x[1]: if data[3] == texture: facelist.append(data) usedverts = [] for face in facelist: usedverts.extend(face[0:3]) usedverts = list(set(usedverts)) for index in usedverts: vertlist.append(x[0][index]) newfacelist = [] for face in facelist: newfacelist.append([vertlist.index(x[0][face[0]]),vertlist.index(x[0][face[1]]),vertlist.index(x[0][face[2]]),face[3]]) temp.append([vertlist, newfacelist]) del geomlist[geomlist.index(x)] geomlist.extend(temp) scene = '''\t*SCENE_FILENAME "{0}" \t*SCENE_FIRSTFRAME 0 \t*SCENE_LASTFRAME 100 \t*SCENE_FRAMESPEED 30 \t*SCENE_TICKSPERFRAME 160 \t*SCENE_BACKGROUND_STATIC 0.0000\t0.0000\t0.0000 \t*SCENE_AMBIENT_STATIC 0.0000\t0.0000\t0.0000'''.format(GlobalMap.getMapName()) materials = str() for x in shaderlist: materials = materials + '''\t*MATERIAL {0} {{ \t\t*MATERIAL_NAME "{1}" \t\t*MATERIAL_CLASS "Standard" \t\t*MATERIAL_AMBIENT 0.5882\t0.5882\t0.5882 \t\t*MATERIAL_DIFFUSE 0.5882\t0.5882\t0.5882 \t\t*MATERIAL_SPECULAR 0.9000\t0.9000\t0.9000 \t\t*MATERIAL_SHINE 0.1000 \t\t*MATERIAL_SHINESTRENGTH 0.0000 \t\t*MATERIAL_TRANSPARENCY 0.0000 \t\t*MATERIAL_WIRESIZE 1.0000 \t\t*MATERIAL_SHADING Blinn \t\t*MATERIAL_XP_FALLOFF 0.0000 \t\t*MATERIAL_SELFILLUM 0.0000 \t\t*MATERIAL_FALLOFF In \t\t*MATERIAL_XP_TYPE Filter \t\t*MAP_DIFFUSE {{ \t\t\t*MAP_NAME "{2}" \t\t\t*MAP_CLASS "Bitmap" \t\t\t*MAP_SUBNO 1 \t\t\t*MAP_AMOUNT 1.0000 \t\t\t*BITMAP "\\\\purgatory\\purgatory\\doom\\base\{2}" \t\t\t*MAP_TYPE Screen \t\t\t*UVW_U_OFFSET 0.0000 \t\t\t*UVW_V_OFFSET 0.0000 \t\t\t*UVW_U_TILING 1.0000 \t\t\t*UVW_V_TILING 1.0000 \t\t\t*UVW_ANGLE 0.0000 \t\t\t*UVW_BLUR 1.0000 \t\t\t*UVW_BLUR_OFFSET 0.0000 \t\t\t*UVW_NOUSE_AMT 1.0000 \t\t\t*UVW_NOISE_SIZE 1.0000 \t\t\t*UVW_NOISE_LEVEL 1 \t\t\t*UVW_NOISE_PHASE 0.0000 \t\t\t*BITMAP_FILTER Pyramidal \t\t}} \t}} '''.format(shaderlist.index(x), x, x.replace('/','\\')) geomobjects = str() for x in geomlist: # x[0] = vertices # vert[0] - vert[2] = vertex coords # vert[3] - vert[4] = texture coords # vert[5] - vert[7] = normal # x[1] = faces vertlist = str() for count, data in enumerate(x[0]): vertlist = vertlist + '''\t\t\t*MESH_VERTEX {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, data[0], data[1], data[2]) facelist = str() for count, data in enumerate(x[1]): facelist = facelist + '''\t\t\t*MESH_FACE {0}: A: {1} B: {2} C: {3} AB: 0 BC: 0 CA: 0\t *MESH_SMOOTHING 1 \t*MESH_MTLID {4}\n'''.format(count, data[0], data[1], data[2], data[3]) tvertlist = str() for count, data in enumerate(x[0]): tvertlist = tvertlist + '''\t\t\t*MESH_TVERT {0}\t{1: 10.4f}\t{2: 10.4f}\t0.0000\n'''.format(count, data[3], data[4]) tfacelist = str() for count, data in enumerate(x[1]): tfacelist = tfacelist + '''\t\t\t*MESH_TFACE {0}\t{1}\t{2}\t{3}\n'''.format(count, data[0], data[1], data[2]) cfacelist = str() for count, data in enumerate(x[1]): cfacelist = cfacelist + '''\t\t\t*MESH_CFACE {0}\t0\t0\t0\n'''.format(count) normals = str() for count, data in enumerate(x[1]): normals += '''\t\t\t*MESH_FACENORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7]) # greebo: use first vertex normal as face normal normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[0], x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7]) normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[1], x[0][data[1]][5], x[0][data[1]][6], x[0][data[1]][7]) normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[2], x[0][data[2]][5], x[0][data[2]][6], x[0][data[2]][7]) if len(x[1]) == 0: continue geomobjects = geomobjects + '''*GEOMOBJECT {{ \t*NODE_NAME "{0}" \t*NODE_TM {{ \t\t*NODE_NAME "{0}" \t\t*INHERIT_POS 0 0 0 \t\t*INHERIT_ROT 0 0 0 \t\t*INHERIT_SCL 0 0 0 \t\t*TM_ROW0 1.0000\t0.0000\t0.0000 \t\t*TM_ROW1 0.0000\t1.0000\t0.0000 \t\t*TM_ROW2 0.0000\t0.0000\t1.0000 \t\t*TM_ROW3 0.0000\t0.0000\t0.0000 \t\t*TM_POS 0.0000\t0.0000\t0.0000 \t\t*TM_ROTAXIS 0.0000\t0.0000\t0.0000 \t\t*TM_ROTANGLE 0.0000 \t\t*TM_SCALE 1.0000\t1.0000\t1.0000 \t\t*TM_SCALEAXIS 0.0000\t0.0000\t0.0000 \t\t*TM_SCALEAXISANG 0.0000 \t}} \t*MESH {{ \t\t*TIMEVALUE 0 \t\t*MESH_NUMVERTEX {1} \t\t*MESH_NUMFACES {2} \t\t*MESH_VERTEX_LIST {{ {3}\t\t}} \t\t*MESH_FACE_LIST {{ {4}\t\t}} \t\t*MESH_NUMTVERTEX {5} \t\t*MESH_TVERTLIST {{ {6}\t\t}} \t\t*MESH_NUMTVFACES {7} \t\t*MESH_TFACELIST {{ {8}\t\t}} \t\t*MESH_NUMCVERTEX 1 \t\t*MESH_CVERTLIST {{ \t\t\t*MESH_VERTCOL 0\t1.0000\t1.0000\t1.0000 \t\t}} \t\t*MESH_NUMCVFACES {9} \t\t*MESH_CFACELIST {{ {10}\t\t}} \t\t*MESH_NORMALS {{ {11}\t\t}} \t}} \t*PROP_MOTIONBLUR 0 \t*PROP_CASTSHADOW 1 \t*PROP_RECVSHADOW 1 \t*MATERIAL_REF {12} }}\n'''.format('mesh' + str(geomlist.index(x)), \ len(x[0]), \ len(x[1]), \ vertlist, \ facelist, \ len(x[0]), \ tvertlist, \ len(x[1]), \ tfacelist, \ len(x[1]), \ cfacelist, \ normals, \ x[1][0][3]) # material reference from first face data = '''*3DSMAX_ASCIIEXPORT\t200 *COMMENT "{0} v{1}" *SCENE {{ {2} }} *MATERIAL_LIST {{ \t*MATERIAL_COUNT {3} {4}}} {5}'''.format(script, version, scene, len(shaderlist), materials, geomobjects) # Write the compiled data to the output file file = open(fullpath, 'w') file.write(data) file.close() GlobalCommandSystem.execute('DeleteSelection') # Create a new func_static func_static = GlobalEntityCreator.createEntity("func_static") # Set the origin originKey = '{0} {1} {2}'.format(found_func_static_origin.x(), found_func_static_origin.y(), found_func_static_origin.z()) func_static.getEntity().setKeyValue("origin", originKey) relativePath = fullpath relativePath = relativePath.replace(GlobalGameManager.getModPath(), '') relativePath = relativePath.replace(GlobalGameManager.getModBasePath(), '') print('Relative path is ' + relativePath) func_static.getEntity().setKeyValue("model", relativePath) # Insert the entity as child to the root node func_static.addToContainer(GlobalSceneGraph.root()) # __executeCommand__ evaluates to true after DarkRadiant has successfully initialised if __executeCommand__: execute()DarkRadiant-2.5.0/install/scripts/commands/example.py000066400000000000000000000014401321750546400226530ustar00rootroot00000000000000# Set the command name so that DarkRadiant recognises this file __commandName__ = 'Example' # should not contain spaces __commandDisplayName__ = 'Nice display name for the menus' # should not contain spaces # The actual algorithm called by DarkRadiant is contained in the execute() function def execute(): shader = GlobalShaderSystem.getShaderForName('bc_rat') print(shader.getName()) # the "Global*" variables like GlobalShaderSystem are already exposed to this scripts # The rest needs to imported from the darkradiant module and referred to by the prefix "dr" import darkradiant as dr GlobalDialogManager.createMessageBox('Example Box', str(e), dr.Dialog.ERROR).run() # __executeCommand__ evaluates to true after DarkRadiant has successfully initialised if __executeCommand__: execute() DarkRadiant-2.5.0/install/scripts/commands/export_obj.py000066400000000000000000000264441321750546400234060ustar00rootroot00000000000000# ***** BEGIN GPL LICENSE BLOCK ***** # # 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 2 # 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, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** # Set the command name so that DarkRadiant recognises this file __commandName__ = 'objExport' __commandDisplayName__ = 'Export OBJ...' # The actual algorithm called by DarkRadiant is contained in the execute() function def execute(): script = "DarkRadiant Wavefront OBJ Export (*.obj)" author = "Python port by greebo, based on original exporter C++ code in DarkRadiant and the ASE exporter scripts" version = "0.2" import darkradiant as dr # Check if we have a valid selection selectionInfo = GlobalSelectionSystem.getSelectionInfo() # Don't allow empty selections or selected components only if selectionInfo.totalCount == 0 or selectionInfo.totalCount == selectionInfo.componentCount: errMsg = GlobalDialogManager.createMessageBox('No selection', 'Nothing selected, cannot run exporter.', dr.Dialog.ERROR) errMsg.run() return # An exportable object found in the map class Geometry(object): name = '' # Name of the object to be exported vertices = [] # Vertices texcoords = [] # Texture coordinates faces = [] # Each face in turn is an array of indices referencing the vertices and texcoords # For simplicity, we assume that the referenced vertices and texcoords always # have the same global index number. def __init__(self, name): self.name = name self.vertices = [] self.texcoords = [] self.faces = [] # An exportable object found in the map class Geometries(object): vertexCount = 0 # Global Vertex Index (every vertex in an OBJ file has a unique number) objects = [] # List of Geometry objects # We put all of the objects we collect in the map into this array # Before we write it to the output stream the vertices can be processed (e.g. centered) geomlist = Geometries() def processBrush(brushnode): # Create a new exportable object geometry = Geometry('Brush{0}'.format(len(geomlist.objects))) numfaces = brushnode.getNumFaces() for index in range(numfaces): facenode = brushnode.getFace(index) shader = facenode.getShader() # Tels: skip if caulk and no caulk should be exported if (shader == 'textures/common/caulk') and (int(GlobalRegistry.get('user/scripts/objExport/exportcaulk'))) == 0: continue winding = facenode.getWinding() # Remember the index of the first vertex firstVertex = geomlist.vertexCount for point in winding: # Write coordinates into the export buffers geometry.vertices.append(point.vertex) geometry.texcoords.append(point.texcoord) # Keep track of the exported vertices geomlist.vertexCount += 1 # Append the face to the list, referencing vertices from firstVertex to the current count # Face indices are 1-based, so increase by 1 each time # Reverse the list to produce the correct face normal direction geometry.faces.append([i for i in reversed(range(firstVertex+1, geomlist.vertexCount+1))]) print('Processed brush geometry: {0} verts and {1} faces'.format(len(geometry.vertices), len(geometry.faces))) geomlist.objects.append(geometry) return def processPatch(patchnode): shader = patchnode.getShader() # Tels: skip if caulk and no caulk should be exported if shader == 'textures/common/caulk' and int(GlobalRegistry.get('user/scripts/objExport/exportcaulk')) == 0: return # Create a new exportable object geometry = Geometry('Patch{0}'.format(len(geomlist.objects))) mesh = patchnode.getTesselatedPatchMesh() # Remember the index of the first vertex firstVertex = geomlist.vertexCount for h in range(0, mesh.height): for w in range(0, mesh.width): point = mesh.vertices[mesh.width*h + w] # Write coordinates into the lists geometry.vertices.append(point.vertex) geometry.texcoords.append(point.texcoord) # Keep track of the exported vertices geomlist.vertexCount += 1 # Starting from the second row, we're gathering the faces if h > 0 and w > 0: # Gather the indices forming a quad v1 = 1 + firstVertex + h*mesh.width + w; v2 = 1 + firstVertex + (h-1)*mesh.width + w; v3 = 1 + firstVertex + (h-1)*mesh.width + (w-1); v4 = 1 + firstVertex + h*mesh.width + (w-1); # Construct the quad geometry.faces.append([v1, v4, v3, v2]) print('Processed patch geometry: {0} verts and {1} faces'.format(len(geometry.vertices), len(geometry.faces))) geomlist.objects.append(geometry) return # Traversor class to visit child primitives of entities class nodeVisitor(dr.SceneNodeVisitor): def pre(self, scenenode): if scenenode.isBrush(): processBrush(scenenode.getBrush()) elif scenenode.isPatch(): processPatch(scenenode.getPatch()) # Traverse all child nodes, regardless of type return 1 class SelectionWalker(dr.SelectionVisitor): def visit(self, scenenode): if scenenode.isBrush(): processBrush(scenenode.getBrush()) elif scenenode.isPatch(): processPatch(scenenode.getPatch()) elif scenenode.isEntity(): # greebo: Found an entity, this could be a func_static or similar # Traverse children of this entity using a new walker nodewalker = nodeVisitor() scenenode.traverse(nodewalker) else: print('WARNING: unsupported node type selected. Skipping: ' + scenenode.getNodeType()) # Dialog dialog = GlobalDialogManager.createDialog(script + ' v' + version) # Add an entry box and remember the handle fileHandle = dialog.addEntryBox("Filename:") dialog.setElementValue(fileHandle, GlobalRegistry.get('user/scripts/objExport/recentFilename')) # Add an entry box and remember the handle pathHandle = dialog.addPathEntry("Save path:", True) dialog.setElementValue(pathHandle, GlobalRegistry.get('user/scripts/objExport/recentPath')) # Add a checkbox centerObjectsHandle = dialog.addCheckbox("Center objects at 0,0,0 origin") dialog.setElementValue(centerObjectsHandle, GlobalRegistry.get('user/scripts/objExport/centerObjects')) # Add another checkbox exportCaulkHandle = dialog.addCheckbox("Export caulked faces") dialog.setElementValue(exportCaulkHandle, GlobalRegistry.get('user/scripts/objExport/exportcaulk')) if dialog.run() == dr.Dialog.OK: fullpath = dialog.getElementValue(pathHandle) + '/' + dialog.getElementValue(fileHandle) if not fullpath.endswith('.obj'): fullpath = fullpath + '.obj' # Save the path for later use GlobalRegistry.set('user/scripts/objExport/recentFilename', dialog.getElementValue(fileHandle)) GlobalRegistry.set('user/scripts/objExport/recentPath', dialog.getElementValue(pathHandle)) GlobalRegistry.set('user/scripts/objExport/centerObjects', dialog.getElementValue(centerObjectsHandle)) GlobalRegistry.set('user/scripts/objExport/exportcaulk', dialog.getElementValue(exportCaulkHandle)) try: file = open(fullpath, 'r') file.close() prompt = GlobalDialogManager.createMessageBox('Warning', 'The file ' + fullpath + ' already exists. Do you wish to overwrite it?', dr.Dialog.ASK) if prompt.run() == dr.Dialog.YES: overwrite = True else: overwrite = False except IOError: overwrite = True if overwrite: walker = SelectionWalker() GlobalSelectionSystem.foreachSelected(walker) # greebo: Check if we should center objects at the 0,0,0 origin if int(dialog.getElementValue(centerObjectsHandle)) == 1: # center objects at 0,0,0 xlist = [] ylist = [] zlist = [] for item in geomlist.objects: for vert in item.vertices: xlist.append(vert.x()) ylist.append(vert.y()) zlist.append(vert.z()) xcenter=(max(xlist)+min(xlist))/2 ycenter=(max(ylist)+min(ylist))/2 zcenter=(max(zlist)+min(zlist))/2 for item in geomlist.objects: for vert in item.vertices: vert -= Vector3(xcenter, ycenter, zcenter) # This string will hold our export data data = str() for x in geomlist.objects: objstr = str() # Group name (g) objstr = objstr + "g {0}\n\n".format(x.name) # Vertex list (v) for vert in x.vertices: objstr = objstr + "v {0} {1} {2}\n".format(vert.x(), vert.y(), vert.z()) objstr = objstr + "\n" # Texture coord list (vt) for texcoord in x.texcoords: objstr = objstr + "vt {0} {1}\n".format(texcoord.x(), 1-texcoord.y()) # reverse V coord objstr = objstr + "\n" # Face list (f) for faceIndices in x.faces: objstr = objstr + "f" for faceIndex in faceIndices: objstr = objstr + " {0}/{1}".format(faceIndex, faceIndex) objstr = objstr + "\n" objstr = objstr + "\n" data = data + objstr # Write the compiled data to the output file file = open(fullpath, 'w') file.write(data) file.close() print('Done writing OBJ data to {0}'.format(fullpath)) # __executeCommand__ evaluates to true after DarkRadiant has successfully initialised if __executeCommand__: execute() DarkRadiant-2.5.0/install/scripts/commands/patchsplitter.py000066400000000000000000000302511321750546400241100ustar00rootroot00000000000000__commandName__ = 'SplitPatch' __commandDisplayName__ = 'Split patch' def execute(): MAXGRIDPOWER = 8 # Grid 256 MINGRIDPOWER = -1 # Grid 0.5 class UserError(Exception): pass class TooManyVerts(Exception): pass class TooFewVerts(Exception): pass class Vert: """Holds coords for one patch control vertex.""" def __init__(self, vertex, texcoord): from darkradiant import Vector3, Vector2 self.vertex = Vector3(vertex) self.texcoord = Vector2(texcoord) class Verts: """A 2d matrix (row/col) implemented as a list where each element is a list of Vert objects in one row of the patch.""" def __init__(self, patch=None): self.verts = [] if patch: for rownum in range(patch.getHeight()): row = [] for colnum in range(patch.getWidth()): v = Vert(patch.ctrlAt(rownum, colnum).vertex, patch.ctrlAt(rownum, colnum).texcoord) row.append(v) self.verts.append(row) class PatchData: """Store all of the information needed to reconstruct a patch exactly.""" def __init__(self, patch): self.cols = patch.getWidth() # int self.rows = patch.getHeight() # int self.verts = Verts(patch) self.shader = patch.getShader() # string self.subdivisionsFixed = patch.subdivionsFixed() # bool self.subdivs = patch.getSubdivisions() # Subdivisions def replaceverts(self, verts): self.verts = verts self.rows = len(verts.verts) self.cols = len(verts.verts[0]) def getRows(self, first=0, last=None): """Return a Verts object holding the subset of the patch mesh verts in the specified row range.""" if not last: last = self.rows result = Verts() result.verts = self.verts.verts[first:last] return result def getCols(self, first=0, last=None): """Return a Verts object holding the subset of the patch mesh verts in the specified column range.""" if not last: last = self.cols result = Verts() result.verts = [r[first:last] for r in self.verts.verts] return result def getVertex(self, row, col): return self.verts.verts[row][col].vertex def getTexcoord(self, row, col): return self.verts.verts[row][col].texcoord # Utility functions def getAndValidateSelection(): sInfo = GlobalSelectionSystem.getSelectionInfo() if sInfo.patchCount != 1 or sInfo.brushCount > 0 or sInfo.entityCount > 0 or sInfo.componentCount < 2: raise UserError('Bad selection. Select one patch only, and ' \ '2 or more verts from the same (pink) row or column.') patch = GlobalSelectionSystem.ultimateSelected().getPatch() if not patch.isValid(): raise UserError("This isn't a valid patch. It has some invalid " \ "vertices, or maybe all vertices are in the same spot.") #if not patch.subdivionsFixed(): #raise UserError('You need to fix tesselation on the patch in the ' \ #'Patch Inspector (Shift+S) before splitting it.') return patch def resetPatch(patch, patchdata): """Apply patchdata to the patch, reconstructing it.""" p, pd = patch, patchdata p.setDims(pd.cols, pd.rows) p.setShader(pd.shader) p.setFixedSubdivisions(pd.subdivisionsFixed, pd.subdivs) for row in range(pd.rows): for col in range(pd.cols): p.ctrlAt(row, col).vertex = pd.getVertex(row, col) p.ctrlAt(row, col).texcoord = pd.getTexcoord(row, col) p.controlPointsChanged() def clonePatch(patch): """Clone the existing patch so the new one is automatically part of the same func_static and layer as the original. Return a reference to the new patch.""" patch.setSelected(0) # Clears vertex editing mode patch.setSelected(1) GlobalCommandSystem.execute('CloneSelection') return GlobalSelectionSystem.ultimateSelected().getPatch() # Procedures for identifying the selected verts. # They can't be accessed directly, so move them about to find out at what # distance they cross the bounding box of the patch. Then infer the # selected verts' bounding box, and which row or col they sit on. def distToEdge(patch, direction, axis, gridPower): """Return distance from selected verts' bounding box to the edge of the patch's bounding box. Make sure orthoview is facing right way before calling. SIDE EFFECTS: Leaves selected verts displaced by a small amount. Grid size is changed.""" GlobalGrid.setGridSize(gridPower) stepsize = 2 ** gridPower reverse = 'right' if direction == 'left' \ else 'left' if direction == 'right' \ else 'down' if direction == 'up' \ else 'up' origin = lambda: getattr(patch.getWorldAABB().origin, axis)() extent = lambda: getattr(patch.getWorldAABB().extents, axis)() if direction in ('right', 'up'): boundary = lambda: origin() + extent() comparison = '<=' else: boundary = lambda: origin() - extent() comparison = '>=' starting_boundary = boundary() looplimit = (extent() * 2) / stepsize # Safety valve. Don't let verts move further than the # extent of the patch. Stops an infinite loop crashing # DR if something goes wrong. stepcount = 0 while eval('boundary() %s starting_boundary' % comparison) and stepcount < looplimit: GlobalCommandSystem.execute('SelectNudge' + direction) stepcount += 1 for undostep in range(stepcount): GlobalCommandSystem.execute('SelectNudge' + reverse) return (stepcount - 1) * stepsize def attemptGetVertsLine(patch, tolerance): """Return ('row', 4) or ('col', 2) etc. Designed to be called multiple times with different tolerance if necessary. SIDE EFFECTS: Leaves selected verts displaced by a small amount. Grid size is changed.""" from math import log AABB = patch.getWorldAABB() gridPower = int(log(tolerance, 2)) GlobalCommandSystem.execute('ViewSide') minX = AABB.origin.x() - AABB.extents.x() + distToEdge(patch, 'left', 'x', gridPower) - tolerance maxX = AABB.origin.x() + AABB.extents.x() - distToEdge(patch, 'right', 'x', gridPower) + tolerance minZ = AABB.origin.z() - AABB.extents.z() + distToEdge(patch, 'down', 'z', gridPower) - tolerance maxZ = AABB.origin.z() + AABB.extents.z() - distToEdge(patch, 'up', 'z', gridPower) + tolerance GlobalCommandSystem.execute('ViewFront') minY = AABB.origin.y() - AABB.extents.y() + distToEdge(patch, 'left', 'y', gridPower) - tolerance maxY = AABB.origin.y() + AABB.extents.y() - distToEdge(patch, 'right', 'y', gridPower) + tolerance GlobalCommandSystem.execute('ViewTop') # Find which verts lie in the selected box includedRows = set() includedCols = set() for row in range(patch.getHeight()): for col in range(patch.getWidth()): vert = patch.ctrlAt(row, col) if minX <= vert.vertex.x() <= maxX and \ minY <= vert.vertex.y() <= maxY and \ minZ <= vert.vertex.z() <= maxZ: includedRows.add(row) includedCols.add(col) # Interpret result # Special case: if the user selects the existing seam of a polyhedron like a sphere or cone: if includedRows == set((0, patch.getHeight()-1)) or includedCols == set((0, patch.getWidth()-1)): raise UserError("You've selected the existing seam of a 3d patch. It's already cut " \ "there, so no action has been taken.") if len(includedRows) > 1 and len(includedCols) > 1: raise TooManyVerts() elif len(includedRows) < 2 and len(includedCols) < 2: raise TooFewVerts() elif len(includedRows) == 1: return 'row', includedRows.pop() else: return 'col', includedCols.pop() def getSelectedVertsLine(patch, patchdata): """Return ('row', 4) or ('col', 2) etc. Makes multiple attempts if needed. Mutates patch, so uses patchdata to restore state.""" userGridPower = GlobalGrid.getGridPower() # Start with tolerance = the user's grid size tolerance = 2 ** userGridPower mintolerance = 2 ** MINGRIDPOWER maxtolerance = 2 ** MAXGRIDPOWER lineType, lineNum = None, None while (not lineType) and mintolerance <= tolerance <= maxtolerance: try: print('Patch Splitter: Trying to identify selected verts with tolerance %0.2f' % tolerance) lineType, lineNum = attemptGetVertsLine(patch, tolerance) # has side effects that can't be fixed till search \ except TooManyVerts: # \ is finished, as the fix clears the user's selection tolerance /= 2.0 except TooFewVerts: tolerance *= 2 GlobalGrid.setGridSize(userGridPower) # \ fix the side-effects resetPatch(patch, patchdata) # / if not lineType: raise UserError('Unable to determine selected verts. Try again with different ' \ 'verts from the same row/column. Make sure all the verts are ' \ 'on the same line. Selecting 2 _pink_ verts might help with ' \ 'very twisty patches.') if lineNum == 0 \ or (lineType == 'row' and lineNum == patch.getHeight()-1) \ or (lineType == 'col' and lineNum == patch.getWidth()-1): raise UserError("You've selected the existing edge of the patch. No action has been taken.") if lineNum % 2: raise UserError("You've selected a green line. Patches can be cut only along lines with some pink verts.") return lineType, lineNum # Execution starts here # STEP 1: Validate selection patch = getAndValidateSelection() patchdata = PatchData(patch) # STEP 2: Work out what row or column is selected lineType, lineNum = getSelectedVertsLine(patch, patchdata) print('RESULT: ', lineType, lineNum) # STEP 3: Split the patch newpatch = clonePatch(patch) try: if lineType == 'row': newverts1 = patchdata.getRows(last=lineNum+1) newverts2 = patchdata.getRows(first=lineNum) elif lineType == 'col': newverts1 = patchdata.getCols(last=lineNum+1) newverts2 = patchdata.getCols(first=lineNum) # Make PatchData objects for the 2 new patches. Initialize them with # either of the existing patches then swap out the verts list. newdata1, newdata2 = PatchData(newpatch), PatchData(newpatch) newdata1.replaceverts(newverts1) newdata2.replaceverts(newverts2) # Adjust the patches resetPatch(newpatch, newdata2) resetPatch(patch, newdata1) except Exception as e: # Clean up before reporting error GlobalSelectionSystem.setSelectedAll(False) newpatch.setSelected(True) GlobalCommandSystem.execute('deleteSelected') resetPatch(patch, patchdata) raise # Step 4: Success! leave the new patches selected patch.setSelected(True) newpatch.setSelected(True) import darkradiant as dr if __executeCommand__: try: execute() except Exception as e: GlobalDialogManager.createMessageBox('Patch Splitter', str(e), dr.Dialog.ERROR).run() DarkRadiant-2.5.0/install/scripts/commands/select_all_models_of_type.py000066400000000000000000000027321321750546400264240ustar00rootroot00000000000000# Set the command name so that DarkRadiant recognises this file __commandName__ = 'SelectAllModelsOfType' __commandDisplayName__ = 'Select all Models of same type' # The actual algorithm called by DarkRadiant is contained in the execute() # function def execute(): # Collect all currently selected models selectedModelNames = {} import darkradiant as dr class Walker(dr.SelectionVisitor) : def visit(self, node): # Try to "cast" the node to an entity entity = node.getEntity() if not entity.isNull(): if not entity.getKeyValue('model') == '': selectedModelNames[entity.getKeyValue('model')] = 1 visitor = Walker() GlobalSelectionSystem.foreachSelected(visitor) print('Unique models currently selected: ' + str(len(selectedModelNames))) # Now traverse the scenegraph, selecting all of the same names class SceneWalker(dr.SceneNodeVisitor) : def pre(self, node): # Try to "cast" the node to an entity entity = node.getEntity() if not entity.isNull(): modelName = entity.getKeyValue('model') if not modelName == '' and modelName in selectedModelNames: # match, select this node node.setSelected(1) # SteveL #3690: fixing return values return 0 # don't traverse this entity's children return 1 # not an entity, so traverse children walker = SceneWalker() GlobalSceneGraph.root().traverse(walker) # __executeCommand__ evaluates to true after DarkRadiant has successfully # initialised if __executeCommand__: execute() DarkRadiant-2.5.0/install/scripts/commands/shift_textures_randomly.py000066400000000000000000000013111321750546400262020ustar00rootroot00000000000000# Set the command name so that DarkRadiant recognises this file __commandName__ = 'ShiftTexturesRandomly' __commandDisplayName__ = 'Shift Textures randomly' # The actual algorithm called by DarkRadiant # is contained in the execute() function def execute(): import random s = random.randint(0, 256) t = random.randint(0, 256) for i in range(0, s+1): GlobalCommandSystem.execute('texshiftright "' + str(s) + '"') else: for i in range(0, t+1): GlobalCommandSystem.execute('texshiftup "' + str(t) + '"') else: print("texture translated over " + str(s) + " " + str(t)) # The variable __executeCommand__ evaluates to true # when DarkRadiant executes this command if __executeCommand__: execute() DarkRadiant-2.5.0/install/scripts/commands/shift_textures_upwards_randomly.py000066400000000000000000000011121321750546400277460ustar00rootroot00000000000000# Set the command name so that DarkRadiant recognises this file __commandName__ = 'ShiftTexturesUpwardsRandomly' __commandDisplayName__ = 'Shift Textures randomly upwards' # The actual algorithm called by DarkRadiant # is contained in the execute() function def execute(): import random s = random.randint(0, 256) for i in range(0, s+1): GlobalCommandSystem.execute('texshiftup "' + str(s) + '"') else: print("texture translated over " + str(s) ) # The variable __executeCommand__ evaluates to true # when DarkRadiant executes this command if __executeCommand__: execute() DarkRadiant-2.5.0/install/scripts/commands/test_targets.py000066400000000000000000000047011321750546400237330ustar00rootroot00000000000000"""Find any entities with "target" spawnargs that don't point to a valid entity in the map. Print results to the console, plus show a dialog and offer to select any entities that have missing targets. # 3718 Proposal: RJFerret Script: SteveL """ __commandName__ = 'test_targets' # should not contain spaces __commandDisplayName__ = 'Test for Missing Targets' # should not contain spaces def execute(): g_targets = {} # entityname : [targets] g_missing = {} # entityname : [missingtargets] import darkradiant as dr class TargetFinder(dr.SceneNodeVisitor): def pre(self, node): if node.isEntity(): n = node.getEntity() name = n.getKeyValue('name') targs = [t[1] for t in n.getKeyValuePairs('target')] g_targets[name] = targs return 1 # Instantiate a new walker object and get list of entities/targets walker = TargetFinder() GlobalSceneGraph.root().traverse(walker) # Find any targets that don't exist, and count all targets entities = g_targets.keys() targetcount = 0 for ent in entities: targetcount += len(g_targets[ent]) missing = [] for targ in g_targets[ent]: if targ not in entities: missing.append(targ) if missing: g_missing[ent] = missing # generate report msg = '%d entities found with %d targets' % (len(entities), targetcount) + '\n\n' if not g_missing: msg += 'No missing targets found' GlobalDialogManager.createMessageBox('Missing targets', msg, dr.Dialog.CONFIRM).run() else: msg += 'Missing targets:\n' for ent in g_missing.keys(): for targ in g_missing[ent]: msg += '%s -> %s\n' % (ent, targ) print(msg) # output to console msg += "\nThe list of missing targets has been printed to the console." msg += "\n\nDo you want to select all entities with missing targets?" response = GlobalDialogManager.createMessageBox('Missing targets', msg, dr.Dialog.ASK).run() if response == dr.Dialog.YES: class Selector(SceneNodeVisitor): def pre(self, node): if node.isEntity() and node.getEntity().getKeyValue('name') in g_missing.keys(): node.setSelected(True) return 1 GlobalSceneGraph.root().traverse(Selector()) if __executeCommand__: execute() DarkRadiant-2.5.0/install/scripts/dialogtest.py000066400000000000000000000013701321750546400215600ustar00rootroot00000000000000# Test creating a new dialog dialog = GlobalDialogManager.createDialog("Test") # Add a label dialog.addLabel("Testlabel") # Add an entry box and remember the handle entryHandle = dialog.addEntryBox("Entry") # Add a spin button dialog.addSpinButton("Spinner", 0, 10, 0.5) # Add a combo box, the options must be passed in the form of a StringVector options = StringVector() options.append("Test1") options.append("Test2") dialog.addComboBox("Test", options) # Add a simple checkbox dialog.addCheckbox("TestCheckbox") if dialog.run() == Dialog.OK: dialog = GlobalDialogManager.createMessageBox("Result", "User pressed OK, entry is: " + dialog.getElementValue(entryHandle) + "", Dialog.CONFIRM) dialog.setTitle("Result Popup Message") dialog.run() DarkRadiant-2.5.0/install/scripts/init.py000066400000000000000000000001661321750546400203660ustar00rootroot00000000000000# Called at DarkRadiant startup import sys print('DarkRadiant init.py called, this is Python {0}'.format(sys.version))DarkRadiant-2.5.0/install/scripts/patchtest.py000066400000000000000000000011541321750546400214200ustar00rootroot00000000000000# Test patch manipulation class PatchManipulator(SceneNodeVisitor) : def pre(self, node): # Try to get a patch from this node patch = node.getPatch() if not patch.isNull(): print('Patch information:') print('Dimensions: ' + str(patch.getWidth()) + 'x' + str(patch.getHeight())) mesh = patch.getTesselatedPatchMesh() print('Mesh dimension: w=' + str(mesh.width) + ', h=' + str(mesh.height)) for v in mesh.vertices: print('Mesh vertex: ' + str(v.vertex.x()) + ',' + str(v.vertex.y()) + ',' + str(v.vertex.z())) return 1 walker = PatchManipulator() GlobalSceneGraph.root().traverse(walker)DarkRadiant-2.5.0/install/scripts/test.py000066400000000000000000000203421321750546400204000ustar00rootroot00000000000000# Some interface tests # Test the Registry interface value = GlobalRegistry.get('user/paths/appPath') print(value) worldspawn = Radiant.findEntityByClassname("worldspawn") worldspawn.setKeyValue('test', 'success') print('Worldspawn edited') # Test the EClassManager interface eclass = GlobalEntityClassManager.findClass('atdm:func_shooter') print(eclass.getAttribute('editor_usage').getValue()) # Try creating a func_shooter entity if not eclass.isNull(): shooter = GlobalEntityCreator.createEntity(eclass) modelDef = GlobalEntityClassManager.findModel('tdm_ai_citywatch') print('ModelDef mesh for tdm_ai_citywatch = ' + modelDef.mesh) # Test iterating over C++ std::map for anim in modelDef.anims: print(anim + " = " + modelDef.anims[anim]) # Test implementing a eclass visitor interface #class TestVisitor(EntityClassVisitor) : # def visit(self, eclass): # print(eclass.getAttribute('editor_usage').getValue()) #eclassVisitor = TestVisitor() #GlobalEntityClassManager.forEachEntityClass(eclassVisitor) # Test implementing a model def visitor interface #class TestModelDefVisitor(ModelDefVisitor) : # def visit(self, modelDef): # print(modelDef.mesh) # #modelDefVisitor = TestModelDefVisitor() #GlobalEntityClassManager.forEachModelDef(modelDefVisitor) # Test traversing the scenegraph class SceneWalker(SceneNodeVisitor) : def pre(self, node): print(node.getNodeType()) # Try to get a model from this node model = node.getModel() if not model.isNull(): print('Node is a model') else: print('Node is not a model') return 1 walker = SceneWalker() GlobalSceneGraph.root().traverse(walker) # Test traversing the current selection class Walker(SelectionVisitor) : def visit(self, node): # Try to "cast" the node to a brush brush = node.getBrush() # If the Brush is not NULL the cast succeeded if not brush.isNull(): print(brush.getNumFaces()) else: print('Node is not a brush') # Try to cast the node to a patch patch = node.getPatch() # If the Patch is not NULL the cast succeeded if not patch.isNull(): print('Node is a patch') else: print('Node is not a patch') # Try to get a model from this node model = node.getModel() if not model.isNull(): print('Node is a model') else: print('Node is not a model') visitor = Walker() GlobalSelectionSystem.foreachSelected(visitor) print('Map name is ' + GlobalMap.getMapName()) # Try to find the map's worldspawn worldspawn = GlobalMap.getWorldSpawn() if not worldspawn.isNull(): # Cast the node onto an entity worldspawnent = worldspawn.getEntity() if not worldspawnent.isNull(): print('Spawnclass of worldspawn: ' + worldspawnent.getKeyValue('spawnclass')) else: print('There is no worldspawn in this map yet') # Test the entity visitor interface class TestEntityVisitor(EntityVisitor) : def visit(self, key, value): print('Worldspawn has spawnarg: ' + key + ' = ' + value) if not worldspawn.isNull(): tev = TestEntityVisitor() # Cast the node onto an entity worldspawnent = worldspawn.getEntity() worldspawnent.forEachKeyValue(tev) # Try to retrieve all keyvalues starting with "n" keyvalues = worldspawnent.getKeyValuePairs('t') for kv in keyvalues: print('Keyvalue ' + kv[0] + ' = ' + kv[1]) # Test the commandsystem GlobalCommandSystem.execute('texscale "0 0.1"') # Test the GameManager interface print('Mod path = ' + GlobalGameManager.getModPath()) game = GlobalGameManager.currentGame() print('Current game type: ' + game.getKeyValue('type')) print('VFS Search paths:') vfsPaths = GlobalGameManager.getVFSSearchPaths() for path in vfsPaths: print(path) # Test FileSystem (VFS) class TestFileVisitor(FileVisitor) : def visit(self, filename): print('Found file: ' + filename) filevisitor = TestFileVisitor() GlobalFileSystem.forEachFile('skins/', 'skin', filevisitor, 99) filecontents = GlobalFileSystem.readTextFile('skins/tdm_ai_guard_citywatch.skin'); print(filecontents) # Test the Grid Interface print('Current grid size = ' + str(GlobalGrid.getGridSize())) # Test the ShaderSystem interface class TestShaderVisitor(ShaderVisitor) : def visit(self, shader): if not shader.isNull(): print('Found shader: ' + shader.getName() + ' defined in ' + shader.getShaderFileName()) shadervisitor = TestShaderVisitor() GlobalMaterialManager.foreachShader(shadervisitor) shader = GlobalMaterialManager.getMaterialForName('bc_rat') if not shader.isNull(): print('Shader ' + shader.getName() + ' is defined in ' + shader.getShaderFileName()) # Test finding a model class ModelFinder(SceneNodeVisitor) : def pre(self, node): # Try to get a model from this node model = node.getModel() if not model.isNull(): print('Model information:') print('Filename: ' + model.getFilename()) print('Model path: ' + model.getModelPath()) print('Surface count: ' + str(model.getSurfaceCount())) print('Vertex count: ' + str(model.getVertexCount())) print('Poly count: ' + str(model.getPolyCount())) materials = model.getActiveMaterials() print('Active Materials:') for material in materials: print(material) for i in range(0, model.getSurfaceCount()): surface = model.getSurface(i) print('Surface: ' + str(i)) print(' Default Shader: ' + surface.getDefaultMaterial()) print(' Active Shader: ' + surface.getActiveMaterial()) print(' PolyCount: ' + str(surface.getNumTriangles())) print(' Vertex Count: ' + str(surface.getNumVertices())) s = Vector3(0,0,0) numverts = surface.getNumVertices() for v in range(0, numverts): meshvertex = surface.getVertex(v) s += meshvertex.vertex print(' Sum of all vertices: ' + str(s.x()) + ',' + str(s.y()) + ',' + str(s.z())) return 1 walker = ModelFinder() GlobalSceneGraph.root().traverse(walker) # Test the ModelSkinCache interface #allSkins = GlobalModelSkinCache.getAllSkins() # #for skin in allSkins: # modelskin = GlobalModelSkinCache.capture(skin) # print('Skin found: ' + modelskin.getName()) v = Vector3(6,6,6) v += Vector3(10,10,10) print(v) # Test patch manipulation class PatchManipulator(SceneNodeVisitor) : def pre(self, node): # Try to get a patch from this node patch = node.getPatch() if not patch.isNull(): print('Patch information:') print('Dimensions: ' + str(patch.getWidth()) + 'x' + str(patch.getHeight())) w = 0 while w < patch.getWidth(): h = 0 while h < patch.getHeight(): # Push the vertex around a bit ctrl = patch.ctrlAt(h,w) ctrl.vertex += Vector3(0,0,10*(h-w)) h += 1 w += 1 patch.controlPointsChanged() return 1 walker = PatchManipulator() GlobalSceneGraph.root().traverse(walker) # Test the SelectionSetManager interface class SelectionSetWalker(SelectionSetVisitor) : def visit(self, selectionset): print(selectionset.getName()) walker = SelectionSetWalker() GlobalSelectionSetManager.foreachSelectionSet(walker) selSet = GlobalSelectionSetManager.createSelectionSet("TestSelectionSet") selSet.assignFromCurrentScene() selSet.deselect() selSet.select() selSet.clear() if selSet.empty(): GlobalSelectionSetManager.deleteSelectionSet("TestSelectionSet") print('') soundshader = GlobalSoundManager.getSoundShader('tdm_ai_lady_alertdown_to_idle') if not soundshader.isNull(): print('Name of this sound shader: ' + soundshader.getName()) radii = soundshader.getRadii() print('Minimum radius in meters: ' + str(radii.getMin(1))) print('Maximum radius in meters: ' + str(radii.getMax(1))) fileList = soundshader.getSoundFileList() for i in range(0, len(fileList)): print(' Sound file used by this shader: ' + fileList[i]) if (len(fileList) > 0): GlobalSoundManager.playSound(fileList[0]) # Test SelectionGroup interface group = GlobalSelectionGroupManager.createSelectionGroup() print('Created group with ID: ' + str(group.getId())) # Test traversing the current selection class GroupAdder(SelectionVisitor) : def visit(self, node): group.addNode(node) visitor = Walker() GlobalSelectionSystem.foreachSelected(visitor) print('The group contains now ' + str(group.size()) + ' items') # Deselect the group GlobalSelectionGroupManager.setGroupSelected(group.getId(), 0) # List nodes in this group class SelectionGroupWalker(SelectionGroupVisitor) : def visit(self, node): print('Group Member: ' + node.getNodeType()) gropWalker = SelectionGroupWalker(); group.foreachNode(gropWalker)DarkRadiant-2.5.0/install/ui/000077500000000000000000000000001321750546400157745ustar00rootroot00000000000000DarkRadiant-2.5.0/install/ui/aboutdialog.fbp000066400000000000000000003015121321750546400207610ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject1 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY AboutDialogPanel -1,-1 wxTAB_TRAVERSAL bSizer1 wxVERTICAL none 12 wxALL|wxEXPAND 0 bSizer2 wxHORIZONTAL none 6 wxALL 0 1 1 1 1 Load From Art Provider; darkradiant:logo.png; 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_bitmap1 1 protected 1 Resizable 1 0 6 wxLEFT 1 bSizer3 wxVERTICAL none 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY DarkRadiant x.y.z 0 0 1 AboutDialogAppTitle 1 protected 1 Resizable 1 0 -1 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Build date: 2014-08-04 0 0 1 AboutDialogBuildDate 1 protected 1 Resizable 1 0 -1 6 wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY http://www.darkradiant.net/ This product contains software technology from id Software, Inc. ('id Technology'). id Technology 2000 id Software,Inc. DarkRadiant is originally based on the GPL version of GtkRadiant. The Dark Mod (www.thedarkmod.com) 0 0 1 m_staticText5 1 protected 1 Resizable 1 0 -1 12 wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT 0 bSizer4 wxVERTICAL none 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY wxWidgets Properties 0 0 1 AboutDialogHeader1 1 protected 1 Resizable 1 0 -1 12 wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Version: 0 0 1 AboutDialogWxWidgetsVersion 1 protected 1 Resizable 1 0 -1 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY OpenGL Properties 0 0 1 AboutDialogHeader2 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 1 bSizer5 wxVERTICAL none 6 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Vendor: 0 0 1 AboutDialogOpenGLVendor 1 protected 1 Resizable 1 0 -1 6 wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Version: 0 0 1 AboutDialogOpenGLVersion 1 protected 1 Resizable 1 0 -1 6 wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Renderer: 0 0 1 AboutDialogOpenGLRenderer 1 protected 1 Resizable 1 0 -1 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY OpenGL Extensions 0 0 1 AboutDialogHeader3 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,70 1 AboutDialogOpenGLExtensions 1 protected 1 Resizable 1 -1,70 wxTE_MULTILINE|wxTE_READONLY|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY DarkRadiant Modules 0 0 1 AboutDialogHeader4 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,70 1 AboutDialogDarkRadiantModules 1 protected 1 Resizable 1 -1,70 wxTE_MULTILINE|wxTE_READONLY|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator 12 wxALIGN_RIGHT|wxBOTTOM|wxRIGHT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_CLOSE OK 0 0 1 AboutDialogOkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/aboutdialog.xrc000066400000000000000000000130061321750546400210040ustar00rootroot00000000000000 wxVERTICAL wxALL|wxEXPAND 12 wxHORIZONTAL wxALL 6 undefined.png wxLEFT 6 wxVERTICAL wxBOTTOM 6 -1 wxBOTTOM 6 -1 wxTOP 6 -1 wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT 12 wxVERTICAL wxBOTTOM 6 -1 wxLEFT 12 -1 wxBOTTOM|wxTOP 6 -1 wxEXPAND|wxLEFT 12 wxVERTICAL 6 -1 wxTOP 6 -1 wxTOP 6 -1 wxBOTTOM|wxTOP 6 -1 wxEXPAND|wxLEFT 12 -1,70 wxBOTTOM|wxTOP 6 -1 wxEXPAND|wxLEFT 12 -1,70 wxALIGN_RIGHT|wxBOTTOM|wxRIGHT 12 0 DarkRadiant-2.5.0/install/ui/addpropertydialog.fbp000066400000000000000000000671321321750546400222130ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY AddPropertyDialogPanel 500,300 wxTAB_TRAVERSAL bSizer3 wxVERTICAL none 6 wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Description 0 0 1 m_staticText3 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 0 1 0 0 wxID_ANY 0 0 1 Description 1 protected 1 Resizable 1 -1,90 wxTE_MULTILINE|wxTE_READONLY|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator wxSTATIC_BORDER 5 wxALIGN_RIGHT 0 bSizer4 wxHORIZONTAL none 5 wxALL 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 CancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALL 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 OkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/addpropertydialog.xrc000066400000000000000000000030771321750546400222360ustar00rootroot00000000000000 500,300 wxVERTICAL wxLEFT|wxRIGHT 6 -1 wxALL|wxEXPAND 6 -1,90 0 wxALIGN_RIGHT 5 wxHORIZONTAL wxALL 5 0 wxALL 5 0 DarkRadiant-2.5.0/install/ui/camwnd.fbp000066400000000000000000000743141321750546400177470ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject1 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY CamWndPanel 500,300 wxTAB_TRAVERSAL camSizer wxVERTICAL none 0 wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT|wxTOP 0 bSizer2 wxHORIZONTAL none 3 wxBOTTOM|wxEXPAND|wxLEFT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 CamToolbar 1 1 protected 1 Resizable 5 1 wxTB_HORIZONTAL|wxTB_NODIVIDER 0 Load From Art Provider; darkradiant:wireframeMode16.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_RADIO wireframeBtn wireframeBtn protected Render in wireframe mode Load From Art Provider; darkradiant:solidMode16.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_RADIO flatShadeBtn flatShadeBtn protected Render in flat shaded mode Load From Art Provider; darkradiant:textureMode16.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_RADIO texturedBtn texturedBtn protected Render in fullbright textured mode Load From Art Provider; darkradiant:lightingMode.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_RADIO lightingBtn lightingBtn protected Render in lighting preview mode 3 wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 MiscToolbar 1 1 protected 1 Resizable 5 1 wxTB_HORIZONTAL|wxTB_NODIVIDER 0 protected Load From Art Provider; darkradiant:media-playback-start-ltr.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_NORMAL startTimeButton startTimeButton protected Start Render Time Load From Art Provider; darkradiant:media-playback-stop.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_NORMAL stopTimeButton stopTimeButton protected Stop Render Time protected Load From Art Provider; darkradiant:farClipIn.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_NORMAL clipPlaneInButton clipPlaneInButton protected Clip plane in Load From Art Provider; darkradiant:farClipOut.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_NORMAL clipPlaneOutButton clipPlaneOutButton protected Clip plane out DarkRadiant-2.5.0/install/ui/camwnd.xrc000066400000000000000000000074301321750546400177670ustar00rootroot00000000000000 500,300 wxVERTICAL wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT|wxTOP 0 wxHORIZONTAL wxBOTTOM|wxEXPAND|wxLEFT|wxTOP 3 1 5 Render in wireframe mode undefined.png 1 Render in flat shaded mode undefined.png 1 Render in fullbright textured mode undefined.png 1 Render in lighting preview mode undefined.png 1 wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP 3 1 5 Start Render Time undefined.png Stop Render Time undefined.png Clip plane in undefined.png Clip plane out undefined.png DarkRadiant-2.5.0/install/ui/conversationcmdeditor.fbp000066400000000000000000001756331321750546400231110ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ConvCmdEditorMainPanel 250,280 wxTAB_TRAVERSAL bSizer5 wxVERTICAL none 12 wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Actor 0 0 1 ConvCmdEditorActorLabel 1 protected 1 Resizable 1 0 -1 18 wxEXPAND|wxLEFT|wxRIGHT 0 bSizer11 wxHORIZONTAL none 6 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ConvCmdEditorActorChoice 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 12 wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Command 0 0 -1,-1 1 ConvCmdEditorCommandLabel 1 protected 1 Resizable 1 0 -1 18 wxEXPAND|wxLEFT|wxRIGHT 0 bSizer61 wxHORIZONTAL none 6 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ConvCmdEditorCommandChoice 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 12 wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Command Arguments 0 0 1 ConvCmdEditorCmdArgLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ConvCmdEditorArgPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL 3 wxBOTH 1 12 fgSizer1 wxFLEX_GROWMODE_SPECIFIED none 1 6 12 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Command Properties 0 0 1 ConvCmdEditorPropertiesLabel 1 protected 1 Resizable 1 0 -1 18 wxEXPAND|wxLEFT 0 bSizer611 wxHORIZONTAL none 3 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Wait until finished 0 0 1 ConvCmdEditorWaitUntilFinished 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 bSizer8 wxHORIZONTAL none 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 ConvCmdEditorCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 ConvCmdEditorOkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/conversationcmdeditor.xrc000066400000000000000000000076601321750546400231300ustar00rootroot00000000000000 250,280 wxVERTICAL wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT 18 wxHORIZONTAL 6 0 wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT 18 wxHORIZONTAL 6 0 wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT 12 1 3 6 12 1 wxALL 12 -1 wxEXPAND|wxLEFT 18 wxHORIZONTAL wxALL 3 0 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxLEFT 6 0 wxEXPAND|wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/conversationdialog.fbp000066400000000000000000002440431321750546400223660ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ConvDialogMainPanel 500,500 wxTAB_TRAVERSAL bSizer5 wxVERTICAL none 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Conversation Entities 0 0 1 ConvDialogEntityLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 1 bSizer61 wxHORIZONTAL none 6 wxEXPAND|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ConvDialogEntityPanel 1 protected 1 Resizable 1 -1,120 0 wxTAB_TRAVERSAL bSizer7 wxVERTICAL none 5 wxEXPAND 0 ButtonSizer1 wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 ConvDialogAddEntityButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 ConvDialogDeleteEntityButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Conversations 0 0 1 ConvDialogConvLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 1 bSizer6 wxHORIZONTAL none 6 wxEXPAND|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ConvDialogConversationPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer9 wxVERTICAL none 12 0 ButtonSizer wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 ConvDialogAddConvButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Edit 0 0 1 ConvDialogEditConvButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 ConvDialogDeleteConvButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Clear 0 0 1 ConvDialogClearConvButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 bSizer8 wxHORIZONTAL none 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 ConvDialogCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 ConvDialogOkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/conversationdialog.xrc000066400000000000000000000117011321750546400224040ustar00rootroot00000000000000 500,500 wxVERTICAL wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxRIGHT 6 -1,120 wxVERTICAL wxEXPAND 5 wxVERTICAL wxBOTTOM|wxEXPAND 6 80,-1 0 wxEXPAND 6 80,-1 0 wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxRIGHT 6 wxVERTICAL 12 wxVERTICAL wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxLEFT 6 0 wxEXPAND|wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/conversationeditor.fbp000066400000000000000000004476231321750546400224260ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ConvEditorMainPanel 500,650 wxTAB_TRAVERSAL bSizer5 wxVERTICAL none 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Properties 0 0 1 ConvEditorPropertyLabel 1 protected 1 Resizable 1 0 -1 18 wxEXPAND|wxLEFT|wxRIGHT 0 bSizer11 wxVERTICAL none 6 wxBOTTOM|wxEXPAND|wxTOP 1 bSizer12 wxHORIZONTAL none 5 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Name: 0 0 1 m_staticText4 1 protected 1 Resizable 1 0 -1 0 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ConvEditorNameEntry 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxLEFT 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Actors must be within talk distance 0 0 1 ConvEditorActorsWithinTalkDistance 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxLEFT|wxTOP 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Actors always face each other while talking 0 0 1 ConvEditorActorsMustFace 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 2 wxBOTTOM|wxEXPAND|wxTOP 0 bSizer13 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Let this conversation play 0 0 1 ConvEditorRepeatCheckbox 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 9999 0 -1 0 1 ConvEditorRepeatTimes 1 protected 1 Resizable 1 60,-1 wxSP_ARROW_KEYS 0 -1 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY times at maximum 0 0 1 ConvEditorRepeatAdditionalText 1 protected 1 Resizable 1 0 -1 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Actors 0 0 -1,-1 1 ConvEditorActorLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 1 bSizer61 wxHORIZONTAL none 6 wxEXPAND|wxLEFT|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,160 1 ConvEditorActorPanel 1 protected 1 Resizable 1 -1,160 0 wxTAB_TRAVERSAL bSizer7 wxVERTICAL none 5 wxEXPAND 0 ButtonSizer1 wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 ConvEditorAddActorButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 ConvEditorDeleteActorButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND|wxTOP 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Validate all 0 0 1 ConvEditorValidateActorsButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Commands 0 0 1 ConvEditorCommandLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 1 bSizer6 wxHORIZONTAL none 6 wxEXPAND|wxLEFT|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,200 1 ConvEditorCommandPanel 1 protected 1 Resizable 1 -1,-1 0 wxTAB_TRAVERSAL bSizer9 wxVERTICAL none 12 0 ButtonSizer wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 ConvEditorAddCommandButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Edit 0 0 1 ConvEditorEditCommandButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Move up 0 0 1 ConvEditorMoveUpCommandButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Move down 0 0 1 ConvEditorMoveDownCommandButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 ConvEditorDeleteCommandButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 bSizer8 wxHORIZONTAL none 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 ConvEditorCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 ConvEditorOkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/conversationeditor.xrc000066400000000000000000000211621321750546400224350ustar00rootroot00000000000000 500,650 wxVERTICAL wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT 18 wxVERTICAL wxBOTTOM|wxEXPAND|wxTOP 6 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxALL 5 -1 wxALIGN_CENTER_VERTICAL 0 wxBOTTOM|wxLEFT 6 0 wxBOTTOM|wxLEFT|wxTOP 6 0 wxBOTTOM|wxEXPAND|wxTOP 2 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxLEFT 6 0 wxALIGN_CENTER_VERTICAL|wxLEFT 6 60,-1 0 -1 9999 wxALIGN_CENTER_VERTICAL|wxLEFT 6 -1 wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxLEFT|wxRIGHT 6 -1,160 wxVERTICAL wxEXPAND 5 wxVERTICAL wxBOTTOM|wxEXPAND 6 80,-1 0 wxEXPAND 6 80,-1 0 wxEXPAND|wxTOP 6 80,-1 0 wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxLEFT|wxRIGHT 6 wxVERTICAL 12 wxVERTICAL wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxLEFT 6 0 wxEXPAND|wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/difficultyeditor.fbp000066400000000000000000003560171321750546400220520ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY DifficultyEditorMainPanel -1,-1 wxTAB_TRAVERSAL bSizer17 wxVERTICAL none 12 wxALL|wxEXPAND 1 bSizer18 wxHORIZONTAL none 0 wxEXPAND 1 bSizer19 wxVERTICAL none 5 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,-1 1 DifficultyEditorTreeViewPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL 350,120 bSizer21 wxVERTICAL none 6 wxEXPAND|wxTOP 0 DifficultyEditorButtonPanel wxHORIZONTAL none 5 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 DifficultyEditorAddSettingButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT|wxRIGHT 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 DifficultyEditorDeleteSettingButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Refresh 0 0 1 DifficultyEditorRefreshButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxEXPAND|wxLEFT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 DifficultyEditorSettingsPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL 300,-1 bSizer22 wxVERTICAL none 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Setting 0 0 1 DifficultyEditorSettingLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 0 2 wxBOTH 1 0 fgSizer3 wxFLEX_GROWMODE_SPECIFIED none 3 0 6 wxALL|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Classname: 0 0 1 m_staticText11 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 DifficultyEditorClassName 1 protected 1 Resizable -1 1 wxCB_DROPDOWN|wxCB_SORT 0 wxFILTER_NONE wxDefaultValidator 6 wxALL|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Spawnarg: 0 0 1 m_staticText12 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 DifficultyEditorSpawnarg 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALL|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Argument: 0 0 1 m_staticText13 1 protected 1 Resizable 1 0 -1 6 wxEXPAND 1 bSizer23 wxHORIZONTAL none 6 wxTOP|wxBOTTOM|wxLEFT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 DifficultyEditorArgument 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 DifficultyEditorApplicationType 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALL|wxALIGN_RIGHT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Save 0 0 1 DifficultyEditorSaveSettingButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 DifficultyEditorNoteText 1 protected 1 Resizable 1 wxST_NO_AUTORESIZE 0 -1 DarkRadiant-2.5.0/install/ui/difficultyeditor.xrc000066400000000000000000000144261321750546400220720ustar00rootroot00000000000000 wxVERTICAL wxALL|wxEXPAND 12 wxHORIZONTAL wxEXPAND 0 wxVERTICAL wxEXPAND | wxALL 5 350,120 wxVERTICAL wxEXPAND|wxTOP 6 wxHORIZONTAL 5 0 wxLEFT|wxRIGHT 6 0 5 0 wxEXPAND|wxLEFT 12 300,-1 wxVERTICAL wxBOTTOM 6 -1 wxEXPAND 5 3 2 0 0 1 wxALL|wxALIGN_CENTER_VERTICAL 6 -1 wxALL|wxEXPAND 6 wxALL|wxALIGN_CENTER_VERTICAL 6 -1 wxALL|wxEXPAND 6 wxALL|wxALIGN_CENTER_VERTICAL 6 -1 wxEXPAND 6 wxHORIZONTAL wxTOP|wxBOTTOM|wxLEFT 6 wxALL 6 0 wxALL|wxALIGN_RIGHT 6 0 wxALL|wxEXPAND 5 -1 DarkRadiant-2.5.0/install/ui/entityclasschooser.fbp000066400000000000000000001225121321750546400224150ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject1 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY EntityClassChooserMainPanel 500,300 wxTAB_TRAVERSAL bSizer1 wxVERTICAL none 12 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 0 1 EntityClassChooserSplitter 1 protected 1 Resizable 0.0 300 -1 1 wxSPLIT_VERTICAL wxSP_3D 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 EntityClassChooserLeftPane 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer2 wxVERTICAL none 6 wxEXPAND|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,90 1 EntityClassChooserUsageText 1 protected 1 Resizable 1 -1,90 wxTE_MULTILINE|wxTE_READONLY|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 EntityClassChooserRightPane 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer4 wxVERTICAL none 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT 0 bSizer3 wxHORIZONTAL none 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_CANCEL Cancel 0 0 1 EntityClassChooserCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ADD Add 0 0 1 EntityClassChooserAddButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/entityclasschooser.xrc000066400000000000000000000042501321750546400224400ustar00rootroot00000000000000 500,300 wxVERTICAL wxALL|wxEXPAND 12 300 0 0 vertical wxVERTICAL wxEXPAND|wxTOP 6 -1,90 wxVERTICAL wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT 12 wxHORIZONTAL wxALL|wxEXPAND 5 0 wxALL|wxEXPAND 5 0 DarkRadiant-2.5.0/install/ui/exportasmodeldialog.fbp000066400000000000000000002533231321750546400225430ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject3 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY -1,-1 ExportDialogMainPanel 560,-1 wxTAB_TRAVERSAL bSizer23 wxVERTICAL none 12 wxALL|wxEXPAND 1 bSizer13 wxVERTICAL none 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Output File 0 0 1 ExportDialogOutputLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 0 2 wxBOTH 1 12 fgSizer1 wxFLEX_GROWMODE_SPECIFIED none 2 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Output Format: 0 0 1 m_staticText8 1 protected 1 Resizable 1 0 -1 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 "ASCII Scene Export (.ase)" 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ExportDialogFormatChoice 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY File Path: 0 0 1 m_staticText9 1 protected 1 Resizable 1 0 -1 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 Select a file 0 1 ExportDialogFilePicker 1 protected 1 Resizable 1 wxFLP_DEFAULT_STYLE 0 wxFILTER_NONE wxDefaultValidator *.* 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Export Options 0 0 1 ExportDialogOptionLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 0 bSizer20 wxVERTICAL none 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Center Objects around Origin 0 0 1 ExportDialogCenterObjects 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Skip Surfaces textured with Caulk 0 0 1 ExportDialogSkipCaulk 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Replace Selection with exported Model 0 0 1 ExportDialogReplaceWithModel 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Use Entity Origin as export Origin 0 0 1 ExportDialogUseEntityOrigin 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxALIGN_RIGHT|wxLEFT|wxTOP 0 bSizer9 wxHORIZONTAL none 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 -1,-1 1 ExportDialogCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Export 0 0 -1,-1 1 ExportDialogExportButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/exportasmodeldialog.xrc000066400000000000000000000116021321750546400225600ustar00rootroot00000000000000 560,-1 wxVERTICAL wxALL|wxEXPAND 12 wxVERTICAL wxBOTTOM 6 -1 wxEXPAND|wxLEFT 12 2 2 0 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxALL|wxEXPAND 5 0 ASCII Scene Export (.ase) wxALIGN_CENTER_VERTICAL 0 -1 wxALL|wxEXPAND 5 Select a file *.* wxBOTTOM|wxTOP 6 -1 wxEXPAND|wxLEFT 12 wxVERTICAL wxBOTTOM|wxTOP 6 0 wxBOTTOM|wxTOP 6 0 wxBOTTOM|wxTOP 6 0 wxBOTTOM|wxTOP 6 0 wxALIGN_RIGHT|wxLEFT|wxTOP 12 wxHORIZONTAL wxLEFT 6 0 wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/filterdialog.fbp000066400000000000000000001604121321750546400211360ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY FilterDialogMainPanel 500,300 wxTAB_TRAVERSAL bSizer5 wxVERTICAL none 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Filters 0 0 1 FilterDialogTopLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 1 bSizer6 wxHORIZONTAL none 6 wxEXPAND|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 FilterDialogTreeViewPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer9 wxVERTICAL none 12 0 ButtonSizer wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 FilterDialogAddButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY View 0 0 1 FilterDialogViewButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Edit 0 0 1 FilterDialogEditButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 FilterDialogDeleteButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 bSizer8 wxHORIZONTAL none 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 FilterDialogCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 FilterDialogOkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/filterdialog.xrc000066400000000000000000000062501321750546400211620ustar00rootroot00000000000000 500,300 wxVERTICAL wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxRIGHT 6 wxVERTICAL 12 wxVERTICAL wxBOTTOM|wxEXPAND 6 0 wxBOTTOM|wxEXPAND 6 0 wxBOTTOM|wxEXPAND 6 0 wxEXPAND 6 0 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxLEFT 6 0 wxEXPAND|wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/filtereditor.fbp000066400000000000000000002515501321750546400211710ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY FilterEditorMainPanel 500,300 wxTAB_TRAVERSAL bSizer5 wxVERTICAL none 12 wxALL|wxEXPAND 1 bSizer10 wxVERTICAL none 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Name 0 0 1 FilterEditorTopLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 FilterEditorNameEntry 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP|wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Rules 0 0 1 FilterEditorRuleLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND 1 bSizer6 wxHORIZONTAL none 6 wxEXPAND|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 FilterEditorTreeViewPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer9 wxVERTICAL none 12 0 ButtonSizer wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 FilterEditorAddButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Move Up 0 0 1 FilterEditorUpButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Move Down 0 0 1 FilterEditorDownButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 FilterEditorDeleteButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Filter rules are applied in the shown order. "Match" is accepting regular expressions. "Object"-type filters can be used to match "patch" or "brush". 0 0 1 FilterEditorHelpText 1 protected 1 Resizable 1 0 -1 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 bSizer8 wxHORIZONTAL none 0 wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 FilterEditorOkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 FilterEditorCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Save 0 0 1 FilterEditorSaveButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/filtereditor.xrc000066400000000000000000000112151321750546400212060ustar00rootroot00000000000000 500,300 wxVERTICAL wxALL|wxEXPAND 12 wxVERTICAL wxBOTTOM 6 -1 wxEXPAND|wxLEFT 12 wxTOP|wxBOTTOM 6 -1 wxEXPAND 12 wxHORIZONTAL wxEXPAND|wxRIGHT 6 wxVERTICAL 12 wxVERTICAL wxBOTTOM|wxEXPAND 6 0 wxBOTTOM|wxEXPAND 6 0 wxBOTTOM|wxEXPAND 6 0 wxEXPAND 6 0 wxALL 5 -1 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND 0 0 wxEXPAND|wxLEFT 6 0 wxEXPAND|wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/findandreplacedialog.fbp000066400000000000000000002420621321750546400226120ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject1 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY 500,200 FindReplaceDialogMainPanel 500,200 wxTAB_TRAVERSAL bSizer1 wxVERTICAL none 12 wxALL|wxEXPAND 0 4 wxBOTH 1 0 fgSizer1 wxFLEX_GROWMODE_SPECIFIED none 2 3 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Find: 0 0 1 m_staticText1 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 FindReplaceDialogFindEntry 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 Load From Art Provider; darkradiant:folder16.png; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY MyButton 0 0 1 FindReplaceDialogFindSelectButton 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 5 wxALL 0 1 1 1 1 Load From Art Provider; darkradiant:texture_pick.png; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 FindReplaceDialogFindPickButton 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Replace: 0 0 1 m_staticText3 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 FindReplaceDialogReplaceEntry 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 Load From Art Provider; darkradiant:folder16.png; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY MyButton 0 0 1 FindReplaceDialogReplaceSelectButton 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 5 wxALL 0 1 1 1 1 Load From Art Provider; darkradiant:texture_pick.png; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 FindReplaceDialogReplacePickButton 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 12 wxLEFT 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Search current selection only 0 0 1 FindReplaceDialogSearchCurSelection 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxALL|wxEXPAND 0 bSizer3 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY x shader(s) replaced. 0 0 200,-1 1 FindReplaceDialogStatusLabel 1 protected 1 Resizable 1 200,-1 0 -1 0 wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 bSizer2 wxHORIZONTAL none 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Find and Replace 0 0 1 FindReplaceDialogFindButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Close 0 0 1 FindReplaceDialogCloseButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/findandreplacedialog.xrc000066400000000000000000000131041321750546400226300ustar00rootroot00000000000000 500,200 wxVERTICAL wxALL|wxEXPAND 12 2 4 3 0 1 wxALIGN_CENTER_VERTICAL 0 -1 wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND 6 wxALIGN_CENTER_VERTICAL 0 undefined.png 0 wxALL 5 undefined.png 0 wxALIGN_CENTER_VERTICAL 0 -1 wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND 6 wxALIGN_CENTER_VERTICAL 0 undefined.png 0 wxALL 5 undefined.png 0 wxLEFT 12 0 wxALL|wxEXPAND 12 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxRIGHT 6 200,-1 -1 wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 wxHORIZONTAL wxLEFT 6 0 wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/fonts/000077500000000000000000000000001321750546400171255ustar00rootroot00000000000000DarkRadiant-2.5.0/install/ui/fonts/FreeMono.ttf000066400000000000000000012376541321750546400214000ustar00rootroot00000000000000 FFTMT x?GDEFyXk18PGPOS68vGSUBLprep\ɻyBH@7 h_< ȸ0'ȸ0'8  8ZX  xX/]1 @ GNU @  8 `XdXXXXX\XqXWXiXX&XXqXHXXHXXqXqXqXTX`XiX`XXiXqXXXXHX3XNXXiX X+X?X+X+X+X?X5XqXTX+X?X XX3X+X3X+X\XHX(X XX(X3XgXXqXXqXXXHXXTX?X?XiX?X+X\XX?X\X X5XHXX?XTXgX+X+XXX3X3XsXXXX\XXXqX?XgX3XXBXXXX?XHXHXXXXHXXXX+XOXXXXX?XXXXqX X X X X X X X?X+X+X+X+XqXqXqXqXXX3X3X3X3X3XvX(X(X(X(X(X3X+XXHXHXHXHXHXHX XTX?X?X?X?X\X\X\X\XHX5XHXHXHXHXHXHX5X+X+X+X+X3XX3X XHX XHX XHX?XTX?XTX?XTX?XTX+X?XX?X+X?X+X?X+X?X+X?X+X?X?X?X?X?X?X?X?X?X5X+X5X+XqX\XqX\XqX\XqX\XqX\XX|XTXX+X?XFX?X\X?X\X?X\X?X4X+X\XX5XX5XX5XX5X5X3XHX3XHX3XHX X X+XTX+XTX+XTX\XgX\XgX\XgX\XgXHX+XHX+XHX+X(X+X(X+X(X+X(X+X(X+X(X+XXX3X3X3XgXsXgXsXgXsXiXXX,XX.XX?X?XTXXX:XXDX+X2X\XXWX?X0XXXqX+X?X\X:X XX5X3X3XHXXXXX<X\XgXfXQX+XX+XHX(X+XNX+XX3XgXsX`XPX_XXXTXPX_XXXXX3XXXXXXX|XXX X XHXqX\X3XHX(X+X(X+X(X+X(X+X(X+X?X XHX XHX X X?X?X?X?X+X?X3XHX3XHX`X_XXXXX?X?XXXX5X XHX X X(X5X XHX XHX+X?X+X?XdX\XqX\X3XHX3XHX$XTX+XTX(X+X(X+X\XgXHX+XpX|X5X+XXgXsX XHX+X?X3XHX3XHX3XHX3XHX3X3XXHX?XXXTXTX?X?X?X?XCXgXgXgXLXX?X?XMX4X2X+X3X3X\XXX\X\XXX X X XX5X6XHX X+XpXTXTX4XTXiXTXTX<X<XgXWXWXWXQX+X+X+XFXXXX3XQXsXsX_XpXXXX?XHXIXgXMXSXX?X\X?XXX)XX XXXXXX%XXHX XXzXXXXWXwXfX{XXXXXXX,XXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXX2X2X2X2X2XdXZXX7XdXXXXXXXXXXXXXXXxXxX;CC9CC4twCCCCG]nkXU9zA7J;N:<LFE!A6|9;ARAOBnQmb wBXXXXXXXXXXX XXXXX X+X>X X+XgX5X3XqX+X X XXEX3X3X+XfXHX3X.X(XXFXqX3X?XX5XX+X?X<X=XDXXzX5XqXXFX:X+X5X|XHXRXX^X+X+X+X%XX"X+XZX+XHX+X+XpXBX0XX0XQX+XPXYX+X'XdX3XX+X+XX>X@X\XqXqXTXX XX-X3X2X3X X,X+X>X X+XX\X3X3X-X9X X5X3X3X+X?XHX2XX(X2XIXXXX X+X@XX XHXQXlXrX7X?X XgXAXAXFXXX4XAXHXAXXTXfX3XX3X5X`X X X@X XlXJXXXX?X?X+XrXJXgX\X\XX"XX+XFXAX3X>XX+XXXX X XXX&XX&XX"X3X XX XXX+(GnX3XAX+XlX+XX>XrX>XrX>XrXX X\XgX$XFX$XFX$XFXX X5XRX5XRXXX?XTX?XTXHXzX3XX3XX(X3XXXJX`XJX`XJX`XXXXXqXX X$XFX9XXX5XRX5XAXJX`X X4XqX XHX XHX X X+X?X2XLX2XLXX X\XgX`X_X3XAX3XAX3XHX3XHX3XHX@XJX2X3X2X3X2X3XIX`X>XrX X X\XgX9XXX3X?XXX-XFX)X)X+XX"X"X:X)X X5XX?XX.X)XkX5XX!XXZXX=X*X+XX@X X)X X\X*XAX\XGXX3XZXXXXXXXX X4X+X!X,X+X}X5XX5X5XIXXGX8X5XGXBXPX6X[XXGX5X]X XGX5X,X,XgX5X?XyX XiXHXZX,XXXdX1XX<X<XX<X<XXX<XXX<X<X<X<X<XXX<X<X<X<X<X2X1X<X3XXZXZXZXXXfXjX]X5XdXdXAX@XgXcX5XfXcX:XdX]X5XjX5X5X4XbX5XhX@XjX5XfXPXLXfX2XjX.XnXbXuXWX]XeXX+X+XHXX X\XHX3XJX2X XTX+XXhXXhX>X4XXBXHX@X X2X#XX3XJX*X3X9X?XXJXgXXXHXX5X XXhX(XXTX+X\XLX&X2X XMX;XHXZX X\X)X2X?X?X?XX4X+X2X&X(X+X(X X#XX3X3XHXQXX>X<X2XX+XXXGXGXGXGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXzX3XXXXXX~XXXXXXXXXXXXnXX3XXXXiXXXXiXXMXXXXX3XXXXVXXXXvX3XZX XHX+XX+XX+XX?XTX+X?X+X?X+X?X+X?X+X?X+X?X+X?X+X?X+X?X+X?X+XiX?X?X5X+X5X+X5X+X5X+X5X+XqX\XqX\X+X?X+X?X+X?X?X\X?X\X?X\X?X\X X X X X X XX5XX5XX5XX5X3XHX3XHX3XHX3XHX+XX+XX+XTX+XTX+XTX+XTX\XgX\XgX\XgX\XgX\XgXHX+XHX+XHX+XHX+X(X+X(X+X(X+X(X+X(X+X XX XXXXXXXXXXXX(X3X(X3X3X3XgXsXgXsXgXsX+X+XX3XHXiX XHX XHX XHXXX XHX XHX XHX XHX XHX XHX XHX XHX+X?X+X?X+X?X+X?XXXX&X+X?X+X?XqX\XqX\X3XHX3XHX3XHXXXX0X3XHX3XHX3XHX3XHX3XHX3XHX3XHX(X+X(X+X(X+X(X+X(X+X(X+X(X+X3X3X3X3X3X3X3X3X?X?X?X?X?X?X?X?XXXXXXXXXXXXXXXXXXX~XX5X5X5X5X5X5X5X5XXXXXXXXXXXfXiXXXOXoXXXXXXXXXHXHXHXHXHXHXX XXXXX+X+X+X+X+X+X+X+XXXXX+X+X+X+X+X+X+X+XXXXXXXXX?X?XXX5X5XXXHXHX+X+X+X+X?X?X?X?X?X?X?X?XXXXxXXXXX5X5X5X5X5X5X5X5XXXXXXXXX+X+X+X+X+X+X+X+XXXXXXXXX?X?X?X?X?X?X?X X XXX XXXXXX5X5X5X5X5XXXXX3XXXXjXjXfXX`XZXqXqXXXXXX+X+X+X+XXX+X+X3X3XXXXXXX+X+X+X+X+XXXXXFXXXXXXXXXXXXXXXXXHXHXXXXXXXX2XBX]XVXX|X|XXXXX3XXXXXXXXXX"XXXiXXXiXXX?X%X=XpXXXdXdXXXHX2XXXX&XXiXOXQXQXqXXdXqXjXXdXHXhXXXXXXHXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXX?X?X+X?X XXXXX<X?X X+XHXgX+X?X X>X? 6@)XXX?XX?X X X\X+XX!X"X"X1XVXVXuXXX XX4X+X3X(X#X+X XXX1XFXFXX+X X?X+X<X<XX<X>XXX2X7X7X3XjXXbXXXXXXXXXXXX8XXqX1X!X%X+X%X XXX(XXX?X?X+X X\XOXX XXX+XX?X3X"X"X\XTX?X X)XX6XXXXpX{XxXX(X(XMXMX(XX(XX)X>X*XX*XXX)XFX)XFXXXXiXXiXX*X)X(X(X+X&X;X9X!X!XXX!X1XXX)X+X)X)X*XJX*X!X!X8XX8X8XX2XXXX,X*XX*XXX X?XX+X+X#X+X+X&X&XsX&X&XsXX3X3XqXHXHXHX`X`XqXXXXXX%XXXFX1XCXXXXX<X<XGXIXX8XXnX8XXnXnXnX;X;XX<XHXHX<X\X\X\XXfXX\XJXJXHX3X3X3X\X\XJX\X3XdX3X3X3X3X3X3X.X.X3X3X3X3X3X3X3X3XX3X3X3X3X3XAXNX3X3X%X%XXXXdXHXNX+X;XHXNXHXNXXXXXHXFXHXFXHXFXHXFX3X3X3X3X3X3X3X3X3X3XIXIXIXJX>XJX>XHXHXXXXX#X#X#X#X#X2X2X2X2X3X'XXXXX3XwXIX"X3X3XyX"X{X{X_XyXHXHXXX\X4XdX<X<X<XXX-X-X+X+XXXXHX\X\X\XXXLX8X8X3X3XGXGXxX3XNXNXXXXX8X8XFXHXHXFX4X3X3X3XHXNXHXFX_X`XHXHXXCX$XX7X2XXXXXX"XX"XX(XX(XHX#XX7XXHXX'XX'XXYXdXdXX XX>XXXX+XXPXXXXXX#X#XHXHXXXXX)XIXXX#XX+XXXXHXXX+XXXXHXXX+XXX#XGXX#XXX+XXX#XHX3X\XNXHX+X\X\XXXXJXJXXX+X?XXX+X?XXXXX<XXXXX2XXXXXXXXXX#X#X#X#XXXXXXXXXTXTXTXQXXQX,X,X,XdXXdXX,XXXcXXcXXX<X<X X XXXX"XXXXXXX(X(X(XXXXXXXWXWXXXXX@XXXXXX+XX XX,X8X.X#X#X-XX&XXjXsXXXuXhXoX~X+X!X+X+X+X"X$X#XXrX!X#XaX_XiXZXX!X(XXXjXjXjXjX XX X#X XbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXX,XXXXXXXXXXXXXXXXXXXXXX,XXXXX XX,XXXXXX,XXX2X2X2X2X2X2X2X2X2X2XXX2X2XXXXX#X#XXXTXTXXXyXyX#X#XXX9X9XXX^X_X#X#X#X#XYX#X#X#X#X#X#X#X#X#X#X#X#X,XXXXX#X,X,X#X#X#X2X2X2X2XX2X2X2X2X2X#X#X#XX2X2X2X2X#X#X#X#X2X2X2X2X2X}X}X2XX/X<XXXXxXZX#X2X2X2X&X2X&X2X2X8XPXPXX#X#X2X2X2X2X2X2X2X2X#X#X#XXX7XvXvXvX@XX8XXXVXX:XXXXXXXX XXXHX,XTXXHX,XQXXXX6X6XmXxX=XJXJXJXJXJXJXX#XXXXXX-X X X[XMX#X-XXYXYXXXX X XXX}X}X-X-XXX X X X X X X X XXlXlXlXkXkXkXkXpXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXlXlXlXlXkXkXkXkXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXpXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X(X(X(XEX3X?X?X?X@XX*XXX?X>X?X?XXX2X2X2X2X#X#X#X#XXXXXXX+X#XXXYXYXxXxXXXXXXcXXXXXXSXSXSXSXSXSXSXSXSXSXSXSXSXSXUX)XSXSXSXSXSXSXSXSXSXSXSXSXSXSXSXSXSXSXSXSXSXS#'GGXS)MG BZ<<<<<<<<H3131<<<<<<<<<<<<<21<3<<<<""~ 37IMZuz~dhmrw}V_EMWY[]} d q ! !!!"!$!'!+!.!2!;!D!K!N!!!!!"#######'#,#z#####$!$$$J$i&& &&&&&)&7&S&g&o&''''''(***?+ ++++T./6<>ADO $7PKPtz~fjpt|1Ya HPY[]_ p t !! !! !$!&!)!.!2!5!A!K!M!S!!!!"########)#6#|####$$#$@$`%&&&&&&(&.&9&`&i&''''''(***?++++S.8>@CF|{zyxub`VUTRQMHF0*]wuthgMJGFED410/.-,*'% {f_^[OIHEE/NJI"`ed}    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkQvj As E Fgw9 l|"cnT )m}b : yjq|}~zk,K*PXJvY#?+X=YK*PX}Y ԰.-, ڰ +-,KRXE#Y!-,i @PX!@Y-,+X!#!zXYKRXXY#!+XFvYXYYY-, \Z-,"PX \\Y-,$PX@\\Y-, 9/- , }+XY %I# &JPXea PX8!!Ya RX8!!YY- ,+X!!Y- , Ұ +- , /+\X G#Faj X db8!!Y!Y- , 9/ GFa# #JPX#RX@8!Y#PX@e8!YY-,+X=!! ֊KRX #I UX8!!Y!!YY-,# /+\X# XKS!YX&I## I#a8!!!!Y!!!!!Y-, ڰ+-, Ұ+-, /+\X G#Faj G#F#aj` X db8!!Y!!Y-, %Jd# PX<Y-,@@BBKcKc UX RX#b #Bb #BY @RX CcB CcB ce!Y!!Y-,Cc#Cc#-KPXYF+X!YKRX!Y+\X E+D E+DY+3))((((hl(T(h\  ( l h d X   \`hH`D(d8 tD,t,hx4 L !|!"$""#$#x##$d$%&@&x'8'|($()),)<)**T*+++,,,--L--./l0\00011,1D1\12222233(3@3X3344404H4`45X5p55556<6677747L7d8,8899909H9`9x9:0:H:`:x:::; ;;;;<< <<<<<==>P>h>>>>>>??(?@?P??@@@4@L@d@AAAAABBB4BLBdB|BBCPCCDD(D@DXDpDE\EtEF<FFFGGG,GDG\GtGGGGGHdHHIII0IHI`IxIJxJJJJJKKL(L@LXLpLLLLLMMMNNNOPOhOOPP|PPPPPQ Q$Q<QTQlRRRRRRSS S8SPShSSSTU U0UV0VW4WXXXhXYTYYZDZTZ[d[\|]]]^X^__`aabbbccddeefLffgdghXhi4ijDjk8klHlmmnno8op<pq0qqrr\rlrrrs\stTuuvXvpvvvvvwww0wHxx(xy yyzPzhzzzzz{x|| |8|P|h||||||}d}~t4@Xp8Xp0H`x 8Ph(@X4 8d| $<T @`H<Xl@(\X,Th|X Tt XD<<$l4|4DX00@`x Td 8dP<P 0DXl Pd0t`X<d” Hxô<PĐĤĸ$HpńŘ8L`tƈƜ`tLjǜǰD`tȈȜȰ(<Ɉɜɰ(`tʈʜp˄˘ˬ,@Ť,͔ͨ0DTh|ΐά0DXtψϜϰ,@ЄД<TфќѴ,<LҐ ӌӜӬ$Դ,<ՄՔդ<Lt׌פ׼؀xH l@PݬL\0߸|@((@`<T |H0H`xLt$4\l,Ph`p$ 0|pt8xx XhllPD@(8H`p(@Xp  x 0  8  |  `p(@Xx`$dxL$8\XX\ !,!"\"#H#$$h$%\%&d&'D'(H()h)*d++,d-$-4-L-d.../h/0|112233x33344 404@4X4p4455,5D5\5t5555556 6$6<6677 787P7h77777788\8889x::;;(;8;H;X<<=D=>0>?(??@`AAxAB<BC(CDDDEdEFhFGlGH\HIxIJTJJK<KLLM@MPMNNN$NdNtNNO|OPhPQ\QRRxRSdSTTUUV VWWWX4XY YYZlZ[p[\ \0\\\]]^^___\_`0`aaDaab bPbxbcc,c`cccddPdddePefffgghh<hhiPij<jtjkLkl4lmmmnHnoPopp`ppqqrHrsDstu u\uvvwtwx\xyyz{@{|p} }~~,|H@$8L`p,  0@4(8@DL \LxllTPdx,dP`p,p|\t,<lD \8h8\@L4\ @@p<h8H(T$\ Xt(Ph@,D\t4d|@X4Ld|”¬ $<TlÄÜL $<T,D\tƌƤƼ4Ld|ǔǬ $<TlȄȜȴd|4Lxː˨ 8Ph̘̰̀,D\tόϤϼ4Ld|H`(@Xp҈ҠҸ0H`xӐӨ 8PhԀԘ԰(@XpՈՠո44Ld|۔۬ $<(@Xp݈ݠݸ0H`xސި\tߌߤ߼4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld|(`\t4Ld| $<Tl$<Tlhx(@Xp ,T|H|$D0Hh0D0xl ( t    T p       @ p     4 4 4 4 4 4 4    D l  4H\p8h|x4`0044@ D !"X"l"""""# #d##$0$D$%%%%&&$&8&L&`&'()`**+,,-P-.x/ /0l01X122334t45x6x7789:0:::::::;;;(;8;H;;<<<=0=D=X=|====>> >@>`>>>>???(?|?@H@A ABBBC8CCCDDD$DEEEFFG(GGHxI$I4IDITIdIJJLJKK|KL4LLMlMNlNOlOOPPPQPQRRdRS`STdTUVTVWWlWWX,XXYtYZ[ [[\\L\\] ]L]^^4^_L_`l`a`abLbcdcd$ddelefdfffgg|gghphi8iijjdkkhk|l(l<lLllm@mhm|mmnnn0n@nxo$op ppq(qrTrxrrs\sxssstLtuvwy4z{{| |4|P|||||}}\}~8~~8Ph$<h  0L|`0Hd0Dx@Xp< 8Pl $p4dxPpTlLd0ph((L`4H<Tldx(<,DX<Ph$Xh<$ (<XxDl($h|dd(@Xp(44 8P<Tlh,D\tÌäü4Ĉ 8`Ǩ,<L\tȌȤȼɨXʠʼ(̐HxϸДhш4xҌH|ӤԨtHdלװ$H\l؀ذ0L`٬(`ڤ,Lۀۜ۸$T<ݸ|`pXl<P,T|4TtDl 4\| ,Ll(dT Hh,`@T4PlH0Pp0Pp0X,X$LxHp8d 8` PL@p@hDx0\0` 8<Ht<Xt$Lp4Pl  ( @ \  lPl,LlXt  4Px@\ Ld Dd8hp\  d   !!t!""@"p""##P#l####$$$L$t$$%%@%h%%&,&h&&'T'((((P(x((())0**+,H,-d-..t../x/0401112|233445585566L67T88:,:; ;`?@@@@A4ABCtD,EHEFG(GIIJ JKKLLMN4NpNO0OOP4PPQ0QR$RStSST TTTU\UxUUV@VVVWWLW`WWX XhX|XXY8YYZ|[[[\(\\]]]^^^,^X^^__h_``L`aaPab$bbc(ctcd,deeef@fg4gh$hiTiijjjk<kl0llmXmnLno\ppLpq qrrs(st4tuDuvtwwxxxy@yyz\z{P{||p|}d}~x l@0@ThDh\Px8THX,(H44D8,`p(P,<(<pt(p4h|@8 (tńtǠ(p4d|@TdҔ8Ԥ0րDش|\ۈ0ݘ\ p80(XD`|$x4Pl8`x0X<Xt@Td|X `\(x8plHXp80<t  l| D0HDt ,   0 H ` |       ( @ X p        0 H ` x        8 0d33!%!!dN3!hj#"'4&5463232+"&46R-  3F **;\ 3"'73"'"4"4\$$$$\<@7##"&5457#"54;7#"54;7623763232+32+#"&54#3A[ WY NQ&[Y[ PS [ [$ ~~ ~qD%4.54675432632#"'.#"#"=.'#"=432326.EPE.Q?!,I.4I.EPE.ZL#HW7@S$,7,6P44<(=+!) <.;PwwO 0=Wc !,<%2#"&46"32654&2#"&46"32654&#"&547%632i4GH23HH2#12#"214GH23HH2"22#"21 { I52HIfI&2$#32#%2H52IIfI&3F33"%2z z  i/8!'#"&5467.5463262&#"6732+32#/32-C;V;4"I2!!  ! 2,o+ &qi(/>)70@^@4M7$1F  1>E:FS+)V ;)/F;l\3"'"2\$$&\%4>32#".&=D ' d C54'&54632#"& ' d C<=D h ;GI mNJn q\)54327632#"/#"&54?'&54632 Q  RQ  Q  + *p  pp  p* H #"=#"54;543232#@oT 73#"&547υ   H+ !"543!2nvt %32+"&46' (( ((t%8&&8%q #"&547632  G yH   qj "&=462'"326=4&hhhhDNQADNQ_dvvdwk^Yb^Ybqd32#!"54;#"&54?Aq  dq Tj"4632!5432!57>54&#"#"&hmMIo5[:vF+V93S  -ghD-OU$M"4N<- `j/4>32#"&546232654&#"543254&#"#"&}'V3KeA&;B{V; *N1DcjIN:*;   ('YA:D U3Lk; T:;Un1A i\%!5332+32+"54;5#xT"""LЩ/va`\&"#"5!2+632#".463232654&<'>& I;MdsY'M3 *N2I\Ob nT_y!  &&bOCTj,"&#"632#"&5463232654&#"!(TL1=iDcdIYp{ / J<8KK41# 3%DtG&znLQp QQX@;V1i\5!#"=!#"&547u  !#LA qj'"&547&5462&"32654"32654&wpnnpfiixrOO98P=UU=54'#"&54632#"&546.#"32>!(TL1=iDcdIYp{ / 9J<8KK41# (%DtG&znLQp wQQX@;V1v %32+"&4632+"&46' (( (( (( ((t%8&&8%-&8&&8&o^ 73#"&54732+"&46Ӆ   (( ((  &8&&8&H, %#"'-632  _ c C 3%w !"543!2!"543!2 DDNN,4632 #"&547-&N ^ c  A)>32#"=>54&#"#"532+"&46 >?(PbG^XMP>=D UE8L,)D%E+2?"),,ip-6"&=4632+5#"&546354&#"32>3275"32+WknUD[! :NaOB3DWVI*5 _@;L7+>or\EH5=S/4D~c]~ (:-&/ O3 $%!32+"54;#"54;32+"54;'#6Ox%LGpl%+3*7#"543!2#!"54332654&+4.+326|6IbUaD_@RJ7O&M65J)S>J.-d?Z<.,;&%A?@+4>325432#"'.#"3267632#"&5? "2Z9eHgCSvX4R(  4T,fE7J<+Ep.DYGZ*0  &+ e+37#"54;2+"5434.+3265h"aa'Q5Hp)j8k66ۑwb--+3'32+"54;#"543!#"=!35432#"=66Ƒb--?2@/"3275#"54;2+#"&=46325432#"'.;>Z)|lFIZa{mcC`6N@Ifu"4vJi6[&65'33!32+"54;#"54;2+!5#"54;2+32+"54;6-x66x.6q332#!"54;#"543!2#@? TG3#"&'54323265#"543!2#oM0V6KI=U( Jk)1DR:e+<32732+"54;#"54;2+%#"54;2+32+.'K66K-vIP88W7VGݴmw?3!5432!"54;#"54;2#=``  Q3,%##32+"54;#"54;32+32+"54;#F.J"cd"Ja\23"!#32+"54;#"54;#"54;2+4K"6j1J"13%@ 2#"&46"32654&,jghhVzzVU{y@y)Җgl+3!732+"54;#"54;2#'32654&+66PmvUA^U;_a)J43I3%@%0"#"&54?.546263232632#"&"32654&>0_ ZUqБh6(*G5 IE4VzzVU{yY Aj||{( $pҖgl+M3$-732+"54;#"54;232+./32654&+K66Ik/@=9BU9rJhU6]?a0 Rkzg)D0-E\@7%4.546325432#"'.#"#"'#"=4323264M[M4iOV<R:>Q4M[M4tZj?bDF]*4?2BY;g-m9`>@ps $xp(03332+"54;'32+"54;7'#"54;2+7#"54;2+E>@n+-o 3%3&%32+"54;5#"54;2+7#"54;2+Biio&(n g3)55!#"=!!5432vH]:;v:4\ 32+32#Aa3zq#"'&54632G    H    @\ #"54;#"543aS(qbg#"/#"&47,   g   X!5XK22@#"/&54632r  r vd  d  H ,463232+5#"&5463254&#""&5&#"326}&F[6_YfFVu^;MD7.b &45YzF5CSG9AQG%- Z;/'/(\#632#"'#"54;#"543"32654&Ik[~ZnF_66JhhJIig\g]^hX jkjJNkT%%#"&546325432#"'.#"32762"7Z/bd]?]?SghRmN  X !$|`c7 [(7gROeH ?G\#32+5#"&46325#"543"32654&6_GoYYnH6}JhhKJhg\YihjLMjjKNj?%!3267632#"&54632!."` nQ/b  De`e`v fdO_ 8a[{@DRQi\*".#"32+32#!"54;#"54;54632.A1>bXXWCFE'/&=O=7G ?F2'532++"54;26=#"&4632"2654&_6`Ert3GBiUyyUjgFbbbaNSl@^H3a|{)bcbFIb+'\-4&#"32+"54;#"54;>3232+"54;D7*7) -.6_&F.FY-- -9!0  .&O=\p32#!"54;#"5437#5@v;OhhFp#"543!+"54;265#5WD2A;xCFXB3hh?\)7#"54;#"54;7#"54;2+32+"54;'_66_/- \\32#!"54;#"543@u\  Q7>3263232+4&#"32+4&#"32+"54;#"543p0 @!7:+>"J(26"J'27"m""4$GG@,?+R<-RO51"32+"54;#"54;>3232+"54;54&?! --"K+B/CZ"m"A  OE0#N:+;H 2#"&546"32654&,`^__NmmNMnl_\^\)jkjJOjF(>32#"'32+"54;#"543"2654&(R9]}~\pCb66JhhhfR4,yz` cFGcbFIb?FG'532+32+"54;5#"&4632"32654&_66bCq\~~]ooKggKJigOR`zy)bcbFIbT""&#"32#!"54;#"54;>324"IS`KtEV&(< X*0LOf?10 g6%4.546325432#"'.#"#"'#"=432326D``DaJU4H98JD``DoU`=Z@BXu%(2,1@.E"(+  909K6 S"14+3$32+3267632#"&5#"54;5432<4*_  @GTJJ&. -C8 w+ !5#"&5#"54;327#"54;32#Tb2#".#"#"&54632* R%B;   L,E;'& P0/ R0/[h632#"&574#"&46;2-  nF **qv/4>754322632#"'.#"32>32#"=.q H4?)>0DYWB,; b2I^7,=2 kj$= 'S@?S(.qqi?B732+32>32#!"543>54'#"54;&54632".#"cZ9 1##7h^S:3N -)<`#hG&7mEU$;T@ =g_2>7#"&54?&547'&54626327632#"/#""32654&9  8#"7  8,:9-7  7"#7  8/6773II32JH8  8/6;+8  7##7  8/6:,8  8#"I34II25I3%3@%32+32+"54;5#"54;5#"54;'#"54;2+7#"54;2+32#AVVtn''nv<dd<@\"542"542@((((A N B[1E%+543232654&/&5467&546;#"=#">54&/&'#"f L9(5;HA8/ L4%6PH 1AoG+ 8($PC"-/EmE-41&CS:$.+G#-c 2"&4632"&46((c((TB%0<%#"&=463242#"'.#"32>22#"&46"32654&<";ZR9+.$4"+=C.& }z~x{jgm "Z;-!,463232+5#"&5463254&#"#"&5&#"32U,8?65,7K:-$C *+7"7/%,#+6 , ; $??632"'?632"'?          H !"543!#"5|H+TB )4@32+"4;5#"4;232+&/32654&+72#"&46"32654&$`.;& '96?8*1'MCz~x{jgmf$$$4), (6$w$z})jiki@#"4;2((Z| 2#"&546"264,:U)=*,==VH#"=#"4;543232#!2#!"4@on-((((d46323542#57>54&#"#"&@-+A;}$&+)  =<) Es'+{& ' d."#"&54632#"&5463232654&'&54632654&,  E",<.:G2%M :$"34($'@ %5'+$4,?$ "**  #"&54?632r  r  Wd  d +8!7#"5#"4;327#"4;32+5#"6_3(eRJs"KPe9&()2[((;KO \%-.=46;2+32+"547#+"54;5\qyb=>bHa:MMP r Q/;$ `32+"5432#"&546?G'A `$   A 2#"&546"2654->UW<=VVk\@AZAAV>;VV=54&#"#"&  ^  G'A *@-+A;}$&+) b   }$   =<) Es'+{& ' Dd.=TX"#"&54632#"&5463232654&'&54632654&"&54763246;5#57332+32+"75#}  E",<.:G2%M :$"34($'Q  ^  . 9  U9k@ %5'+$4,?$ "**  cb   + 9"$9$qQ'#"&546754323275432#"&46;2cLPbG^WNP>325432#"'.#"3267632+#"&5463232654+5.5? "2Z9eHgCSvX4R( &#Z) A,#> ,.llE7J<+Ep.DYGZ*0 # ';$  EM+ #o(+#n(+"jt(+"j_(q #o(,q#n,q"jy,q"ja,3.#"54;5#"54;2+"54;4.+32+3265hMM"^^"w )F+Gqh8h,<1#}O2"d13% #o23% #n23%"jx23%"b23%"j`2vd!762"/#"&54?'&54632,     7     (0]'/7#"&54?&546327632#"'&#" 32654A  EBhVHB  FChZB ;IVzl;JVz3R  WXr|BS  XSx|l;i[;i^(0 #o8(0#n8(0"jx8(0"j`83% #n<+3(732+"54;#"54;2+32#'32654&+66PmvUA^U;oFO_a)J43I\:%4.+"54;2654&#"32+"54;4632#"&5432326,A9&:B,*@n6Y:@W:HA3F  $.20H$3!*>8%S5QS?<'.T]D1%FH"oDH"nDHr"jDHD"DHO"jDH"~ D B2?G4632>32!32>2#"'"="&5463254&#"#"&5&#"32%.#"Gb%>@$IHH2.! ])J2(8TkL),3%$> ,# J;.3B*Yz .&'-}kLb BM"DT@6G <#2 ] "*6'5TS>%#"&5463232654+5.546325432"'.#"32762%1K+A,#> ,-2K'd]?(\?SghRmN  X;$  C*1>(c7 [(7gROeH ?"o H?"nH?p"jH?O"jH\"o\"n\t"j\O"jHl,7.54632?2#"&54632&'#"&547"32654&. /e_ KKX`_^)@)5D[ WNmmNMnl%  0* "@Sl^\c1* jkjJOj5D"QH"o"RH"nRHv"jRHD"RHO"jRH!"43!22"&5462"&546n((((5#+7"&4?&54632762#"32654&#"B  B9_R>@  @;_P3BNm2ANm#D  CBS]1B  AAV]Y(jLD&jLD+"oX+"nX+r"jX+O"jX3F%"n\FN(>32#"'32+"54;#"543"2654&(R9]}~\pCb66JhhhfN4,yz`cFGcbFIb3F%O"j\ O"m\$H"mD O"|{$Ho"|D eY3482632#"&547#"54;'!32+"54;#"54;32#3 #  1$(MuL86Ox%:Jpl{   :$!)"`%He,4@46323232632#"&5467#5#"&5463254&#""&5&#"326}&F[6%+  1$(#*=YfFVu^;MD7.b &45YzF5 %"   +CSG9AQG%- Z;/'/(? #n&T"nF?"jy&To"jF?"}e&TO"}F?"kr&T"kF+"kp'?\#`8G3?G\&232+32+5#"&546325#"54;5#"54;"32654&666_FoZZmH6_JhhKIigJQa]\`+jLMjjJOj+"m\(?"mH+"||(?p"|H+"}a(?\"}H+e$3<2632#"&547!"54;#"543!#"=!35432#"=#!5432  1$(Mn66ۑ::{   :$b--w+"?e+12632#"&547#"&54632!3267632!."  1$(6f`e` nQ/b  5@5v fd{   2!b[{iO_  $"2"kDRQ+"kp(?"kH?2"j u*?F2"jJ?2"| |*?F2m"|J?2"} g*?F2^"}J?82@'*?F2"_J5'"jx++'#jK5'3?C32+32+"54;5!32+"54;#"4;5#"54;2+!5#"54;2+!!.66-x66x)(T(eeeD+'\94&#"32+"54;#"4;5#"54;32+>3232+"54;D7*7) -.666_&F.FY-- -9!0 (6_(z.&O=q"c,\D"q"m[,\"mq"||,\n"|qe3+2632#"&5467!"54;#"543!2+32  1$(#*? % {   + %"\ep&*2632#"&5467!"54;#"54;32#5  1$(#*v & X;{   +O $"hhq"}c,\32#!"54;#"543@vO;36#"&'54323265#"54;2#!32+"54;#"54;2#FI/> (%8-_m,,, Pe*0)JBe|Fp*.#"54;+"54;265#532+"54;#"5437#5QzWD2A0;@B8l;xCFXB3hhOhhTG"jln-F&j+8<3'.?8\'NF?"ny/\#nO?83'/\8\'O? @#`/\\#`O?3'y/4\'yO+3-7632!5432!"54;5#"&54?5#"54;2#  <`j  ~` P  \ޡ>  J\\&763232#!"54;5#"&54?5#"543?X  lW  ku\3  >2  =2"nt15"nQ823'158'Q2"ks15"kQ"Q` 5 A*4&#"32+"54;#"54;632#"&5463232P=aK--"KKeIi> .i+q|`qES 595232654&#"32+"54;#"54;>32#"&546,.;A7! --"K+B/CZV=> A3J+;  OE0#N:?X 3%"m]2H"mR3%"||2Hm"|R3%"6p2Hz"6R N3$135432!"&5463!#"=#35432#"=%;#"Rrm;N-X= =X-\}q` )NK..KN B"*5632!32>2#"&'#"&54632.#"'"32654&+-W//I0.! ])-ME*>\[>)H +Y-DD.,ED=r1`BJd B@79>\Y>&6mnnGJn+M"nu5T"n U+8M3'5T8'U+M"kq5T"k U\"nw6g"nV\"jp6gr"jV\S@M#"&5463232654+5&'#"=43232654.546325432#"'.#"4M[M4lXA,#> -/\1bDG\4M[M4iOV<R:>Q'0C3F\;$  B <p1DI7*4?2BY;g-Qh2fSA,#> ,.T.Z@BXu%(2,1@.E!)+ "pH;$  B , S"14\"kn6g"kVHS33232654+5#"54;##"=!#"&=#32+#"&546,.ii ilA,#> r QIrr I/;$ +C3"zWH"kq7+e#`WH3(32+32+"54;#"4;5##"=!#"&=#Accii`` Q((Irr I+305#"54;543232+32+3267632#"&=#"43JJ<4*_  @GTKewwe(~&. -C8((0"c8+D"X(0"m[8+"mX(0"|}8+m"|X(0 "~x8+"~ X(0"6s8+z"X(e03:2632#"&547#"&5#"54;2+3265#"54;2+  1#)<'Tr"J[CB\J"!//!{  !2$tUQD\]CQ-J/(,"+e,42632#"&5467#5#"&5#"54;327#"54;32  1$(#*)TbbXXWCFE'/&KO=7G \ 1"32654&%5#"54;32+632#"'#"54;#"543BJhhJIig6_Ik[~ZnF_66jkjJNk+Tg]^hX3832654&+4.+326!2#!"54;#"32#"&46@RJ7O&M65JRIbUaD66%! %9<9<.,;&%AS>J.-d?Z!  7R4,3g\ ("32654&'632#"'#"54;#"543!2#BJhhJIigIk[~ZnF_66jkjJNkg]^hX .%N 3%4.+32632#!"54;#"&54?54327632&M65JA_0aD62  FJ  &%Ar24?Z#  0B%3  N 532654&#"'632#"'#"54;#"&54?54327632hJIigKJhIk[~ZnF_6-  AO  kjJNkUg]^hX  -C'7  ?@+72326=4&#"#"=432632#".54S #H.XvSCgHe7Y3# f+S5!}&ZGYD.pE)325462#"&54&##"'.#"3267632#"&5? "2Z9eH4R7   gCSvX4R(  4T,fE7J<+E%;8& !$p.DYGZ*0  &+ eT2%#"&546325462#"&54&##"'.#"32762"7Z/bd]?4R7   ]?SghRmN  X !$|`c7 %;8& !$[(7gROeH 33 ,4.+3265!2+"54;#"32#"&46'Q5HpCaa"F$! &8<>j8k   7R4:,3&35!#"=!2+32#!"&54>;5#"66Da0_{J5ЕiQCb^@54 /FL\ ("3264&7!"543!2+32+5#"&54632KgiIJhhh66_FnZ~[kIkNJjkjXh^]gD9 -J+3)##"=43235!#"=!2+32#!5432!66>:--bw2@\@;"3267632#"=#"&54675.546325432#"'.#";2#)IX]FDb?jZt<2'1iOV<R:>QWA0@57ID1pH^I5HA,BY;g- ' A3b--;J Wj-"&#"32+#"&546323265#"54;54632)3?``V=> ..;``W@> 6 @3W?X A3WA[ ?x<46325462#"&54&##"'.#"3275#"54;2+#"&5?mcC4R7   `B>Z)|lFIZa{>i6%;8& !$[&66N@Ifu"4v0"3=%3254.'#"54;2+7#"54;2+#"&54>, !<o&(n  50/6& &"%5Z  !("#:IN56-E\8%#"'.5'54+"32+"54;#"54;>323265#"54;EG;@. D$3-.6_-)5&-)Jst8L9 % ]5 %"&432A%2;=fD) D "!G9(EW q3#5#"543!2+32+32#!"54;5#"43?dd_A((+f3?32+.'32+"54;#"54;2+%#"54;2#"&5463265.#IP88W7VGQK66K-v$<8& !$ mwI4R7   ?j2"&#"7#"54;2+32+"54;'#"54;4632k)3?/-._6W@> 6 @3'A[ \\!+32#!"54;5#"4;5#"54;32 baaub) (:&f<2763232+"54; 32+"'"&54;'#"&54?'#"543H  Jn85n  N  P^f 1  40 T86  87 Q37!5#"'#"&5#"54;327#"54;327#"54;2+32#0 @!7:+>"J(26"J'27"m""4$GG@,/+R2-RT3&7"&546323265#"54;#"54;2+#?4> ,!(6jJ"4:@V A35H1"32+"54;#"54;>3232+"54;4&?! --"K+B/CZ"m"A  OE0#N:J+;3%@.3U )"32654&32674&#"&54632+#"&4632,VzzVU{y8:$! &8;%BghhPҖgl   7R4UzyHN *"32654&732674&#"&54632+#"&54632,NmmNMnl4?$! &8;%4^__HjkjJOj   7R4=T\^\R~ 3"32654&'63232+"54;4.#""&54632TY\QR[Z)f?:W(!(3<pspf!qlnoIJ* "G)~~T /"3264'63232+"54;4&#"#"&54632tJK9:L62^)9..#!=-?gCGdcH+lPKfj{<;1?!'.32#"'32+"54;4632hhfLJh)3?(R9]}~\pCb6W@> GcbFIbc @34,yz`_A[ ZI,C\:@+/& 2)7'q?  --  ?.#*+B2*4 |$ {j_    \@774>54&#"#"=4326323267632#"=#"&\4M[M4Q>:R7654&#"#"=432632D``DXB@Z=`Uo1MOL J89H4UJa>-1'&(41"S 6K9*6  +("E.@fAQj '"2654#"&54'"&4632326322##2#>=V7L77&@W;., I"""X?& $56J5[Ar3A +9342326=#"&5#"54;543232+3267632#"&546..;KSGTJJ<4*_  V=> A3;!C8 ww&. c?X 3'"32#"&463!#"&=#32+"54;q$! &8<$ ii   7R4r I+j/"&#"32+3267632#"&5#"54;54632l)3?<4*_  @GTJJW@> 6 @3-&. -C8 -A[ H93 #"&5##"=!#"&=#32632>=V ;., X?:Irr I3A (u2#"&5#"54;2+3265#"54;2674&#"&54632#sTUr"J[CB\J$! &8;% UtsVQD\]CQ   7R4+>5232+5#"&5#"54;327#"54;2674&#"&54632"KTb75##"=33>54&/53#"=#+.W&8-}MRS1\+= AD A_+6&Z%4';[,ae``.?.532+#"&5#"54;2+,(+^ 4U]51M 0# NPx:=%337#"54;2+32+"54;5#"32#"&46;2#(nii$! &8<$o    7R43Fj52 32+"54;7#"54;2+#"54;2674&#"&54632#AGs46n$! &8;%xxK   7R4g3!!5432!57#"4;75!#"=!32#6:vMj]Q;(v:(s%7##"=!32+!5432!57#"43(x^}GjM7`$(8a$(`3/"&54?##"=!2+632#".463232654&#" ?&,MdsY'M3 *N2I\O?4\:7` nT_y!  &&bOCT(P30".#"32>32#"&54632'#"543!#"=#&@&?O\I2N* 3M'YsdM,&? :TCOb&&  !y_Tn `7_b0%".#"32>32#"&546;'#"543!2+l  "/AJ2$<'  !C"JZSE  ȴ  Z>Ek%% UMs~ X8A#".5>32>54&#"#"54?#"&543!2+2#"3267632r;2E8^C),88"   (51$H25E=1-Mh)7 2+<6 1#-   ~/%22$% ,Tj0+!5432!57#"4;>54&#"#"&5463232 ]J:vW:$V93S  mMIo15 <$M<(99 4N<- -ghD$:3P3,"#"=#"&543!2+632#".463232654&<'>& - GI;MdsY'M3 *N2I\Ob nT_y!  &&bOCT_*7"="543!2+6;2#"&546232654&#" %P\QG>Y7-97=G 0 y JD@R2 9160 327543232654&'5#"54;5#"54;2+32+#".D=>PMXii--ii^GbP(?>"D)"?2+E%Qoo6,L8EUF )%654&#"32+"54;#"54;7>32I10$+DMb66_3#R.%W;G)5#"54;2+#".'5432qY((  _2s   S+e  "FGp-135432!"54;#"54;2##"54;+"54;265#5``QzWD2A0; CFXB3hh|Fp*#"54;+"54;265#532+"54;#"543QzWD2A0;@B8xCFXB3hh X3C#"=4323265#"54;2##32+"54;#"54;#"54;2+0R*$u2+f&Z*u"jS1ZD%6Q(@&FGp"6:!#32+"54;#"54;#"54;2+#"54;+"54;265#54K"6jJ"QzWD2A0;ACFXB3hh FGp3GK4.#"32+"54;#"54;>;232+"54;#"54;+"54;265#5a8*  --"K'*"BT"m"QzWD2A0; ++  OE5N:OCFXB3hh O"kq$H^"kDq"kq,\l"k3%"kq2H^"kR(0"kq8+^"kX(0 #q+"ql(0%/9I#"&5#"54;2+3265#"54;2#%2"&4632"&467#"&54?632sTUr"J[CB\J((r  r  LhhL0Z)|lFIZa{mcC`6N@Ifu".??E4vJi6[&6?F2 4"2654&+"54;27#"4;6=#"&4632532+32#Fbbba~MIqBPMb66_C19b@,377UMɆCC& -2"C6q15"C< Q O 04>H#"&54?632!32+"54;#"54;32+"54;/#2"&46"2654:r  r  f6Ox%LGplc'67L77?2##2#d  d v^5'$56J5!"""H &~&Dv N"vq B^"v(0#v5"v O#0$H#SD O#1u$HV#SD+#(?z#H+#Hu(?I#OHd#!,\z# q#Xu,\I#W3%#"2Hz#"R3%#Xw2HI#XR$M#5Tz#.U+M#;t5TI#dU(0#"8+z#X(0#Zw8+I#?X\8@&6g8'VH83&7+83&Wp@6%".54?654&#"#"=432632#"54?>54&# 'Q>:R54&' J89H4UJa M(  %& 5 f +("E.@1- !J+%V V#I5'"kq++' #kKF:A*32+"54;4&#"32+"54;#"54;632--P=aK--"KKeIiq|`qESg93%2326=!55!#"=!!5432#"&546..;H]:V=> A3*;v:4?X s9#2326=!5##"=!!5432#"&546..;-^V=> A3*$T7`$8?X O"}_$HO"}D+S3@232654+5#"54;#"543!#"=!35432#"=#!5432##"&546,.66ۑ:A,#> r Qb--w/;$ ?D27!.".4632!3267632#"&5463232654+hv fdZw`e` nQ/b  DA,#> ,.DRQ {iO_ 6/;$  3% #qH"ql3% #qH"ql3%"}e2HM"}R3% #qH"qu3%"qX<3F% "q\F#"543!+"54;265WD2AxCFXB3H De?G !"32654&43232+5#"&4632JhhKJhgg6_GoYYnHjLMjjKNjYih' fj ,32654&#"7"&#"632#"'#"54;4632hJIigKJJ)3?Ik[~ZnF_6W@> kjJNk @3g]^hXA[ T(7232654&#"#"=432632#".546g #O4RhgS?]?]db(K4( keORg7([ 7c`| T|/5".546325432#"'.#"354632#"&5764#"HEe3d]?]?Sg+S:7029  )B>*FF&c7 [(7gR :8"D3?9.gb |I?9\ ,"32654&#"&=#"&46325#"54;32632JhhKJhgQ>=VGoYYnH6_;.. jLMjjKNjX?ihz3A ?j ,%4&#"326"&#"32+5#"&463254632gLJhhKJh)3?6_GoYYnHW@> NjjLMjj @3[YihA[ ?723267!4632#"&546%."V,V8Qn `d`eD df V_Oh{[a8 EQRD? HGC)1%#"&/#"&'%&#"#"&54632732632'32654*. `Hqn9p/b  D@k!:+ !6fRh ) ,.[{H>T 892J KeI$g@7326=432#"=#"&54>75'&46325432#"'.#";2+"XB@Z=`Uo .aJU4H98JDP0D-A u(41"S 6K9(  b@.E"(+ ) g@%4.+"54;2654&#"#"=432632#"'#"=432326 A-D0PDJ89H4UJa. oU`=Z@BXu  ) +("E.@b  (9K6 S"14gT732632#"&/#"'#"=43232654.+"54;2654&#"#"=432632:+ !*. = oU`=Z@BX A-D0PDJ89H4U/PwJ ) 3% (9K6 S"14(  ) +("E.L <747>32#".'.732654.+"54;2654&#"LEZ.Ja= oU%  G,jBX A-D0PDJ8`OJ@13% (9K  Z@O%S4(  ) +F !%#"543!32++"54;26=#"543;;WD2A)OFXB3?Fj 1%4&#"26"&#"+"54;26=#"&463254632aGFbbb)3?+3Grt3GBiUyyUjAW@> Ibbcb @3:.6H3a|{aA[ ?F &"2654&5432+"54;26=#"&4632Fbbbaa+3Grt3GBiUyyUjbcbFIb8SC:.6H3a|{M2"3275#"&54;2+#"&=46325432#*#"'.+Sbg`;>u  OUm|`T; QWA6JN\  v&hX7Nq( C &48('/%3#"54;2++"&=47#"54;2+274+2o% %o1%S{J3%%2LL-!!2&.8632#"=.#""&547&#"#"=43263263232654E5H5^5H4F!V@@V"<"!S/Z/IH0Z/S99uPw".."w+E' KR3'j64&#"32+"54;4632#"&#">3232+"54;D7*7) -.W@> )3?&F.FY-- -9!0 A[ @3s.&O=39j:"&#">32#"&5463232654&#"32+"54;4632W)3?&F.FYV=> ,.;D7*7) -.W@> 6 @3s.&O=?X A3J-9!0 A[ \p$#5#"54;5#"54;32+32#!"54;>;vphhX%#"&5#"4;32>32A%2;=fD) D "!G9 (W 32#!"546;#"&543!2#Aw  yy  x   O  \\0746325#"54;32>2#"'32#!"54;5&#"#"&\L,$u/* R%'*   R&'& P# \\&0325#"54;32+32#!"54;5#"&=464&'; u 2'33Y/2   6"%1n)/9\#"&5#"54;32632>=Vu;.. X?cz3A b7\?%"54?#32#!"54;#"54;!2+32#".'&5463232654&#"*xk5 ESZJ"C!  '<$2JA/"  ~sMU %%kE>Z  Q P\ FQ75#"'#"&5#"54;327#"54;327#"54;2+32#0 @!7:+>"J(26"J'27"m""$GG@,+R -R  9@232654&#"32+4&#"32+"54;#"54;>32632#"&546=..;(26"J'27"m""J0 @!7:+>U=> A3i+R<-RO4$GG@,?X 95"#"&546323265#"54;>3232+"54;54&?! V=> -.;"K+B/CZ"m"A  ?X A3E0#N:+;595"32+"54;#"54;>3232632#"&54&?! --"K+B/CZ;.. >=VA  OE0#N:3A X?P+;6(!#32+"546;#"&54;#"&54;2+1@  - ] ?  k   O B  H 2#"&546.#"!26,`^_iIHjtjk_\^\F\]E(E^^ K%.75463!#"=#35432#"=#35432!"&#; |y5KKr$u\nh Xiq`\wMOS^+(!9"32>=43232654&'.#"&'#"&5467>3233_:*"  77= T UG*0*&IT1]B;g 8d7Q&( 1)xG6G!"&P;5332+#5#"&53;#"4&+3265p D0"-5?N D0"-5?N-?%163,W<(163,)*J;(+J;1,Q6%2+6%T UqT[  q49C+7232675#"543!2+32632#"&=#"&546I4"IS<`;.. >=VEV&(< I*0L^3A X??10 TF""&#"32#!"54;#"54;>324"IS`KtEV&(< X*0Li f?10 i9&"&#"32632#"&5#"54;>324"IS;.. >=VKtEV&(< X*0L3A X?f?10 T)"54;546;2+"32`WD2AFXB3T8"54;4&+"54;232#A2DW`3BXF^<)4732+"546;#"&46;232+.'74.+326?  -- @^@:*;2 28K4)$ a;]  M E/$99I XIx2<)473>732++"&46;#"&54;2+4&+32>q4K82 2;*:@^@ --  ?];a $)IX I99$/E M  2g9B%4.546325432#"'.#"#"'32632#"&=432326D``DaJU4H98J>Qh2oU`=;.. >=VZ@BXu%(2,1@.E"(+ "89K6P3A X?"14Wj"&#"#"&5463232654632)3?V=> ..;W@> 6 @3l?X A3A[ Wj,"&#"32+#"&54632327#"54;654632)3?`kI.> .<P^W@> 6 @3[*3 4 A[ Wj#"&54&#"#"&5463232632>=V?3( >@W;., ;X?3@ [Ar3A Qj '2654&""&#"#"&4626546322##2#,.;W@&77L7V=> <"""N A3rA[5J65$ &?X +3 W#+83$32+3267632#"&5#"54;5432<4*_  @GTJJ=&. -C8w+*232+32+5#"&=#"54;5#"54;!5#"54;!327"""KTb=V-^;., X?0$T7`$83A s +%3267&'7+"&=#5##"=!3546;2v)/>0  -^ѣ6"%1)/ ),P O$T7`$2'33_b07"54?#"&543!2+32#".'&5463232654&#"  ESZJ"C!  '<$2JA/"  ~sMU %%kE>Z  p8 H'&#"3267#"/#"&54632654&#"#"54?#"&543!2+2(Q   ),8$35A/"   /E"%m+% - . =%3-+4_>Z   ~'>=6aA$>3232+"54;5>54&#"#"5 >?(PbG^--XMP>=DUE8L,%E+2?")A$#"=&#"32+"54;5.54632D=>PMX--^GbP(?>D)"?2+E%,L8EU3$7543232654&'5#"54;2+#".D=>PMX--^GbP(?>"D)"?2+E%,L8EU?8B+4>325432#"'.#"3267632#"&5? "2Z9eHgCSvX4R(  4T,fG7J<+Ep.DYZ*0  &+ eH "32+"&4672#"&546"32654&' (( (("`^__NmmNMnl %8&&8%_\^\)jkjJOjI!)7#"&54;2#!"5463732654&+4+326- @VKpU< V8C;10;)O >-8!!K/B '" "S'g8";2+"32654."&54>75&54632.8JDP0D-A XB]I"@.Uo =aJ+E,+P+ )  (4`j&0jK9( %31@'0)u  OUmNq( %;8& !$C &WA6JN\  v&hXS ;%#32+"&546;#"&54;2+35#"54;2+32+"546;- { % j--j & | -ě   O    F"p*#5+"&5>;#"543!32+";267;gb4 7MR1D m$85' &Fphh!IPC/5<O  ( />8?E N\\35432!"546;#"&46;2#s SS  xl O   ?Fj 0%4&#"326"&#"32+"54;5#"&463254632gLKggKJi)3?6bCq\~~]oDW@> Ibbcb @3`zy`A[ A2>3232+32+"54;5#"54;5>54&#"#"5 >?(PbG^ii--iiXMP>=DUE8L,6ooQ%E+2?")A2#"=&#"32+32+"54;5#"54;5.54632D=>PMXii--ii^GbP(?>D)"?2+E%Qoo6,L8EU)9\ -732654&#"73%25#"54;335432!5#"&546R2-:=,,R6_V/E=LkiLNjk]O7@$8a1A~b^b=\ Q732654&#"725#"54;!2+32#".'&5463232654&#"#"54?#32+5#"&546B, 07=,,FR6_ ESZJ"C!  '<$2JA/"  &O[0@=@whMNjk@~sMU %%kE>Z  1A]^ S\ 5AE%3267&'7#"&546325#"54;33546;2+"&=%32654&#"73)/V/E=2R6_=6"%1>0  2-:=,,)/[1A~b^@$2'33# ),P OLkiLNjk]OK3C%32654.546325432#"'.#"+"&=#"54;543232+@F;UU;OH(0=168;UU;]S=VJJ@@;3)%(2,2?E/)":0;IX?ww3AFj<"&#"#"&=#"54;543232+32654&54&546323)1B#Z:=VJJ||90-?#[=> 6 :-7TX?ww3A=+A ;S |O6OW32+327&546325432#"54&#"354632#"&="'#"&5#"54;5432>54#"//<4 ;`T+8"O9@H8&./10KJ  0)#GTJJ%=/84&.8ve~ [%:eT)6!D4>8/;5b `C8 w#">I9?\CG".#"3632#"&5463232654&#"32#!"54;#"54;546323.A1>>O>PF9> .772?E-NDDWCFEZ'/&=ESN:@W D0J,:ZO=7G OP\63"54;#"54;32654.546325432#"'.#"#"W>H;UU;OH(0B-68;UU;]S 0(#%/*2;E0&! :1:F%2\!35432!"54;#"54;8W$8a O: A#'##"54;2+73#"54;2+#'##"54;2+73#"54;2+2[Y3To5HV3YE2oS2[Y3To5HV3YE2o77H4#"=!#"&=#"=!#"&=q   p pkp p E&h.73275#"54;2+32+5#"&54#"#"&54632A0D3-.6_8,=X>( 744-;c$&N<Ot BU9Wh732632#"&=#"&54#"#"&546323275#"54;2+   5.88,=X>( 744A0D3-.*4@ VAl$&N<Ot BU-;czt-4&#"32+"54;#"54;>3232+"54;{,$%V#>--:V%  S 3(  64&#"32+"54;4632#"&#">3232+"54;,$$V9)( !).-:U%  +; )"J3(  u#"54;+"54;265#5y9,TS +& -9 +!CC ""&#"32+"54;5#"54;>32"05t>1L-7' 1  C)  W "232675#"54;2+32+5#"&546e!06t>0K-7'- 1  C)  w5+232675#"54;2+32632#"&=#"&546!06t>&((8-7'U 1 !* 9)b)  f_$-3>732++"54;#"54;2+32654&#V%7+%(*ZE0##o1c$7D0CO F5?)= 9 -,{ %#'#'#"54;2+737#"54;2+ <9"6 I#/8!:-!H  Ц {%%'#"54;+7#"54;2+32+"54;!z K"ki#G *S    v 3#"&5465&PW   &]]Av #7632KPW   v 3#"&5465&PW   v #"/K*  W , 2654#,##(57&""!4'$5, "&5467",&75(##!5$'4!"">32#"=>54&#"#"5))4@.= 923)(,  7-$2}- )#"=&#"#"=.54632 ,()329 =.@4)),) -}2$-7 S #"/7632n   > b` S fXsS #"&54?#"',> aa n  S hXs#"/#"&47,  kk  l  XX  '&54632762,  kk  m  XX  @{"=42@((cbb@qv@CE@l[ql8~N:~Pr 32"/&54"54?62#d6666p p_p pr 32"/&54d66p p,ub,uc #"4;54232B(B(BB(oxX54232+"=#"43(BB(BoBB(BB(@#"4;2((l2267632"&54>-^c 2"&46,(c( 2"&46"2654,'67L77?2##2#5'$56J5!"""e2632#"&5473j  1$(M/:{   :$!)"]2632#"&#"#"&54632j0 ?V &  AW-. 40- 10y#"&54?632"&4?632#a  b  b  b  Q^  ^  ^  ^ 732632#"&/._+ !*. ;b3J )!#"&54?'&546327632#"/ //  //  //  //   //  //  //  // t#+7#"54;2+#"&547'#"54;2+274+k!H zv I g    0! 2  u32+"54;#"543:hhLu  S  74.546325432#"'.#"#"'#"=432326,?>,?07" /%$1(5D H7>(  ;)+9@  *- %%1#6 "332+"54;'32+"54;7'#"54;2+7#"54;2+>xV*df,WxjHXY Ht  aa  tf UU w$#"=&#"32+"54;5.54632 ,((429U=.@4)(X,) -  2$-7 2 !"5!"4M}( (2 !"43!542"5U((a(} 2 !"43!42"5U((|(b 2 %!"43!42"5U(((& 2 3"43!42MU((d )42!2(U(Z{ 7!2#!"542U(((P8'&54632762,  kk  m  XX  7!!"43!2!"43!2LLr((b((dW\87632'&54632,> aa vn  8X=8#"/7632n   d< `^ 8X88~X~mo'R~\955vDn 32+"5jB((Bn "=#"43n(BjB(n]W|[zx85432!5432 U~~ Ux8 !5432 )~ U;8"7632!2#!#"'< BTB nT ) CCevCej9o]Ce@q@!"43!2RL((Cel|c}4ucjt0!"&=4>7&#"&546;2  26  8&(4L      63 )68w1~CeyCek{l${"=42"=42((x((cbbbbCey&54632#"/&54632"'  b   b  Q  ^  ^  ^  Ce#}@|GiH |27_U` a  :.9=CO8/vrNB" "=#"4;542(BB(&(&B5.)]x54&#"&54632+"&546;26! &8;%B B$  7R4  8v4nEPx-kEMX8:zNU7{o92326=3#"&546..;)V=> A3*0?X 9#"&=332632o>=V);.. X?0*3A c}d9gzjh89~X8f 3#"&547DJ  4 zS!zeYJ`A8 7] 2325432325432#"'#"&54L66 1./2,0+ 9 9 .34-JDlkZ;E] kNPp|f:I\ | A9i? v]v1]9#".5332632>-!)%. :(#/ o^q#"&54?632jW W  v  ##"&54?6322"&4632"&46W W    v  B  Wk# v 32+"&46' (( ((&8&&8&Ok#GVm#/ k#$7Lk#('Ui#0Im#,"7"2 O3$+3%>3732+"54;#"543!"=!66()b O3)"54;#"54;32%!#4x%x)+3(g3=5'3+3%@$/%#"=##"&=463235432'2#"&46"32654&  sjghhVzzVU{y--  00حy)Җglq3,+<3. O3 732+"54;#"54;32+"54;#lOx%L) Q30231E3 '7"=!#"=!#"=##"&=4632354322!54632!54t   | 2dd--  00 ee 3%@233'7+"54;#"&543!2+32+"&54;!32, - 56 " "+33fA)57'5!#"=!!5432|ʨ4<:yH373%3<.*309#";5#"&46;5#"543!2+32+32#!"54332654&#"B^U;2.OmuU us UumO.s2;U^BJ43IY_a<?G !"32654&43232+5#"&4632JhhKJhgg6_GoYYnHjLMjjKNjYih3"32654&#"'543267654&IAR)F=d:@t; "G3CML6Kgj]E+ <\M@'B JA_u((U/2#1AedFHH!-8=F!!+32+"4;5#"4;2+#"4;!A= n58n((y((L(D(832#"&54767.546325432#"=&#"2654.#"P>cK1b]/9 &6G;69*2Rf,5*Nj77 *V7Z\\^1: 3$8<'N'&oL*E( mOL87E732>75432##"'"=#"&5'467&54632#".#";2+"48    .:@].+NO@S]  +@'1? & *,w>  JB7 #? 96HB  ##2)$ &6zF\.##"=!;232+"54;654#"&5467^F4=-'-=" ~-"?S=I37`$AeK/A$/8 ?&-Y?SyC5F,"32+"54;#"54;>3232+4&?! --"K+B/CZ"KA  OE0#N:H+;qj "&=46226=!"!54&hhhhDNQADN$Q_dvvdwD^b'^b%#"&5#"4;32>32A%2;=fD) D "!G9 (W F(3#"54;#"54;7#"54;2+32+"54;'_66_/-O:&f%32+"54; 32+"54;'#"54;2n85n G^U 9T+8w5!!##"54;2+3>5#"54;CFt91b)5@ D#F*x+?[]-(+ZCX-|F\<;2+3232+"54;76=4#"&5467&54?##"=!$7%4 4TB& ~/+|Lr\P,B^#3  04j/! $@FFUi%9P 7`= HRR%!2+32+"54;#32+"54;#"54m~""x--w!"OOB&2#"'32+"54;'4676"32654&=Wz\n@b6D3,6N`eIHe^z_\|[`@k+fHIefHJd^n1754>;2432#"=&#32#"&46326=4'#"&^,IV0 ;12:]@ KQ*$ 5Lv=\4#E0KO#/*  &h+ 72654&""&546763!2+J^`abGZx./.K Y#<bOLhjg)[>Q('  Bs5E#=+) %#"&5#"5463!2+32>32A%2;  D) D "!G9  W +)%26=#"54;2+#"=#"4;(:M15]U4^<A6(PN(.E%8! )%+#5#"&=#"54;;3226=4+! @-+--?O2 $\3(2b(; 3,d1)*F;)2y*+9!YF)("4;7#"54;2+32+32+"54;7G@1-4 L)5y(( "F-\-7;462326532++"&=#"&=#"4;=-  *%2V -H9)  +9H)R1-  6' :K L:(+(="&547#"4;2+32>=432327654'#"4;2+#"&'IT7m1:*"  7@#:2n6UG*0*\^S((P]7Q&( 1)Z/6[Q((S_b%Z;"j2+);"j>H"8+)" >+("$Bpf &03254&+"32+".4>;2#;2654#B69K(>: ^H5Q++O5D` N:8BD7;gw"$"-!E[32#"&'#"&46;2326=4&#";2+"&r&F-ShhSEb! , "fDNQA 5 t5I;*wdviY   ^Yb!/8)   0(3 %#5&5476327>32#"'&#")  $   C3%%#5#"&54?6327>32#"'&#"2)f  r $Y  d  0("jBJQF\ ;4&+3265%;#"+"&=#".=4>;546232<(163,?%163, @-+  5(; @-+  5(; 2+6%2+Q6%)* *+)* *++(="&547#"543!2+#"&'""+32>=432327654'IT76UG*0*K`1:*"  7@#:\^SS_b%P]7Q&( 1)Z/6[QPnT2'#"+"'5;254.#.546;TDaaD)J;)   `lɯ =C)*$.(A  RYYm'#"#";2>54.5463ldYG&@; 6 9%1/ERE/~(ki4.%!/ (  B1{+3$#"=#32+"54;#"543!#"=!32_66Ƒ)--b'1("4;7#"54;2+32+'32+"54;7'OIp.*{1 L~&2usy(۱( xdB#/2#"'32#".='4676"32654&=Wz\n@CIC32#"32+32Dh26W5M`cJCe8(NA(_AE_+&kC,q+&kjsw61326=4&#"32+"54;##"=!"=#6732#T=QHDL%6x|t(|)O RamO`S84+ItrL ?AKj>&iq@@/4>32542"5.#"!2#!327632#"&5@ #3Y7dJ((hCSuXbL  4T,fE9I;)Ep.DYZZ  &+ e\@6q3,q"jr,TG3-H3,532+"54;##"&=432325#"54;2#32654&#T(UwnPst6$; .:3732+"54;#"543!"=!66()b 63%765#"543!2+3#"=!#"=;#"Yc<)""bbb"+3(P3N2+7#"54;2+32+.'32+"54;5#"54;>7'#"54;2+5#"543^V2=98RKd3?589=2V34pXu"9r]Xp4\@<2654&#"#"=432632#"'#"=43232654&+"543"AWQ>:Rx6>N"M6x.)=1.133%3#"54;2+32#!"54;#"54;2+3#"54;2+@6.,66.)O35)"54;#"54;2+3#"54;2+3#"54;2+3#"5&Z[[J-3##"=32+32+"54;32654&#@UvmP6);U^A ba_I34J Q30932+"54;#"54;2#32+"54;#"54;2+32654&#ee7UvmPe;U^A a_I34J+2!32654&#'32#!"54;#"54;2+;U^AUvmP66v"I34J)a_@@/72326=!"543!54&#""=42632#".54T "H.XuSCh((Jd7Y3# f7h5}&ZYD.pE)32"&'#32+"54;#"54;2+%"32654&s`fpspJWZSZ[RQ\Y9s~~ސonlq /3-5#"3##"54;>7&546;2+32+"54;6UhJr9UB9=@/kI66K$E-0D)gzkR 0a?]HDQr#.%"&54>;5432"&#">32'"32654&~~ $3K_@<0BH,)E8[~JhhJIig_`:HP-/&P#?,.WjkjJNkl(7#"54;2+"546373254+32654&+%FI Y<@ L^veo/'PC^)O IB$$*)r#"=#32+"54;#"543!--"M[O7(%765#"543!2+3#"=!#"=73#"M"ea·) %[[) O?H MS!#"54;532+"54;67'#"54;2+5#"54;2+7#"54;2+32+"54;.'32do#?Re0c#o#c0eR?##E.oAAo.E#g@%4.+"54;2654&#"#"=432632#"'#"=432326 A-D0PDJ89H4UJa. oU`=Z@BXu  ) +("E.@b  (9K6 S"14A3%5#"54;2+32+"54;532+"54;#"54;2# -x""x--w!"xx3+OA&XF(3#"54;#"54;7#"54;2+32+"54;'_66_/-OX*7325#"543!2+32+"54;#+"&'5432"M""x-J"A7O>4%,32+32+"54;##'#32+"54;#"54;k""m"}~(nbwOOA3!5#"54;2+32+"54;5!32+"54;#"54;2# -**--w!"xxOHRA%!2+32+"54;!32+"54;#"54\""x--w!"OOFSTFf!#"=#32+"54;##"5f-.[P[3F%\F`="+45#"&46;5#"54;32+32+"543";3#32654&F\~}]F6^F]}~\F6LfhJDlDDJhfzyeyzbIFbSbFIb3%[5')"54;#"54;2+!#"54;2+3#"5r"-- "m"8OO`23275#"54;2+32+"54;5#".=#"54;2#>&H "m""m"%A-"xx # P3%3#"54;2+32#!"54;#"54;2+3#"54;2+A"m"! j""m")OOO L5)"54;#"54;2+3#"54;2+3#"54;2+3#"5# k##k""!k!>OOO@#32+32+"546;##"532654+@%:@<@ #p›2$cIf> OZ#%S N19%#"54;2+32+"543#32+"546;#"54;232654+-x""x*:@<@ pB2$c)OOIf> O#%Sl!#32+"546;#"54;232654+%:@<@ #%q=2$cxIf> O#%SJ,723267!"543!.#"#"=432632#".546^ #O3LgeM?\=_dc>n6 kXGJ[7([ 7c`|+/ &07#32+"54;#"54;2+3>32#"&"32654͗.."x.[`^Y(nnNMmOWw_\s#jkjJNX'/%#"&546;2+32+"54;532+"54;#";X^9A<@ "$r$.3#bI32? O"&S?l&C6?\&j+i\D%+"54;2>=4&#"32+"'54;#"54;5#"54;32+>3231A@$&D7$ -.666_D'D[%9O% @-,:  "6_(!K@rb'J+%#"&546325432#"'.#"!2#!32762"7Y0cd_=\?MegLlN  X "#|`c7 [(7[JGXH gV\pL\["jFpM"6+332+"54;#+"'5432325#"543!2#32654+`\9A<@J<"V3#bJxI32?O>;7"&S<5=32+"54;5#32+"54;#"54;2+35#"54;2#32654+^d9A<@ !cc! ^^3#bRxI32?O"&S+'\>%4&#"32+"'54;#"4;5#"54;32+>3232+"54;D7$ -.666_D'D[--,:  "(6_(!K@Fb&Al"C63F%\&|>)!#5#&54;#"54;2+!#"54;2+32D0""x--w!"PPOOD4>"&5467#"4;2+32>=43232654&'#"4;2+#"&'D[ m1#K,*/ +&,S!2n^H+@A{:#((7p -$ "- m;#((#;t(!+(B8`#,32#!"54;##"=35423#"&=#32654&#ƧUvmP7d( `;U^AKa_6_00_ 6I34J-732654+'#"=354323#"&=#32+"546;қ2$c _:@<@ #)#%S&O00O &_If> #<@L%2>32#"&=#32+"54;#"54;2+354632542#".#"32+"9 &!>/`DWZDr`p( 5#OYm& )#$h kEp${][ O32%#2+"54;5#32+"54;#"54;32+"54;'#plk"v6!i%y%u%8l%ۼ O2%'#2+"54;5#32+"54;#"54;32+"54;'#ljk"s6i%y%u"2oƉ``O``3@D7#3732+"54;#"54;2+3#"54;32+"54;'32+"54;57#p:_6&((},m-%q!8l k#pl&*%P448'&543!232+.'32+"54;5#"54;>737#n&n&11" 85?3d3?588=U(^<#=]C]r99r]Wp4,&2/37'&54;232+.'32+"54;5#"54;>737#XXD?8:FdG88!7d(H!ORaM  Ie[H3wP36:?C332+.'32+"54;532+"54;#"543!2'#>7#737#d1=785?3dFPWY-<+}(^=4oW]r9 fp5? &>6:37##>#"543!232+.'32+"54;5#"5437'#$(HS%_tXD?8:FdG8EG*wc$-N!ORaM  IeyyJ3!"F-\A3%@"2N6 J3!#"54;2+33"=# MO(  [J!##"54;2+33"=#PF)Am(,xx] J&lJ]&D&')B+(r'*')(i5432%#"&5 > d~ Ie~ Gi UnH 3n&H2267632"&54 32+"54;#"54;2+5#"54;2+3#"=#"54;>-wD \wAn5H%5#"54;2+3#"=#"54;532+"54;#"54;2#2267632"&54 -x"=i--w!"x$>-+H'32+"54;#"4;546232#32654&#UvmP666  ;U^Asa_(L L(I34Jl'732654+'5463232+32+"546;#"43қ2$c) --:@<@ #-)#%S+ +(`If> $(+344632654&+327'#"/+32+"54;#"54;2  H?U;>  I(466PmJAc I&>3I = J_FO3@F<$4632654&#"327'#"/#"'32+"54;#"54;>32C  J?fLJhhJ,$D  D2!32+"54;#"543!54266( br5432!32+"54;#"543--"[O>3$32+32+"54;#"4;5#"543!"=!6556((@(ybr%5#"543!#"=#32+32+"54;5#"43"MNN--*!W((>0322326=4&+32+"54;#"543!"=!32#"&546W..;]A:66(ƬUwV>> ' A3e4JbaFk?X rD32326=4&+32+"54;#"543!#"=#32#"&546 ..;E9Z--"MVN^V>> A3-1O??X nP3Q%#"=#.'32+"54;5#"54;>7'#"54;2+5#"54;2+7#"54;2+P*5?3d3?589=2VdV2=9)w]r99r]Xp44pX nMU%#"=#"54;.'32+"54;532+"54;67'#"54;2+5#"54;2+7#"54;2+MUR?#o#?Re0c#o#c0)w.E##E.oAAo\S@R2654&#"#"=432632#"&5463232654+5&'#"=43232654&+"543"AWQ>:R ,.X5bDF]XI=7;21B<-g;YB+BG6F^;$  B >p1DI75@gSV232654+5&'#"=43232654.+"54;2654&#"#"=432632#"&546,.M5Z@BX A-D0PDJ89H4UJa. hQA,#> r B/ S"14(  ) +("E.@b  (7J;$ $n538%3#"=#.'32+"54;#"54;2+%#"54;2+SI76L3&J66J"v.DUD)w;]6+  :cFn*%3#"=#"54;'#"54;#"54;7#"54;2+Iu_66_/)wO$43@32+.'"=#"'32+"54;#"54;2+35427#"54;2+Lq'8X"`B(J66J8(v1yvqzIZa;F4%32+"54;'"=##"54;#"54;35427#"54;2+-(_66_(l/֭~AYObFl$43B32+%#"54;2+32+.'32+"54;#"4;5#"54;2+QQ"v.DUD8X76L3&J6;;6J(g :cE;]6+ (3F432+7#"54;2+32+"54;'#"54;#"4;5#"54;++/-_6@@6_U(=(#438!2+%#"54;2+32+.'32+"54;##"5J"v.DUD8X76L3&J6c3 :cE;]6+ [ *37#"54;2+32+"54;'#"54;##"5 /-_6cO[5n'35%#"=#"54;5!32+"54;#"54;2+!5#"54;2+'~66-x66x)wRn5%#"=#"54;5#32+"54;#"54;2+35#"54;2+i--w!"x--x")wO5k35#32+"54;5!32+"54;#"54;2+!5#"54;#"5Ac.66-x66 RV5#32+"54;5#32+"54;#"54;2+35#"54;#"5,c"x--w!"x--xOd3?2326=4&+32+"54;#32+"54;#"543!2+32#"&546..;I5`.66-R\IcV>> ' A3'7N9?X DI?2326=4&+32+"54;#32+"54;#"543!2+32#"&546p,.;E9X"x--w!">"TN^V>> A3-1OOH??X ?|@19"&=4>325432#"'.#"354632#"&576=4#"Eh "2Z9eHgCSvZ7029  )B>fS7J<+Ep.DYGZ3?9.SXb 2O>IT|/5".546325432#"'.#"354632#"&5764#"HEe3d]?]?Sg+S:7029  )B>*FF&c7 [(7gR :8"D3?9.gb |I?e@<2632#"&547#"&=4>325432#"'.#"3267632j  1$(0f "2Z9eHgCSvX4R( W3:{   0eS7J<+Ep.DYGZ*0 &K !)"Te82632#"&547#"&546325432#"'.#"32762j  1$(1 bd]?]?SghRmN  P;:{   0|`c7 [(7gROeH ; !)"Hn3%#"=#"54;##"=!#"&=#i )wIrr Izm%#"=#"54;##"=%#"=v.tdt(wO[[3%3&%32+"54;5#"54;2+7#"54;2+Biio&(n 9:'#"54;2+3#"54;2+32+"54;)AD)23#Oe{3%30%#"4;#"54;2+7#"54;2+32+32+"54;^^o&(neeii( (8:132+"54;5#"4;#"54;2+3#"54;2+32#B23aO)AD)P#||(sO((n035%#"=#"54;'32+"54;7'#"54;2+7#"54;2+0u>@n+-o)w3n%5%#"=#"54;'32+"54;7'#"54;2+7#"54;2+%uAD o.0n)wn&3+#!#"54;2+3#"=!"54;##"=!#"5d6.JfdD w[ +#3#"54;2+3#"=!"54;##"=!#"5"d"m"8"cDxO[O[Jn33%#"=#"54;5#".'5#"54;2+3275#"54;2+~6 E%!.4x6sK)6x)w#35`n4%#"=#"54;5#".=#"54;2+3275#"54;2+^"%A-"x->&H "m")w #J39%675#"54;2+32+"54;5"=.'5#"54;2+542<=#6x.6 5 (>N x6 `(-ZY"0/j`:%"=".=#"54;2+542675#"54;2+32+"54;5B(%("x-N(8"m""m"8799J31>3232+"54;5&#"32+"54;#"54;2+ E%!.4x6sK)6x.vB#35`2%5.#"32+"54;#"54;2+63232+"543>&H "m""R%A-"x) #O@<%54&"#"&=#"&4632#";54>32!3267632[vv 4T,f&%;9% !%& "2Z97Y3# EX4R( 3 YY &+ e4R7   7J<+)32!3267632#"&'#"&4632#"3hv fd6 Ze` nQ/b  D`%;9% !%DRQESliO_ 8|[4R7   e@N%54&"2632#"&547#"&=#"&4632#";54>32!3267632[vv  1$(0 f&%;9% !%& "2Z97Y3# EX4R( R1:3 YYF   0e4R7   7J<+)32!3267632hv fd  1$(1`%;9% !% Ze` nQ/b  D1:DRQP   0|[4R7   SliO_ ' !)"q3,P"|ql M_"|$3>2326=4&+32+"54;#"54;2+%#"54;2+32#"&546 ..;WGJ66J"vK[qV>> 3 A3e:D\Kk?X FD42326=4&+#"54;#"54;7#"54;2+32#"&546..;E9Z_66_/#N^V>> A3-1OH??X 9n'3-7325#"543!2+3#"=#"54;##"&=432b! /QI~66$:P'wB%'*"Xn,7325#"543!2+3#"=#"54;#+"&'5432"M"=i-J"A7wO>593723265!32+"54;#"54;2+!5#"54;2+#"&546,.;6-x66xV=> A3:?X R972326=#32+"54;#"54;2+35#"54;2+#"&546..;-w!"x--x"V=> A3OX?X 5n'35!32+"54;#"54;2+!5#"54;2+3#"=#"54;6-x66xI~6wAn5!5#"54;2+3#"=#"54;5!32+"54;#"54;2# -*Eq--w!"xxwOJm33%75#".'5#"54;2+3275#"54;2+32+#"5KQ F$!.4x6sK)6x.~(#35x`n4%5#".=#"54;2+3275#"54;2+32+#"=%A-"x->&H "m""^) #w nQ3.%##32+"54;#"54;32+3#"=#"54;#F.J"cd=Ja\w4n%.32+3#"=#"54;##'#32+"54;#"54;k"=^"}~(nbwwOOq332#!"54;#"543!2#@?  O&f|qHY&| O&fjrH[&j N3 B+&k|q?\&|2@)%265".=!54&#"#"54>32nvv7Y3# X4R(  4T,f "2Z YY)n3%#"=#"54;#"543!"=!_56()wbrn#"=#3#"=#"54;#"543!Hu-"M[wO Q"jp N["j\@<32+"3267632#"=#"&54675.546325432#"'.#"60=IX]FDb?jZt;3'1iOV<R:>QW7@57ID1pH^I6GB+BY;g-75'&46325432#"'.#";2+"XB@Z=`Uo .aJU4H98JDP0D-A u(41"S 6K9(  b@.E"(+ ) 993/23265##"&=432325#"543!2+#"&546,.;6$:! /QV=> A34B%'*"'?X X9.23265#+"&'5432325#"543!2+#"&546..;J""M"V=> A3>7X?X 3%@4?FGTD3::Z-<3I732+"54;#"54;2+7'&546327#"54;2+#"/32+.'J66J#  )V"v|*  2.DUD8X76L3&)h  $@Z(  ,b :cE;]6+ F=3#"54;#"54;7'&546327#"54;2+#"/32+"54;'_66_U%  +7/X+  1R-OC  %+E% +A)oD39".'#"&5#"54;2+32>5#"54;2+2/83 0Yn#KYC$9"L" y =)xTNE^'/% N2! r)7C+4&#"!2#!32+"54;46232+"54;]ACYtL#qo#fxHZ^DmOVuuV9+FD +272+32+"5473'#"&5464&#"3264R- JJ6fCo]}~gKLefKLfD%3?.*mZ{[]yJbbbcQC2232+32+"54;4&#"32+"54;5460K)YY#k[B%;!f#pC"1<,mOD^'0# 99Wt"03/"&5#"54;2+!2#!32>=#"54;2+'Xo#KuYC$9"e"pySNdE^'/% 99Wu"6B (#"&54632!"'"=423!24&#"326&kmlm7+((!Av8zZ[z{Z[y֎kl#t8(HZxyYZxy:3#"/!"54;#"54;2+!!3 F__7&Q ; Ed)Q )1C$4&#"!2#!"54;46232+"54;]ACY~6#qo#fxHZ^DOVutW1 T?1<4&#"32+"54;4>3232#"&##"'&5!46#32765fTOkK# /W7Z,'-0_5 %2/&wBaiM;.A5&sP69R)h$c T4$+*P5<3 ."&54>;5#"54;2+32+'2>=#"Wp"1<-`"__pW$9"D^ZxW.I) Wu)'/% ZCE^;31>3232+"54;54&"32+"54;#"54;2+ "H)Rq#fZ[AA"b !yR%%C_^0?2)"54;#"54;2+!542;bbb* 5P37%26=#"54;2+#".=#32+"54;#"54;2+3s4L$fYO64n#f[LF\Wo)Q8dXJ.&<  #!"43!2#"&546324&#"326 s0kmlmgzZ[z{Z[y' (֎klZxyYZxy)132%2>=#"54;2+32+"54;5#"&=#"54;2+,,B -y##:aYn#KY&80BLxTE^k;#%4.#"54;2654&+"54;2"5>)ASQ0Eg9(b(%+84)5O<+D/\5AH*232#!"5432;27>54&#"32+54632;&#"F6v54( 4>Dy^M]ox`fLMN'> ?#XU5YK'0' *@^xbN(&^}FGVEC#4&#"32+"54;546232#'"4;]ADX$p#qoJ4xHZ^D11XsuV(!94!()"54;#"54;>;2+"3232'4&+!%!:JK;6$%vRv>zYGwU>O(8-rQ@ZK4/".5#"54;2+32765#"4;2"54&+ )6.#KW=S/L_("(G ,I-NAbY+N*|+',<1#Z]2%4&+"4;2654&#"#"=42632+"543!26g1\_1QT5*= (>OIj9&.ED2A%7;\(<+0@^0.XA(E_/3N8:3)%#"&5#"54;2+32>=#"54;2+%O6OnX%W;)>M"32#"'.54632'4'.+"4;2"=&'&#"D*H'! 4T,mG-'fbM).2:<6(8I1M*  &  ', G-RFgH<# (/*+$2?-Y*2C%463232+"54;4&#"32+"54;hpXVn#K[BCZL"xSxtWOHZ^D+J$0#"&#%&543237"&546324&#"326 !lGW$bab)mOPlmOMog/& /_'dabaPijOPjk?C64>3232+"54;4.'32+"54;"32+"54;W.[>\s#V ?,#o#CV$o#x#A@'qZO-8'Z]D9@H4*432#!"'"=432;2767&#!"=4;2654&+!2>>g1* '=[' B%D9[8%0|X.2U>:&)]$$.f\/>8d6Ne NC+463232+32+"54;4&#"32+"54;^pXVlPP#K[BCZL"xSxtW1(OHZ^D)13(%2>5#"54;2+#"&5#"54;2+,$9"L"pUYn#KY'/% NWuxTNE^ '4/32675#"54;2+32+"54;5#"&=#"54;2+|XA;^"e.^>_Tp-pWK]W7*T{V&\@6*2C$463232+"54;54&"32+"54;hpXUo#o#ZZ"xSxuV11HZ^DAR*4%2654&+"4;.=4632+"&543232654&"e=xXnnWzC8VonYNaFG`H`clruVX?5''G[\`K\GE]KD#52#"'!2#!32+"54;5"4;5464.#"32>K[tuWqBdo9\\| 56Gb*>2.D#DlQTiX(JK([p+? RA+? -.3%@2Z<,3:%#"5&'#"=4325.547>;2+"4&'>.[>Y9X9PX4>8WX $="A>A>IL>W66":r0@AB237RI-2 62 FZc.v`n[2632+"#"&5463j0 ?|&  A-. 4- /C?S 432#"54623254#""5)4[F  1:I8({3,WT ,078CL'763232# T  2L#T  2 Q1"&5#"54;327#"54;327#"54;32+5#"'73#L#45#L"28"K#L54@ 6N  *S,S)8DD4B-+2#"=4&#"!2#!32+"5473#"54;6JC]L1;[d59_FbNRk1Ah7Wa+BF!,232+32+"54;5#"&5464&#"3264R- JJ9dCo]}~gKLefKLf%3?.Z{[]yJbbbc!BC7232+32+"54;4&#"32+"54;#"4;>/%:"[[.qA5!</."K!<!) 3< $T*A,0<$232+5#"&5#"4;!2#!32754#LRaMB#*2#"'32+"54;#"4;6.#"!326Caz}]nD88aJfHJe` cBAb}Y\zZ*KZF]\G(;532+32+'2>=#"Wp"1<-K#`_pW$9"D^ZxW.I) Wu)'/% [CE^5B=-232+"54;54&#"32+"54;#"4;6C&; #n"B20C)."KM!* /902A*MIB#!"54;#"54;!2jXVB==3%#"&5#32+"54;#"4;3327>?#"4;32+5%7#"4;32+"54;5.C #n"C1#:#L/I"2'p/9$*K5<-232+"54;54&#"32+"54;#"4;6C&; #n"B20C)/.!JM!* /902*MGOa&2%+5#"&5467'&54;'&5432+32'4&#"326OLEwblQC_BsC_}#glN[cmOPlP``X{ H = F_NlrHOjjBBR/232+"54;4&#"32+"54;#"54;6M+? U+>.()..#LM!1(=-8 #T=MP<'%#!"54;#"54;546;2+"324.+3BAA51?NQn(%7+PU>T(m|S1H!6)=,46;2+"32+5#"&5#"4;3275'&3 c7`ROM]B2#!"54;#&=42;2w0D(&DFF$ I BQ;"&5#"54;327#"54;327#"54;2+32+"54;5#"'73#L#35"L"28#o##^54@ 6N *S,S8DDGB-%32#!"54;7&+"&=463232654&#"&wV:%/acb=#lP)C' -F#&<+$aQ- B/:Pj)1&5 1232+"54;7654&#"32+"54;#"4;>M,D PUPF.4@2/."K(F+$ '/(:T*>(%,%+5#"&5#"4;327#"4;32LSbM?F2Jy$#!"54;'"54;XX((S BP<7232+4&"32#!"543?#"&5#"54;327#"4;673#L#8@ nn4273#L#35"L6N D*31;N *S*;i e(2#"&#"32+32#!"54;#"54;5469fb$0@dYYZe -)?T?9GHRZB<07>.54325.547>;2+32+"54;"4&'>CzX9PX4>8WX $="nPdZA>A>IL>WWB0@AB237%J[I/0 62 F,<232+5#"&5#"4;32754#LRa=#"&546;#"'32+547'&< :?5 ^| ;>5 ^| 8,~  d6| 8,~  f4|<0232#!"&5463!4.+"&5463Z 4 I ('%    %&(  -3"&54676754&'.'#"&546;2#"/^5S #)I Q(,$ V$4# `'%   #>)   9*<!"&5463!2+#"&5  Y     ] < &74632#"&!2#"&5.'.'!"&546_   +JW   #'   PW %&%   @"&5#"&5463@  Y 4   #"&5463!2+"&5} $ ~     ] <*2#"&5.'##"&54>7#"&5463}'-    (%&;  W #>) %&%( :  3/  )3".5#"&546;32>5##".4?3#135 ^ ($'j$'( ^s  ~ !G2  &%(  (&% ǧ@"&=#"&5463@  Y    <82#"&5.'#"&5463T13   ($'  !G2 '$(   <%2#!"&546;>=.'.'#"&5463T'- MS &%(  %& #>)NY   (&%%'   <i!"&54?>=!54632!  I  +  ~ u-+< !.'72!4?#"&5463hi (%&'- E*K h&%( )#>))*K  <02!"&546;.'#"&5#"&546;72326kR_  ($'eh    :}.H_  '$( h    uu8@"&5#"&5463@  Y l k  32!"&546;4.'#"&546*IX (%'$ OQ  &%(   <.754?#"&5463!2+".;76=.'a*K %+P 13h &$I (%&o)*K  0(O "GAhn&% I&%( <$7#"&546;3>7#"&546;#!"&5463B] I8 %""V ( AR )  I3*  TP  <8*2#"&54.'#32+54?#"&5463}'-   ($'eh *K #>) %&( h  >)*K  <32#!"&5463!2>=.'#32+54?#"&5463}'-  $%&(  ($'eh *K #>)  (&%&%( h!  =.&K  <875#"&546;#"&5'5#"&546;Y  YZ   Vd "  F2"75#"&546;!"&5463!55#"&546;QY 5 Y   UT  R"  C18%3".54?6=!"&5463!#"&54632 ] -a     "  7"7  <!2#"&5.'.'!"&546P+JW   #' PW %&%   3:032+3267#"&546;#!32+3#"&546\ 2Q?6 "2 `' <)^ 52   r0P  #3  r  *2#"&5.'.'#+"&546;>7#"&5463}13   $'|+ $0)Z  !G2 %'# ˂  !65  Z"&5#"&5463#"&5#"&5463  Y P  Y 4   4   Z"&=#"&5463!"&5#"&5463  Y T  Y    4   Z"&=#"&5463#"&=#"&5463  Y P  Y       ;r\3"'^<4\$$;\ 3"'73"'^<4^<4\$$$$fl(#"&5472654./7sRSt ^]%8:2#K8CQ]\R+9,%BHHB*63'SWjl'7"&54>7&54&"&''657324.'326^**pr)4X&2#$ (0l"$"0F@[DCZ.1@! 9,P[[P-E%="G ?D a[1*'.(*++FBEF]H"3#"&5467654&"&54624.'326yVWxJ;f9P8!MnM*+$#-0 851cHGdTa`U=^~(89''(6NN6 % (9#$>+(:4 '5'ELL5B#'3#"&547&#".#52.54632632'"32654&#VKEP( )/DX>f- <Ǘ05)Y@5*)3EU>@^USC:AEJHG!-9$G192105) 47EHS( /4J&Al!.:27"&54>7#"&'5>26=4&#"4&#"3260QUFaec* %5HJ3":6&.,,cP76ON78OGRH[ ?bEFdcG!Ei I5,3K-7!&5A/0@7OP67ON@d,'654.#"#"&546326324&#"32()G+ +F6FOHM?+#7>A>46<@1r8S-IV<1*)/3O8bFOi..m:6ML71Cge&'654&#"&54632G ?f;=d$ G{JKzQ\ QF:ZX<%S WVIoncH'%4&'#532654&'7#"&5473265W00?1G" 4*;#vSTuPC`EDb(=N$G1, 8(.QZNRbbRTA5ECMMC5#@'654#"#54'.#"#54&#".#"'67.54>3262632#G@>##?$ >c@VGZ(z&;U9A*+`,b+m^[\{#RIWJ{H  5,>, # /J14&&&&fl'#"&463254&".546324&"32hGHggH[0__!tRSs$RrRQ:9OdhhGALMA,&Pb`QrSR9:Qcl+#"&#">32#"&5463232674&#"3261WK0EK(IggIHfZ>K%0QR:7TR9;QOE0 %ffgH:>[ 9RT7:QQ:e#'654&#"#5&#"&54632632E>.60 #$56.>ECEA((CECUaXJ;P!*0R^\S3<1*CHIC8M$-%&# E=3ID0HN]H14.'>73267#"&5473265#"&'7326B' 2+2J-xUWxN AcHFc:IM7:E$0-(80# ')Sb`UTA 9AFKLE& 7NM</:85d2'>54&#"#5&#"&54>765363262$"050 #"7d>E6VWY#$^I\5A(&D0S4!&>9R=:`cSMq@+,(WKuF88fjl"&5332654.5463"qq$\BC[ 4(7! P^^PAIIA /!&#$%  75D#i (>%".54>7&5462&4&"24&"24'"&5472>#6dd6&K5!:P:"%4%%4%$4&&4{ :P9{.WvW.>m_88_m>8\X?-'99'.5a4$$4&w4%%4%;(99(;7_U22U_5H#>"&5472654&#"#54&#"#"&5473267>32632#nqPB]Z+-#3&(8E'!-!0"2-AV]^UX>:AEJIFb+) &5>)50 %+&!.''@-4H$8C%26=4&#"#"&54632632#"&547326=4'52'4&"326D7%/RIKVVHE*);4KB+kZUs # ]IHZSArB@;:@0-M&6*+8GliJIh//L4ML!A:X_ZS"( !GHIG%Z$y:SS:=RSbL&"&547265#"&546;&'>tuMAb`+>@+8V?GT]^TR@7@DJIDv,):,@5-  ,5B#6'>54&#"#4654&#"&#"&'63&=4632>32#+,09('9$;&(9QC[+ p 78\N7K'=#7N!;Y./UJ'89&X&98'EHxqS J}E6MA#M6hH'"&5473265#"&547326=3lpO! [GHVU'@cMN10g$U\\UQB5%FGFG"1R@>M/1=X/Y@l*5"&46324&#"#54&#".54632>324&#"26opoQk0.!0 #<+)<-P9K(7 /E%XCBZZYO\\\C!/@59I*;<)*-1$8Q>!EWAFG@AFGjf*"#4632#"'632'654&'4&#"326,EY$^C5LgA-)=aTn$!!\7%5H++2Q^3DvK5?ORuTIMKCA]%7`6:5G#632654&+5232654&'.53#"&547# mlKS;$ 19$#" # '5,"N\3?7N)ocI54&#"#4654&#"! &54632>32#Q1"5#4 5=F1B"91FjrI)$"1. / , /*&V 1F: F@LH HT%'#"&546732654&'5#"&5463254&#"#"''65332632673'.#"32 o3B~TVH;hqGEiA4P72EF16Q#- *$'!'8@![3g&)"21#2dR4PUWQ;N ZBB@A1<IE21FO%:E\U:'Kh1/&1D1fl"&5376324&"26ss$v(Rt$__]]Q__R bPAMLADII2H&]%32654'7#"&/+"'673264&#"?'.#"&546327654&+532T   ;Z3#. HO,=G296+$#23" ,)G=,$ +==#+ ( #J#4!A E,2G4 2F2.z3! ( &>+>$-c $,<jlG2#".54732654&#"#532654&'&#"#53254&#"#"&'73>/))77#;A#1S>A13@^6@ & j)$ . gH 2 9:$7#2 5= &C&8A2>4%+ /9&8$)'$6+D.r*Zf32654#"#"546;2654&#"#"546;27>54.'&543232#"&'&'#"&54>7>32>54&#"'4?0;R= % K0* &  ''/ !$4+,$%eGNo  !w4 .HTM09Ag<(F:m '( !5#34!4"OFY!]  r. =4&#"  s&.U;54&+5326=#"'7327F:8G4W`4.<9+.TN/Z@F%NpTVdhg]`h@kb;8T-  &H1@\ lNuHm8#"&54726=4&+46;7>54&#"&54632&#"32jMLkJ=VzVJ4& -;A)1@C.%50.*#7W,$4!KZYKL=1xDE<&4J =-(A?1.C!/#*U7)FUWk,5##"&546;5+"&546;23#3'54&+";5#"26_bDE`bD0_4HH384H___4$8#44$_06MLlLDbaEDbXH43II3X$X|X$45#%3ނLlKK]8Xe HTFc{0+3'+M35H37?3 97"3264&7#"5432654&#"#"&5467>75#"54;2++>=,*>>Yw^MmlN;`/LCY\ M@)2+#"&5463232654&#"542654&#"$3- Kt`fpscdoJS[\RQ\Y3$'7s} monlq\p%32#!"54;4327#5@ ;)ohhH@C32+#"'#"=43232654&'!"54;&546325432#"'.#"g*1tZj?bDF]30dTiOV<R:>Q0 A+$GI^Hp1DI7,/#WBY;g-54#"!6-Y3U{yWjghR:+424:!<F"B2 29gly|W[B%AfED;J36%2#"&54654&#"32#!"54;#"543!2+632326 /<2;#(?%2,7B"  .7Dx)N+&2&3,"&543232>7#"54;2+#"54;2+&6  $-n*.p:SF1 .7"Q 'gg O3$TG3-+3(D3"*37463235#"54;2+3###"&264&"yP83K.HKˠT,L48'8N99N%pPA1/2CN88N8h@-2+>54&#"#"5463232#!"54;#"543V=Q^FR?]kYvbon<n_KISH7(*[[a=3+3#"&54623>=!#"&54623>5462!54&"=5()7  !!3()7  !ts=\]j%;8& "%%;8& "$UttIC]]Ch3%%32#!"54;#"543!2+35432#"=#7?)-->3 4$@6?72327.54323265#"54;2+#"'#".546654&"y 03'-!)ac)!-'5J .#1881*539> J+#H9669HCu8:@LL&"%WVwOnVUoOD3 3#"54;2+3#"54;2+# z<m58mDj98n>Cmp $ B3'5.54632"54&#"#"&546324&#"32> 6 ?(1E(.  $%&AE(ZoccXS[R?\`@+A":96&*$Q:(9'(1L-C_/^_Iv6MHT&0H3)2#"&=4323254&#"32+"54;46SPmOQ1N?tU;>W6m3_FK\9:,0!%~3I<+z;U@@:%4&+#"=43232#"&=4>325432#"5.#"326=):U'@G%f "2Z9dJhCSvXHb$=--P14Q-eS7J<+Ep.DYGZS O3,!##32+"54;#"54;32+32+"54;#D."m"cd"m" 2&C5%32#!"54;5.##"=432254&#"#"=>32_!-$#/"V326541# #</>5->N1 D^-O?" &-& ("2'%4fLXDc&:G<<+0B!-9C8ZU@%1%#"&546235#"54;2+32+"54;52654&#"s`fpspJWZSZ[RQ\Ys~~ސonlq3%33!32+"54;#"54;2+!5#"54;2+32+"54;6-x66x.6J 3-"#"547675#"543!2+#".546232654&*#839>\t~c/Z7"  NlRhg\\}]`|$!  HeORg*.30732>7#"54;2+#"54;2+#".54;2#PD #)n*.p8U#"+nï5*I'ei j^3%@.93*%275#"54;2+#"&5#"54;543232+:IIKZaGTJJ>"4C8 ww&.?2@*D<*7267>3232+"54;.#"#"&5432h(E0&C Pn$F/0(A),4'b(>51S,:8.+;2"J3g3=J3A4>3232#!"54;5#".5#"54;2+;54&#"32+"543V2L-No_ .)%,-%="V<!>'-1CkJQ 3 $0:R1%R@ 572654&#""&5463263232632#"&54654#"S[\RQ\YTfpscKe2,<" $(&@@onlq)}^Q)F+& ,9Q *$H3!%35432!54323##"=!#"&=#A8  )Irr IIrr I>=6"&54632#"'#"&54632327&54323254&#"f:Kj"UBO7MNL %%2@: (:R:#5A{UR9 k6w )D&]dM2Uv^j5#@/8"5432654#"#"5467&#"#"=4>32632'2654(&v'-!)ac)!-'4 0"1881]EB9>+2H9669H+@",LL[MCCwOnVUoO O@.:"54>54#"#"#"54326;23&54632632'654#"32'p9}</  o\Q"'/+!>"+? :9)%G!(%3, GQ >=B23267&54632#"54&#"3254&#"#"&54632#"'#"&546y %%2@ 6"%3/61R:#5f:Kj"UBO7MNL )D&]d!'E[]EAQ/- )]^I8^QAN;g-<7010VnI (03 )#"&5#"54;2+!5#"54;2#!3265sTUr"J32#"&'##"&5463234&=#"54;2#32654&"!"1LT ;'AJ("<1GlpW67 14 $68"!3+T "#"#(jOugIAO74([wJu7:3>H>7MA/ /(+26' ;:T3"%32+"54;5!5335432#"='5#6KTOx/+}(*+2\35%#"&54623275#"543!2+632#".#"32#!"54;$ %R )/?&,L   ,P &'&R $L D >H%4&#"326'>7.4632>32#"&4>54#"632#"&544#"36,8%$98%)4[ '"(1+$ -) $C.UQ20Sp!76D64S2%%B(#%  -'* )LE?AE;>Bw&23;7"54;2654&+32+"54;#"54;232+"54;4.//JhU6K66Ik+ 9@+*D0-E]?a0 ).N*+K-!2%3#"543!2+3275432#"&5`(UQ0 A41tZj?bDF]4M[M4iO9@4B1# $GI^Hp1DI7*4?2BY52;3&%"5432#"&5##"=!2+32654&;T B.UE8`1>8:<M6+3!_fRTGA&'7H3$#"54;2+32+"54;#"=432rZYYZ f7Z3&!"54;#"=4325!#"=!2+32#*Z66f7b O39\@6)/@*3#"54>32632#"&54>54&#"#"54#"2654!)ac>a,C)O,>Q% $$47:&F9>95Dx33J,.L  #3,4`twOnVUoO2&@ ,4>%4&#"26&5462"&5467'"&54676264&"264&#"#""$"88D879B9 ~} 9B9#"$""$"$""#r!78@88%I0HH0F(sA#1QQ1$AZZA$1QQ1$C>//>/\ 88@87?@ !5432!"54;4632#"54&#"=`EU.B 8:>1|Rf_-(*5G?3/?@&<3#.72+ =#"&5463235#"54;2+!54&#"3#3265!K)+<8'%5JK$*.>=/JhhF2.CB/OO**!.)hLTTL4$@6?"&54654&#"#"5467&#"32+"54;4>32632'2654 03'-!)ac)!-'5J .#1881*539>M J+#H9669HCu8:@LL&"%WVwOnVUoO+332&@/32+"54;4&#"326542#".5!2TP>0L0#  ->0Ea(|T9I6%Lp_Oi2(<@a5 N>Ne &>pI(m&23#"=32+3#"54;2+#( h F b(03=732+"54;#"54;2+7632#"&54654&#"32+"54;766Kg:"$*-6J;b4"> ,B - +<3.(0D$#"&546325#"54;2#4&#"32*]@^~}]rBf`fLJgfKL=M5y][{ZqbbJIc L@:%632''#"&5463277#"/#'#'&#"326?3730 &0(( IHfpscJI ((0&   qQ\YT9=   <<<31@*}";02<<<2000f4lq&f/00#W@J"32675#"54;2+32632#"&'#".54>325432#"'.#8# (>'%/-k(!. >.: I=C]1+;4cC`$5+9!:(1 3A C6 -UdAHn?' 6[&69C1<F".54632327.54323267.5432#"'654&#">54&"*53 03&*'ac+"*#*X.MT5-.fA425= 7">&"%W J+#C8779C}`o=U]XGG12>K>24$T4t$nVUo]3%@ *2#"&46.'3267654&#",jgh/E(&@-zV4\/yW6]/F( &A@yK)11(02i>\.4l?6*10)3%C/:"#"54>32#"&5463232>54&>54#"!!"-BuBhR:+4!6-Y36b824:!< #3A #FK6Qr|W[BB"B2 29hEjV%AfED;H@ #4&#"6&54632#"54323264 $30' 0L08KgQV%ZED_]#8# U|32"&=4632#"=&#""32654zc8axyyS2S7KIBbcONd"(\Y[_^\f)VC1~UFGFFJ>=L"&54632#"'#"&54632327&='#"54?3737632#'3254&#" f:Kj"UBO7MNJ %%2@:    #(:R:$4 @{UR9 k5w )D&]dM3M + 3000+ %+ w`j>\#:724>32#"'#"=.5462654&#"542>54&#"S  "G3ZI)F=~gE4 5p4dj]*.<1CM  s/2T9'B JA]^*2  IMDFJ (-81A<35727>75#"54;2+#"&543232654&#"#'4&54R 8 2"ZZV[0>)H6PL&1 L4$G%,2\\|^?*6%_USf"+%{2&j5">3232+"54;54&#"32+"54;4632"54&5&F.FY--D7*7) -.hNFe(IAb.&O=-9!0 BZI3 1;@E2#'#.#"3275#"54;2+#".54>32776( : -01=- #/L0FIZaNn9'8J7)'+ /  >00+$ 4`?<(3"4/VeAJp<%  2%'7›t/#'#3'g{#37{#"54327#37 !{#&547537'$$  / - -+F # # 33F  g]]CF %*2#"'#&47362374&'>'&"'2/F > jk , lkP=$//#> !"""$!0#<9&gLRTMLK k* MO*o8n99WM*( #3'{0 #'#33g {H #5'#335'g:{%'#57'537oojinnijy/{{.Y.{{t#.4673t" }>0q/P"Chd/J/_ q/O:#3:g{t#.4673t" }>0q//0/5%dev[YusUvif\ #&54632\""+  +!!'#3<{'#&5473****< 35 ] 53 ij'7'Û淶X#'#7'7'77'((''WF # #33#F g]{]qi #'#7'7'ﰰ''Ûκ淶= '#'773=/L.M #'##7'337 (''< '#'573<~~~//L '#5573nnAЀ-|F #&*-##5'#'737533'''5'/5F SS MM '>sNSq=COtoUBgb`![[ ߥ=G9Gpdn #'#3735#'5'5#'~~~~j(j~~~~j(jg{͕͕~~3%4>73#4&'#3'8E5evXeAj>*Uqe e:3:4%#'5573'7rnnnnrrr--/݈#3gPV%##5#53#53533#3[ZZ\\YY[ tt !hh!Lk #"&4632k$#$<,,<,k#89 '#75'37oNooNo8]v '##73'0٧s3%&4>7.533>53#4&'#3'8E5ev[YudevXeAj>*/vifsqe eZ#4>753#5.%4&'>Z*\>Wn!/9,WmhBK_K^_Z?wYi?e:(jg[\ 8O3#~X$H8#~XD+"}o%"}sE+h3#}i%Q\#}RE+3#q%\#qE?S"vqTS^"v+"}k'?G"}sG+i3#}j'?]G\#}^G+3#q'?G\#q G+T3)5232654+5#"54;#"54;2#"&5464.+3265,.""a]A,#>  'Q5Hpq Pj8h.;$  ,. YYnH6}JhhKJhg\YM+;$  FhjLMjjKNj+C3#jY'?8G\#jNG+ :B#"/&54632!5432!"54;#"543!#"=!35432#"=#"4;2r  r =:>66ۑ\d d  k}X(v)K((?"C6q+ 9A#"&54?632!5432!"54;#"543!#"=!35432#"=#"4;2`r  r  :>66ۑ\d  d k}X(v)I((?"vq+C3#jY(?8#jNH+o3#k(?i#eH+S"|{?Dn"|+"}o)i#}I?2"q Y*?F2"q J5'"}o++'"} tK5Y'3#}Z++T'\#}UK5'"jj++'#jK5S'3J232654+5#"54;#"54;2+!5#"54;2+32+"54;5!32+#"&546T,./-x66x.667A,#> r Q/;$ +S'\D232654+5#"54;#"54;>3232+"54;54&#"32+#"&546R,.0.6_&F.FY--D7*7) -.A,#> r Q .&O=-9!0 /;$ 5f'3#||++f'\#||Kqq3#m,\np#jLq '1;#"&54?63232#!"54;#"543!2#%2"&4632"&46r  r  k?((d  d O((\#v+<"vz.?#vN+n<3#}o.?j\#}kN+<3#q.?\#qN?f3#}g/\h\#}iO?f"q`\h#q?3#q/\\#qO?C3#jY/\C\#jYO Q"vq0 Qs"vP Q"}^0 Q["}P iQ3#}j0 jQ#}kP2"}^15\"}Qm23#}n15n#}oQ23#q15#qQC23#jY15C#jYQ3% %=#"&54?6322#"&46"32654&'2632#"&#"#"&54632r  r  jghhVzzVU{y0 ?V &  AWd  d slޛ$\ar. 40- 10H#v3% )A2"&4632"&462#"&46"32654&'2632#"&#"#"&54632(([jghhVzzVU{y0 ?V &  AW((slޛ$\a|. 40- 10H"ju3% &.#"/&546322#"&46"32654&7#"4;2r  r 8jghhVzzVU{yd d  slޛ$\a^((H"C>q3% %-#"&54?6322#"&46"32654&7#"4;2r  r  pjghhVzzVU{yd  d slޛ$\a^((H"vq+"v|3Fq"vS+"}l3Fa"}S+M"}l5TZ"} U+nM3#}o5To#} pU+nM"qXTo"q +M3#q5T#q U\"}o6g["}V\O@#}P6gK#}LV\ CS2"&464.546325432#"'.#"#"'#"=432326#"&54?632"(&>JK>&iOV<R:>Q&>KJ>&tZj?bDF] r  r   (j!*4';P5\)6;-( 7(BT@e-=BHd  d g#}\ CT2"&464.546325432#"'.#"#"'#"=432326'&54632762,(&>JK>&iOV<R:>Q&>KJ>&tZj?bDF]  kk  (k!*4';P5\)6;-( 7(BT@e-=Bm  XX  g#}#\O"}pgK["}H"}l7+"}]WHl3#}m7+^3#}_WH3#q7+3#qWHC3#jY7+83#jNW(W03#jX8+X#jYX(g03#c8+d#`X(803#jN8+8#jNX(0 5M#"&54?632#"&5#"54;2+3265#"54;2#'2632#"&#"#"&54632r  r  %sTUr"J[CB\J0 ?V &  AWd  d LhhL0q::^"C>ZD"vq::^"vZD"j]::["jZD"}b::]"}ZoD3#}p:t:#}uZ(0"}];3%["}[(0"j^;3%["j[3%"}_<3F%\"}\g"jq=sp"j]g`3#}a=s`#}a]g3#q=s#q]+'\#qK+"j\W:"~Z3F%"~\H&#bDi#}A jO3#}k$H\#}]D O #0v$H#SD i#vzHn#vDO #C*#CgW a #1vHd#4 O 8<M2632#"&#"#"&54632!32+"54;#"54;32+"54;'##"/#"&47j0 ?V &  AWO6Ox%LGplj  kk  . 40- 10*l  XX  H# jO"j{?H\p"j@ O 04G#"&54?632!32+"54;#"54;32+"54;'# 2267632"&54r  r  6Ox%LGpl>-H"va O 15H#"/&54632!32+"54;#"54;32+"54;'# 2267632"&54r  r 6Ox%LGpl>-H"C$j O !BFY"&=4>7&#"&546;2!32+"54;#"54;32+"54;/#2267632"&54  26  8&(4!6Ox%LGpl>-H#Sr O 8<O2632#"&#"#"&54632!32+"54;#"54;32+"54;'# 2267632"&54j0 ?V &  AWO6Ox%LGpl>-H# jO"|q?H\p"|@+n3#}o(?_#}`H+ 'Jv(?#O H+"b(?4"H+#vw?l#vN #C:#CXQb #2v&X#(+ ):R7!5432!"54;#"543!#"=!35432#"=#"/#"&4?2632#"&#"#"&54632:>66ۑ  kk  0 ?V &  AWk}X(v)l  XX  . 40- 10?#+n"jqW?_r"jXq 'Uv,\#W qk3#}l,\ip#}jL3`%@#}a2H\#}]R3% #Xv2H#X R3 #vHn#vN% #CF#CSKb #2v0c#33% "->2632#"&#"#"&546322#"&46"32654&'#"/#"&47j0 ?V &  AW1jghhVzzVU{yW  kk  . 40- 10slޛ$\al  XX  H#3`%"jqkH\q"jl3U#vbHN"vc3U#CRbHN"CI%c3U #TvbHN#Yc3U#bHN"#c3aU#}bbHYN#}Zc(\03#}]8+a#}bX(0 #Xv8+#? X(u#vq+>"vr(u #C5q+>"C+r(u #Yvq+>#@r(u#q+>f" r(^u#}_q+^>5#}_r3%"C"|<3F%c"C(\3m%3#}n<3F%#}`\3% #Xv<3F%#X\3%"c<3F%4"\?G"I*?G"*?G"V*?G"c*?G"W *?G"d*?G"X*?G"e*OQ#I OS# ^#V% Em#c Ej#W Ck#d @#XE D#e! J"I."."V."c."W/."d..Mg#I EGg# ?Xn#VPXj#cP}Nk#WFWm#dO5F"I05F"&05F"V05F"c05F"W05F"d05F"X05F"e 0Hf#I !Lf#%m#V[m#c ]xk#WQyj#dR#X8[#e7^"I2"2f"V2i"c2"W2"d 2O"X2o"e2h#Ii#*No#VEg\e#cHuJj#W6cXj#dZqX#XrqX#efqH"I8H"8H"V8H"c8H"W8H"d/8Bd#I" Sd#.tj#VOxh#cSpj#W8Ksj#dQN+)"I>+)">+)"V>+)"c>+)"W>+)"d>+)"X>+)"e>Zf# 5h#cdm#dg#K1+("IB+(" B+("VB+("cB+("WB+("d/B+("XB+("eB+f#I"9i#"'kj#V"Yuh#c"cfg#W@"Toj#dS"]p#Xb"^p#e:"^?G"u*?G"*"u ." .5F"u05F"-0"u2"2H"u8H"8+)"u >+)" >+("uB+("B?9G"?9G"?9G"?9G"?9G"?9G"?9G"?9G";VQ#H 9;VS#H&9;~^#H@9w;cm#H9;ij#H49;wk#H:9;\#H'9;Z#H)959"59"58"58"59"58"59"59"8Hf#H868Lf#H868m#Hs68m#Hw68xk#Hh68yj#Hi68W#HP68#Hx6+8("+9("+8("+8("+9("+8("+8("+8("8+f#H8689i#H@68kj#Hi68uh#Hv68fg#Hj68oj#Hp68*#H/68(#H16?GY"|*?G"q*?9G"?8G"*?9G"%?G="J*?9G="@ O"|~ O"qP Sm#u yj# * 8O3#H6 U 3#"'&54?N@  5i`%#"'3326324u) 9$#/" M 3#"'&54?N@  5i`]2632#"&#"#"&54632j0 ?V &  AW-. 40- 10!+2632#"&#"#"&546322"&4632"&46j0 ?V &  AW((. 40- 10J((59"58"059"'5FA"J059A"ODp#u<Qj#IRm#u+Xj# 138%3#H6&u>I&G0#JIjY"|2j"q2f"s2&t2`0"J2Z"K2q"|~q"qPi#uj#04&uJ&!#J+)Y"|>+)"q>+)"s>+)>B"I:B":+)="J>+)"K>3%"|~3%"qPWg#u2Xg#3f##&54632#"/2"&4632"&46  W x  v   L  a&54632#"'  W v   +8(" +8("B+8("G+(="JB+8(="yBm#u%g#Pj#u">,i#&"F83#H6"q\ #"/5B  @5`i5+ !"543!2+H.H. !"543!2nW. !"543!2< W.uF 8X&BBqv #57632fva -qOWg\ 3#"'&54?va -\Oj%o 73#"'&54?va -oO2W\ #"/5- a\OOBqv'R.]W\&iV{&W\'V+|\47263543232+#"5#"|_d|\'47263543232+32+#"=#"54;5#"|_Q 2#"&46.'9:()9:Q:()9:R8VPP}}]T %2#"&546+TT %2#"&546#2#"&546T3%T  72#"&54632"&46:#"&54d((T(^y"4f #-7AK2"&46"2654& #"&547%6322"&46"2654&72"&46"2654&.@AZAA--->-,@ v .@AZAA--->-,.@AZAA--->-,fB/-AB\A"-@.- !-z  z  B/-AB\A"-@.- !-"B/-AB\A"-@.- !-Rf #=IT_2"&46"2654& #"&547%632632#"'#"'#"&46326232654&#"4&#"326'4&#"326.@AZAA--->-,@ v y";.@A-;" =;" =-AA-;""v3/-, -", -- ,, -- ,fB/-AB\A"-@.- !-z  z  4B/-A4444B\A44o#+- !-- !,-@.. !,-@..W\ 3#"&547}  \ iV\&aWV\''9W\ #"/FF  \ iW\&aWV\'9'[#"/#"&47,  kk  l  XX  ?8?632"'?     %%#"&4?'&462     =3  'I2"&46!2"&462"&462"&46762"/#"&54?'&54632o(|(((     K(((h(Ȏ     pj-9#"'&5463232+"&46 #"'&5463232+"&46-  -  3F **F **A 6%32+"&46>32#"=&5'&54632654&#"#"5  >?(PbG^ yP>=DU,,UE8L,) :G2?"){WBdR2326432#"&54yc754.5432#"54>=.*;;*)(  =,%%,= #VA"EQ]>32#"=>54&#"#"5%>32#"=>54&#"#"532+"&46!32+"&461-NR:YS@@< 91-NR:YS@@<  Q  SG;K*)D#E-3> )D SG;K*)D#E-3> ),,,,&Hj:F#"'&5463232+"&46>32#"=>54&#"#"532+"&462-  7 >?(PbG^XMP>=D 3F **UE8L,)D%E+2?"),,'j:F#"'&5463232+"&46>32#"=>54&#"#"532+"&46r-   >?(PbG^XMP>=D 3F **UE8L,)D%E+2?"),,i5!#"=!#"&547ul  f#LA O \%-32+"547#+"54;#"54;2'>=4&'@aHb>=byq\:MM:HHTC/V  W?a.   $HHf37332+32+"4;5#Ojj7  `'?  >  $>c(""&=32+>32#".5463232654&48 ,>F48,)/'$bF16H3('+t'2#"&#"632"&=463265&#"[< 8N,31440CAb7 )'"A=t \A7C4$ChINq(+/Z4f#"&547##"=3` ^ @ , 8n%"&547&5462&"2654"2654X@?Z?@;=T=G@#$>$$>*'D';+<<+:3'99'4p"('|.&%p "267&#"7.54672#"543232658G#${)3/940D@dG<"2Q8S.&44H5$ChJVnVBE+"=#"54;543232#= SSU` `VV "54;2#&$ t4632. C #< ((FCU/Zp4lNX254;54&'"32+"&54;5"&54;2>3232+"X&"Q ! 2 #(;C  ! 0'B@?{@Ct@;u@F@:@>@>@=@:@==<'b 8tP8NXK#/%232+5#"&5463254&#"#"&547>5&#"326.0AF5?0=Q@%+&!@ [\$'/<$ 61'!+3(/9 # 0!L!3267632#"&54632%3.#" A/";  ['Ea[BEY ;*);(,4  ]C>UWH ,%,+L #"&54632'"32654&\AA\\AB[/BB//BA?YYA?ZZ/@..A@-0@Kl02+"54;'32+"5437'"54;2+7#"546;2#VPQVoaH BD  GaMMk^@@ ^LG?rJPX&'#"&54?.=4>?63276325432#"'&'>7632##"&54?&''   325432#"'.#"#"54;>32#".#"23267632#"&5? "2Z9eHgCSv`H3\0('2 !4R(  4T,fE7J<+Ep.DYGL{ Z9#/ -&*0  &+ e+355#"54;5#"543!#"=!32+32+32+"54;5#"543|666pppp668b8?BC%2>32#!"543>7#"54;&'#"54;&54632".#"32+32+ 1##6lj ^QS:3N -)<nc XU9)&7kC3;T@ =)5 hG Q;FL>3263276232+4'32+5"&54?#"54;#"543754&#"32?&#"p0 @!7:!  !"J "J  ^""}w'27"  264$GG (  ((?z  O-RR23@DHKN!#'#32+"54;5#"54;5#"54;5#"54;35#"54;2+32+32+'5##3'##3'4K"55556juJ"!!!!)((j}`!&/m~S.;nGB.C BNNBBBBBBB):(<.%2674632+46322"&=.+#"&55"      (&%~  )+.I 9  '$( ] ?G 1;"32654&7"54;5#"54;32+32+5#"&46325"543!2#JhhKJhge6_=;6_GoYYnHjLMjjKNjT6_OYih @G%2>32#"&'#"54;5#"54;>327632#"&54654&#"!2#!!2#!W,C .N,_  \4_ S?Kn,y $`<a^  $,fR<Rn+<3AD732+"54;5#"54;5#"54;2+37#"54;2+32+32+.'+K6556K?-v'=+8W4Q>9ݴlnxH3B%32+"54;5#"&54?5#"&54?5##"=!#"&=#762762AiiZ  p^  t V  lZ  p)<  KD?  MIrr I9  HD<  Kg87 8>54&#"2654.''#"&54?7.5432#"54[ /+&0<"Tp20 R6Q& $2M_$[;7P6%)!&7LFH8&@dqg wB-$:m!pH/Ai+3=BI3267!32+"54;#"54;5#"54;5#"54;232+32+#'!&+!654'(F6555567Y c>,E!aA/0(,5/XA?2p3>#"=.=46754325432#"'.'275#"54;2+'Pk}fS<R;FI2iY_)-H' e( mJd0[#4"3+(4>,I\r O3@D3'#32+32+32+"54;'!32+"54;7#"54;7#"54;7#"54;3'נJ\L6&7%L86O73BXgHx̂I;;;;>@Q73267632#"=#"&547#"54;>7#"543!654&#"#"=43263232+!2#!]FDb?jZt3?cKQ>:R754325432#"'.'267632#'DZ .R3YAZ=3Q( 4S,)Lgg%[S4G<->p*B+/ &+!- {SGO}{|w\_<  < 'y'yyo'y'y'yxy: 2#"544&#"326?!!!#.QB VWzW:4&#"62#"54 32ȱxeDeȯ(gwx< 6f!E"#*.45<>23:<."#*23:>4f 8%qS]HKb*; %ydzyiv#,!lYmlZk!4'UE@>GT(2QLa"0 4%VA@j]g kWl^P[ZQ]88M/ -!K9@8 '974'&'!"3!267#!"'3!2654'&'#!"&547>3!2$$>I$#H33H$R9-# *=u'6Xc%#"&546325462".#"32>32"&547632%463232+5#"&5463254&#"#"&5&#"32>"E&=OQ>:'  -"3782!1Y  ^  DU,8?65,7K:-$C *+7"7@ TACW% = ?85; b   e/%,#+6 , ; $K^3Bdo%4.54632432#"'.#"#"'#"=432326"&547632%463232+5#"&5463254&#"#"&5&#"32(,?>,A08-$$.=^"J8<'8)+70  ^  %U,8?65,7K:-$C *+7"7O !*+ L1 6 b   N/%,#+6 , ; $?@)/4>325432#"'.#"3267632#"&5? "2Z9eHgC7-7>4R(  4T,fe<E7J<+Ep.DC%*0  &+ exAYGVC A4632#"&264&"2>32#"&=>32542#"'.'.#":#(49$%7\'$0$",*> &'E.cZ/D%* SE7>32!5432!"54;5.54675#"54;2+5462#".'5'F/=`6ED7``-   #)*.. 1R<=UFF = =30: MP =L2#"&546"2654%#"&546325462".#"32>327"&547632>UW<=VVk\@AZA"E&=OQ>:'  -"3782!1  ^  V>;VV=327"&547632&"O2 #(;Ct"E&=OQ>:'   G3782!1  ^  ! 0'd TACW% = 4?85; pb   \@R+@9#".5432326=##"=432354&#"#"=432632f,T4  (R4XvSCgHe9Z2" ESe +&  0*Z--YD.pE+3232+"]-480*>36-.o6_60N.3232+"546;7654&#"32+"546;  T6_5  U0N.36-.S  :I P$  ;w.&=2   %,!0   1.882632#"&54>32327>7#"&#"#"&54672!];8 (,9M7R (3:!" &% c0  K,!LVmq|-;C 2emjZ1 $*BV3*6232654&=47#"#"5463!2##"&54-)<)9 1#C#7)S:3NW=)(hG&7nE3#;T@VB*".#"32>32#!"543>54&54632-)<)9 1##7)S:3N=)(hG&7mE-;T@ux267654#""&54>7>3232>32#"&'&547v8 -g 5C31iF.$7!  ]>!,/R1  6Ifp+G2]55 d1!71.J =%2654&#"'3#!"54;#"54;54323543232#>32#"&';6*H@<%%@JEP9!>UcMhmIHocW&Axeiy>B3"&#"54;#"54;2+#32+"54;3PJ"47(3 = J3 @2"&546"2654#"4;2#"54;32+##"&=43232#12E119, , (6j4)"- 1$!11#"1 ((&r&&)#XX& 3b+4j5?%#"'332>54.#"#"&547&53>324'326*L2m(';:. @-20/+(..(+CV/JUC.ZR3"%*DH$-I5&1Q0%)+M#0CG.WeCoR1JV0zAYRC4-+3 %732+"54;#"54;2#&+32?65466PmvUA )8_a (8<3%@%05"#"&54?.546263232632#"&'32654&#">0_ ZUqБh6(*G5 IE+4U{yW3,)HY Aj||{( $gl]Mts(1>_>327#7632&547>54.'#.5463232654.##"&54654&) @!6 *!!qF h!# z 7%$  (3!( 1C84 $() '6   )Ci1bc>   p'GH,*   F#M3-132654&+"543!232+.'#32+"54;3DJhU6kIk/@=9BU9V(0$D0-E]?a0 Rkzg+M39B%#"&54?.'#32+"54;#"54;2763232+&'%32654&+i  K7#K66Ik5&L  I$9'rJhU6<  4(/]?a0$64  3"?G+D0-E R?5`4.546325432#"'.#"#"'#"=432326+"54;5#'32+"54;5#"54;732+32'9:'<,,# ) #)(99(C37$ 3%'0yU%R S%U ;SQ< M '"&2! ;!  (#)6)@&&*X3Da35432#"54;#"546;2#35432#"54;#"54;#"=#35432#"=32+"54;##"=3#"&=#JJM G#m!8 >u7`L É8aJa8VBkk BV3G32+"54;5##"=3#"&=+"54;5#'32+"54;5#"54;732+3265G U%R S%U ;SQ< Ijj I1'375#!#"=!!5432!5U8!: H)#v:4;F3"F) "X) 2+<3. O?H+3 )33<<0<>o$#!"546;5#"546;3#5BB" zNC# zvdd22 iiy4@r3+JNt%2+"543'2+"5437'"54;2#7"54;2##32+"543#"54;2+"54;/#'32+"543"54;#"=#35432#"=!5*%$->9. ''d4K_E4  'Gz&Ɲhhvv`H^rI&u&2%B*d273/T373#"54;2+32#!5432!q```= 3%3<X3j HS&/6"&f$&fb)O`#utOf#tY#uX`#tXf#"tYXc#uQ#Xf#L"B`##{XBd##QH`##{XHc#"uQHd##Q8Hf"#3`"{Xq3,1'3#3!2+32#!"54;#"54wkkAkk  !73!2+32#!"54;#"54#3#3#<KK!KKgggg3)%33#732#!"54;#"54;26;2# #s956% fc '$'u) (E*+,332#"&##"'"#"54;26#! %," 3(E*%33##"56;26;2+32#!"56;73#e'$* cf "659D) *G( .3#'%3#32#!"56;#"56;26;2+#9s_'$,OR "(*)*G(G3'+73##32#!"56;#"56;263!2+3lq) UK-;> " H(()*G(J32732+7#"54;2+32+"54;'32+"54;#"5437'*,xx(n"2xx4**pB3)(03;E32732+32+"54;'32+"54;7'#"54;2+7#"54#38++3xx2"n(xy-BB3)J37;#;2#!"54;'32+"54;7'#"54;2+7#"543!2+#||! ddp"||Zdd!)1 ?3/?@&+3' Q30\pLOp##53#532#!"54;#"54;3#"543;;=WlU8a0phhhhOOMp 0#53#53#532#!"54;#"54;3#"54;3#"543;;;=-67`-V6phhhhhhOOO Qp)-!##32+"54;#"54;2+3#"54;2+%#5FH'A5oM;xOOhh:YVp%)!##"54;2+3+"54;32+"54;#7#5F|A )M&M;xOOhh+<p(,0%#32+"54;###"54;2+3#"543%#5##5Dm57UFI L7B;2;)OxOx.hhhhcp,0483#3732#!"54;###"54;2+3#"543%#5##5##5D?57UFI L7Bo;2;-;xOO)Ox.hhhhhh? p<@%32+"54;'32+"54;#"54;37'#"54;2+7#"54;2+%#5i L LR 'PBmXP;:D;۲Oqqhh3%["+p@#532+"54;'32+"54;7'#"54;2+7#"54;2+3#"543;=" LR K mXP;:DTin'phhqqO":p159?%#32#!"54;'32+"54;7'#"54;2+7#"5437#5##5#3Dm LR K mXP;:;2;Ti)OxqqhhhhO\\OTF?G\G QP)%#"/7632!2#!  `Tb mm 56& "/#"5#"&54?  46 ln< `Vb 6%"&54?!"543!'&54632L bT`  65 mm& 74632432762'& 64  nl bV`  ?%%"&54?!#"/7632!'&54632n bb  `6`  66 mm 55 mm,%74632#"&54?#"/7632'& 66 mm 55 mm b:b  `` pg%#"/#"/#"/  >i { k > {f!7"&54?#"&5&5437#"'#&=7 k = f  >h xm4632'&54632'&5475632'&x   ? k  i =  m27327432276 h>     = k (0-%".54?##"/76323763232+Y $b  `.  %h}- H6 mm 5[  IZ (0-23'&54632#"&54?##".54?#"54;76 %`  b-  $x. I5 mm 6Z  H[ M v*#'>32#.#"#"/".M)T.'Y('C('>#)19v  , !$/- % )+e /{M v,%".#""54632>?"54>7#"&54?,''C'  (Y $,W,b1)# % -.$! ,     e+)(0+%#"/##"/76323763232+ Nb  `N `b Y6 mm 5X 56+7"&54?5#"&54?#"/#"/#"=Y6 mm 5X 56 Nb  `N `b (0 X2 X2)%%#"/##"/763237632 b  ` Y6 mm 5X JJ>/%74?'&546323'&54632#"&54?##"&> `  b JJ X5 mm 6Y*62!5432#"=!#"/7> `jb   4ac6 ln "/32+"54;#"&54?  4ac6 ln< `mb *6%"&54?!#"=432!'&463f bj`   6ca4  nl&27462#"54;2+7632'.  4ac6 ln `kb , /32+"5474632#"&54?#"/7632'&  66 mm 55 mm  b:b  `` )'#!#"/7632!267&+"&546;284b  ` &A& &1;c&16 mm 51  2F7'46;2+"3!'&54632#"&54?!"&5F;1& &A& `  b48c%2  15 mm 61&)'2+"&=##"/76323546;23267.'6.9  b  `6**49%!d #2E E6 mm 5$-;2Z F7'446;23'&54632#"&54?#"&=#"&554&'3F4**6`  b  9.6! d$2;-$5 mm 6E E2#,$ ?;2>7327'&'463"'4?#".#"#"&'#.'?2"  `  )  b* -b )  ` !5 Q m 6! 6 R m 5X;23'&54632#"&54?##".54?##"/7632376_ %`  b-  $b  `. I5 mm 6Z  H6 mm 5[ 4463263277632'&d΁ e}Q  d5" o4\ 2IQ0i#"/76323"&5#N  `  b 1mm 5 F64?#"&53'&54632#"& b  `  1 6 o5 mmi%3462##"/7632N b  `  6F 5 mm X* %4632!"&5463!7632'&& 6 p4 nl bF  `  ) 2!462!#"/76 bE  `  6F 5 mm( (7632/4724>32#"'4&#"6 ln 4 *I/8P'PB)>!b  `32763'&5>324&-@! *E-2L(4 S l6L`$>A% :@/#2G9 ` *  b\q+g0; '"543!2#"/#".'#"/1a     i i k %.Z0> &69;!#"/?2!5432#"5#"=432!'&5463#"'4? b    `jDj` * b6 ln 4aca4 S l 6;!(4.'##"/#"/#"&54226" > i)9nfh(w[2$ k>Z7#"'"54657[w(hfn9)i '.Z.> "WqsSam  `Vb .mm 56mm 56+/F@)1/%4?!"543!'&54632#"&!2#!#"/7632L bT`  > bV`  . 65 mm 65 mm)/%#"/7632!2#!#"/7632!2#!  `Tb  `Tb .mm 56mm 56*&. 1"/#"5#"&54?"/#"5#"&54?  46 ln  46 ln< `Vb  `Vb J1/%4?!"543!'&54632#"&4?!"543!'&54632#"&L bT`  bT`  . 65 mm' 65 mm*&. +%'&54632432762'&54632432762nl 64   nl 64   bV`   bV` !a %%#"&54?!"543!2%7632!2#!"54 `  `T:m 5 m 5 !a %7&543!2#!#"'%#!"543!'&54632)` *:T`  5  5 8 H$)32+#7##/?237332+3{.g    (t23   HH.337'#73'&'463"'4?##7##/?23737#7^ !d  .k    (!*  c    28H(37'#7"'4?##7#"54;7#"54;733'&'463 n  ~.t(d g 4  8&!2#!#/?2!2#!|    T   &'#"5"&5>7"/#"5L  b 4   e  2 !"543!'&5763"&56?!"543!7V    z2    %76327672/472432  b |[   e  6H#)&'463"'4>?!#/?2!7'! !d      9'yw  c   \ !'#"&5?"/7632/46327'7     } i!!      )",+'%"&54?'#"/#"54;76323'&54632Z bG`c*",,b^>`  67c-,b65 mm*.#!#"/7632!2!55!.  F)HJQ^*CEE@*.X2E@5 %#7#3'#"&54?+32'&54;CECEHJ$HJ     O37#"54;2+##"54;2+#3;N9M9qw )?;j-2#"&54632654.#"#"&546.#"326|pYIdcDi=07;+! / +74KK8naJ-  /CpaI=Q]]V@>7*  ,K`m>-  /Lfm{3]P+-333%!+,:3(+-3X3& ;2+"&546;2+"!2#QgU eg Vf Tk  gh  gV  &8>#7;2+"'#"&54?.546;763232+32#'7#"4-}V ##1  0;Egs8  0R eY Y`Vf9Z  h  g yGhx  g   (gVsn;2+"&46;2+"!2#:/HPPHd />abk&>0&39=?#"&46;7&+"&46;27632+#"&54?#"&46;267#73&Z T$ +'?  ?6?ee,  $` SUg`MF     tEg]  L  kT̤rsnC/ ~3p(".543!2+32+"&54;!32+"54;b - 56,F  kk3oRqo'!!76;2#!"=47&=43!2+"'ܮ4WGrM H. !2#!"54cn.H sXHt#y\`3 "&547632  ^  b   `3&54632"'h  ^    b  qw }|lQt32+ #"5437TB5X$it.="#"&54632#"&5463232654&'&54632654&32+ #"543}  E",<.:G2%M :$"34($'7TB %5'+$4,?$ "**  X$it)46;5#57332+32+"75#32+ #"543n 9  U9kY7TBu 9"$9$X$i%*0326?.#"32+"&'#"&46326;2+"N@4;&5H86.@=OCXXCP<32326546~ *  @,/'6  "5 M.@7 $"R.H8"=265474.#"2#"&#"#"'#"&54>32326546326.   @+*  @,/53':  "51:&$"R   N 2'N.@018 $"R.H,,J2AP2632632#"&#"+"'#"'#"&54>3232654626547&#"#26547&#"%%#%( @,/!,)#(7  "5  @ j  @  'N.@8 $"R.H$"R& N$"R& Nn07A#".#"#"&54>32326=.546754632>54&'"& @D\\D,/'6  "HbbH51 Z3 W;:V 8"8>ESak32".'#"'#"&54>32326=.5467546;267>54&'26=#"'".'63254&#"327"+ @'QD4,/53':  "E==E51/f;+*23)b "!+O @#,. !%, Mr YHAV.@018 $"^:9]t.H,,#i;G F40N$" 2Mki J$/7w}547&'26=#7#"26=+547&'32&+3272632632".'+"'#"'#"&54>32326=.5467546>4&@ P H%  7 H@:97 %'$%)  @5CB6,/!+*#(7  "9IH:55-98F*23 f& Mf$"zz:$"|z1g& Mf0 Mq_v^.@8 $"a@=an.H'OfQM`Nn&Q#"&54>323265#"546754632#".'7632'&547632454&'"&@,/'6  "7'&547632454&'"&#"&#"7632#"&54>32326=.546754632@0F ;1H6)Z'Ng iF !9N|.@7 $"{kIHhg.Hn&V>7#"&#"#"54?.'"&#".'#"/#"&54>32326=.546754632@5J0 >F1)ZMg\?  Ca |.@7 $"{kIHhg.H;#yN#yANy; V^#yKy<#yK#y#yBKyBH#yH'q#yKy<E#y#y#yB#yB\?#y"y \\%2>2#".#"#"&54632* R%B;   L,E;'& P0/ R0/\\2#".#"#"&5463232>&Q  %9D,L   ;B\P &'/0P /0`@/%"&'.#"32#"&5463232654&#"&4632)L>-A@. =VV=/&27.@A- Vf$2>32#".#"#"&54632n(  G&4*#  B, 6! X7A7+, W7A+p '@\^074632763232>2#"'#"&54>?.#"#"&\L,F N+* R%"1I + &   R  "'& P& "%V  J' J& SH^E4632763232>2#"'32+#"&547#"54;>7.#"#"&\L,$) ) 0 * R% (  n/       KR!Q ` '& PT+ c  3y%&  3%'C7#"4;7#"4;763232+32+#"&5472>2#".#"#"&546324 4+ * R%B;   L,E;R(h(6 &(h(U '& P0/ R0/3%"P2>32#"'32#!!2#!#"54?#"54;7#"54;7.#"#"&546327632" R%5FG7 '.HFB# %  L, !8 6  P TqX  ?qi &' RZ  W\&  S\^U#"'32>2#".'#"&547&#"#"&546327.#"#"&54632763232>2R%#* R%*2 8   R&"     L,+) 0 * PD '& Pd p OD RQ _ '& J '& S \#& ' O 3y%'  dY%2326432#"&54"4&#"#"&54632yc54'# D:z)""3z%E& ~3z%/|X 3z%&j 3z%/jX 3z%J/  3z%'  3z%J/Pr  zI/Ip/H/G 3z%/"  3^%'7#"4;7#"4;763232+32+#"&5474(  4+ (h(O ?(h(U 3k% !"543!2'!"543!2!"543!2 DDD3 %;7".54?#"54;7#"54;7#"543!763232+32+!2#! n51(   15'  BqgU  CgqT 33%'  uA E'?NA&!;3%' ?3%'! ?%g'8#"4;7#"4;763232+32+#"&547"'-632ٙ4 4' 6{ E (S(, (S(A ز    %g'9#"4;7#"4;763232+32+#"&547#"&547-&54632ٙ4 4' YE | (S(, (S(A     rC#%#"'-632#"'-632C & &    rC#4632 #"&54?'&74632 #"&54?'& % %    \1727632#"'#"&54>7&547.54664!    EE  !    FF  7$$#\7"  ~y"  7"  '{"  ``bd A%"4&#"##"&54?#"&546?"#"&543232?632>5432c< V  D*5 [F)Ttc< ^  L'/ UD)RpY,- { $ "?ID',-  " != JD&H &)%#"/#"&54?'%76327632%7 o  pg  OW [ BC|  X  0 K}XN&)4632632#"&54?#"&54?'&7N x  zW  ?G wb  Hw  ]  ( Bxa+ S36%#!#"&54?#"54;7'%76327632"/!24  %#v/B  $P }k lTqZ ASt  ?%  :m  oyY5;S477632!2#!#"&54?#"54;7#"&54?'&546327Va  \\R4  %%Ls \ C  HZ A7  Kt  vKH s&m Nr&!l H CF"&4?#"&546?'%76327632#"/32>2#".'7 %   @&>*  \ J 8>2) R%2$+1i9M h ! Kfv  F3 Ir o.''& P"`NCF"&4?#"&546?#"&54?'&54632763232>2#".'7 %   @&({ @ :  :2>2) R%2$+1/M h ! KoD Z~ z  [.''& P"O>##"'-632"&54?'&54632  &  &   >#4?'&54632 #"&"'-632 & 2&     <?B"&54?'&546327'?6327632#"/#"&4?77'7 7:q l+   *>h b   *~E-  > ;7632#"'.#Hdzr  YY  rzd(/W6  00  6W/F)   [R2H 32432>7632#"'.#432#"'.#Hdzr  YY  rzd:aV]!  %1.(/W6  00  6W/v(/\=  He'F,4#"#"&54767&'&546323#"#"&547>3 dzr  YY  rzd#]ia   ^Va:O(/W6  00  6W/o(/V7 =\/H o'  [fF p'  \eH 3054326?63267632#"'&'#"&54?&#?Hn7t  Ta(  EK  :  {Q0 (  8L  907  o?  F 30563232#"#"&54?#"&5476?&'&5463267&  qU3u:z  XW%  B J  8'  ( 5G  {:87  l?33q7"&463!2#!"3!2#HPPH4:6;54qabG87K3q cM53%.7#"&54?.546;763232+32#%#"3S  KCKPH]  Uryy:6;5q  `EHb  )G87K3%.7632+#"&54?#"54;#"54332654&#`]  UCJPHS  Ksy,y5;6:  aEHa  )K78G3& c3' d3)2;7"&463!7632+32+!2#!#"&54?#"54;?#"3HPPH#   ID4  -J :6;5ab,   eF  -e)G87K36=%2#!#"&54?#"54;7#"54;7&#!"543!27632+326543  6Hy4%,  *5PHGi5;%F  -b:  70VHabsK7E31#"54?#"4;763232+'"&463!2#!"3!2# 1 HPPH4:6;54^ Q(/ (abG87K31#"54?#"4;763232+7!"543!2654&#!"543!2 1 345;6:4HPP^ Q(/ (K78GbaI/#"&=432326=42#"/763232+ef| hRMp(  d{}f g~~g TilQoo 78I"y I& zJq 7!2#!!2#JsqS>q pX5J'  p>' qH !"5!"5H((>fH tX?' "&5462.'#5#65#?桢+ m\*ӿossr]\m *\m?' "&5462."!26?桢+0ssr]]vv]*]vvD) 2#"&54%&"'64'2डtsHH;;f;YF(toot@::HK*J;D* 2#"&54%"&2654'डts`9HAU_9A)tooty]RBH9(^RA9#5& & #5'~ #5)5@54327632#"/#"&54?'&546327"32654&'2"&546 Q  RQ  Q  _^]Zkܛ$+ *p  pp  p* ]_]\(mnom#5'N #5'{ 2& 3!35+33535#2绻) ̾(2& 7!5!!!5!\^*6^( 2& 3!7'%'726ӶѴ Ҷ!cҵ2&& & q31I7#"5432!2#!\F+'%I X1:G"543!2+#"59:V XGI7#"543232++I7#"543232+32++g31I7#"5432!2#!!2#!\FF+gwI 7#"5432#"543232+f++I$I $7#"5432#"5432#"543232+rdf+++";I!7#"5432!2#!!2#!#"5432rrc+g+31I#7#"5432!763232+"&54?#\    +    31I*.7#"5432!763232+32+"&54?#?!\Ak  VFiXl  VW+  fg  f)ggyI$.%#"54323763232+#"&54?##"5432 j  T9Y  Cag+w  ^d  K+"CI 377#"5432#"5432!763232+32+"&54?#?#Kb e  P4WLe  OzK++  fg  f)gg{F.%#".54632654&'->54'#"&54>3212 )  AA  ) 219֢(#&6 6 6 6&<v{F W1_NT75%CHyNT XHp' H& /BQ #"2654&%2#"&'##"&46323>y""0""R'9:(#76$)9:*!46)"""#(:()9-#".:R8+ !*BQ X\Q %2654&"'#"54;>32#"&""0""G6$)9:*!4"""##".:R8+4!% '%#"=432#"=432#"54;2!#"54;2Ab̐d97"543!2+#"5Zl<.' l<3' <3' D0).'46232'.'0   q `  w  (dD03"&54762%!(    +    (-+2#".5473#"',    #    -+2 X'+- X+-#"&542326542-gi(z^\~(eglYN? t^y H+6A76232+"/#"&54?#"4;'&5463272"&5462"&546,  zz   {{  ((7  z({   {(z  W`\RX 7'757nn;{<;{Jwww\RX %'7/͖tn3wwww.W;{\RX X4%#"/#"&54?'&54632   !  4747632#"/#"&!     L& Q86 [B@86 T03q,7"&463!2#!"3!2#%"&5463!2#!"3!2#HPPH4:6;54/632==qabG87KS/'*32/3q M5G3754632#"=4&#"#"754632#"=4&#"#"Hef| hRMp UX8:U ;,(@ g~}h UhlQ CNNC 36<- G Wx"5432#"=4'#"5#"=46@Tk/>U]<<OBd :/CO3%F37!#"=##"=#"54;5#"54;54323543232+32+'5#u??)?gg)ggN, ^2N,' U2!sR@'f 'f@ f sR@ W'{/fV%f'{/fVef8 FN8 EBF, ^TH 3 6$#"#"&54767&'&5463236#".'&546323 dzr  YY  rzd:aV^   bh]#(/W6 00 6W/v(/\= 7V/H YHM64327&#"4326?6326763201#"'&'#"'&'#"54?7HDFc> w;D 4^'  @F  4L   ^& ' F C @$4' ' *4   @7!)13265472#"'#".54?&5463276&#",6B^A T-vRG8T T-wSF7Tw,4C_"k"^B6 U7GSu-T U7IRt-Tw"]A8,2&Y 37%!'24`( #"/#"&547   &8~ XX'qx (&q'qx (6 %!2+#"(~"A %#"5#"43!A(6 X"A X|0#753@)*S(((|A3##53((T(0|%#33#@)))S((@|%#573((((SH #"=!2#q#5  Sa2%&54>& ,>j@-.@i9NNa$48' (82#7,7232654&#"76".54?&54632#"' :4COooMPp*; ?6`^_T@? ;*oONpnND5: ?AU_6?4?[,7;FQ\%#"&=#"&546;5#"&4632354632+324&#"3265#4&+32654&#"35#"326?>,->|?X>>,CC,>>,->|>-,>>,CC,>(''C'|)'C'''''CC'''->>-BB,?>-,?{?X??,CC,??X?{?%''C'{{k'B''*C'''B'''HnX]1 !#('@  8@81Q 8 @'8@Q XX8 46;2"&54&#">4/- *,b8ND/ -1+Y8@  XXd%"4&#"#"&54632ch 37/!!73'3#fD$~pon@茌(@ooooZ#"'632   h ``  Z4632#".547&   F  NNXh Xh+-E %7%%'7%"=42#"'&54?632#"/&547632-((U  U jU  U ؕ(||bb1  1  -1  1  W!"54;#"543!2+32< ^CP!%!!Pph^(X&  UX& UX&  UX& UX. 9 U#5F#!#"=.54675432>4&@ggfgVwx~Wuuig Й@F ~WY#5& H3763232+"&54?#"543  ʥ  +    H3&5463232+"/#"54;h        X& UX& UX& UX&! U)F%%#"/76323543232+#"5#  `vxb mm 56I0F%%"&54?##"5#"54;54323'&54632_ bzx`  65 mmX& UX& U#5%-2#"'#"&547'&546326327654&#"0kK HZnT  EF^LX=[F9mkN 7orN  /DVA`_GAY\&:V&q +-F 336323##"533+)3YWX&  UX& U:U%,%2#!"54;5.54675432>54&55GG57JK6'54Q&12(bR76RS96Sa_='*=<''<HM3&"/32+#"=#"54;5#"&54?  46 lnc `b X& U:&qY +-F'3##"5#3632#@& W(3;YX&  UX& U:G%,"543!2+#"=.5467557>54&96HI56II6'34O&32rS86R~}S77Rr<((=<')=H#746325#"54;543232+762'& 64  nl b`  X& U\&q +-3&q?&q &q#5&q G& X&  U#5&  X& U:'Xp +-'Xpq'X q'X#5'X" HF3432>=3#"'.5H+PTMX+v[ao\^ aR^~ }g3F(7"&46;543232+32+#"=#"3HPPHLL:6;5qabggqq)G87K\c'OaN,c'l!Ho+&+-3#+!>32##"&5467!632.#"+u ?!Z,u  pvL6(*<3 j3 D  .C#'/'\j6"63232754&#".#"326%54622#"&'"&54,DNE; QPB;QA@Lhh hSOg A^0/$9b0/$by@ww  vn  \F)!#"5&#"#"&54632543232>2#"'@*   L,%* R%&# R '& P&qeX& A UX&" UJ.%.#"?3>32##"&467'363232w>6 r\\)M  = l  B*7E;/*Χ D ' +z 0/J)'#&#"#"&5463273>2#'. < ;.)   L, 5 _$ ; m*g,3I 1hD Ru& B +2B:+(B?G&*q&Uq& 4q+(& 6q?G*8W@EK$#!67632#"'&/&54325467#"'&5&'&54767542!54'36W!,4G I**I G4 7$$7(6$!*_IH (((< >  %L L%  =$% 1/12/0 !!/ 01,33D656D9V+#!"5#"4;2!4;V`_u v(U(jV1%%+32+"=#+"4;5#"54632.#"V"xtty!$ih((lpl8"5.46742@1@@1(1@@1(I32JMIfIM<m/D`T) W'+}d4)+}4)+}4)D)X#q#qg/D)D)S!-5=Oas463232+5#"&5463254&#"#"&5&#"326'#"4;2!#"4;2%'&547632#"'&547632#"%#"'&54?632%#"'&54?632X-;$>:B.8L='2,$@',4@("#9bbbb/\\\ \f\\\\-# +6/%*5.: &n((((N! " ! " " ! "  ! X3!2##3.5462654&#}[[,21>LLfMebP[\`96\` ^BDbgNMf2&p"5423#353#53E((Uo(\(( (XX 4%7'#7 '37&'+32>#"=#"=435432,8,Ժ8B++8%*  *%88Ժq$3K2/ 6 CbD 6W*2.5432#"54>7#"+"4;2?63#!"43!+  ?*)@  +.-T:+!Ci<& )<;) %=VV(VV(( WD m67'5'67'7&'#773&/542763232+#"/"=&'#"'&54?&'#"54;67'&5476326^i$<.Li%<.jj.54%!."'xah&$qq{|}|@o2:fph=X4==XIBK#"&5472654&#"&'632#"/#"&547654&54632326327#"'X}2ⓓpE1L[~|Y  _/8  X58}[L,Lono 4 X 30_ XL98 :F&' Tt rV]$+"5!+"4;43!23VnmWZX((#("XG#46;2#"+"4;2653547"26izHUUv>} 8iYY\]^e(jWBc.(eF@oRl?g#5!7#"543!2+7', #5 OX#5!7#"543!2+%!'7,:z 漼#5 QX?y$+"4;6#!"43!+"4;"+"4;?jjkkjj((((((((D %#'73'#3DqLJX!%!!X^(;# gU W 048<DS#5#532#53#53##54;#!"=#535''754;5332#53#53#53#"=33!73"&'($8r__^^(/99*9z((((((( 4( FnKTq:v ((((( 4(Ei 8ccc/$P(3. I! V 7LTX\`h#5#532#53'#53#53#53##54;#535!53#5'"543!237&'#5ᘀ537+5353%#53#53#53#"=33V(&((^^((((9(M^ Y((m*,j6(# (Sw(&(U((K^^^^M(9.(iY(ji.B((S?i`gLI ILFB(j((((*X%9%#!"&=4>75735##%54&'!3!2>X!#f* #G!p)|*#E8'#-48 P@8/B8V#oo @tC-Et@{B+BX_$,1%+"&=46;254&+";26"''.''&5X[[[[(ExyDExyDA+(:u[[[[yDDyyDE;*] !*T8 #7632(  - T8| 3#T(( T #"&/&3  (V -Q8 ]XX8 #3((Q  [XX,8##32م(,8T 3#,(( , 2+3(:d8, cXX8,  bd,  aXX8 "#4632DV(nTB;"NX,8@ 3#4.'>5(05 ()GN.*5G) Ս9 /I.,+G/ %&8! 2#"&53Tn(V;XN";B8@ #@( c8@ iXX8, #67.53@(05 ()GN.*5G)+9 /I.,+G/ %&8!c@  gXX8@ 3#(( 8X!5XxPP<8 65463<~~ XrV rV<8 "&54.'2~&DE-VrX9P)Vr 8:l '5!#''%5!j4/0(@7uA6@*+ : 373!57 !5%H(0=q @6+zX !#"=!#"5.X`X tX Xs/ tX uX"8A 3#&54632^)j, ; X !5X PPX5!5X5PPXT!5XTPP#Xs%!5XsPP +"5476;((q $+"'&5423(((p(/""=.5467542>54&'#?gd(cg(UsvR(VqsTfh[[hgn}VYz~}(/"("=.54675#"43!2+>54&'#?gd(cgUsvR(VqsTfh[[hgX((}VYz~}(/"(32#!"4;5.5467542>54&'#?gd|cg(UsvR(VqsTfhF((Fhgm}VYz~}:%+"=#"547542'#:((5(.__jj{{:!%+"=#"5475#"43!2+'#:(5(.__T((T{{:!%+32#!"4;5#"547542'#:|(5(.J((Jii{{>,746324232>?632#"'"5&#"#"N>4>(@3  O=4?(?3> Ak6C8  Bi6T7< ->274632#"43!2+32>?632#"'"5&#"#"N>4>@3  O=4?(?3> Ak6-((8  Bi6T7< ->2746324232>?632#"'32#!"4;&#"#"N>4>(@3  O=4?|?3> Ak6B8  Bi6((?7< -W +"5#"43!((G(W #!"4;423|(-((G }X |XJ#!2#!"546;2+!#"43ZO2  + ;&H)32##"/76322654#552>53B]c@  wg*HJPre<"RW; jv)}xCE+KxT@!7!5!(( R'%#!"543!42!2#!"43!#!"43!+"4;R(Ik77ppct((s((t((RO!%+#!"=#"54;543!232#5!5!RRDDnRCnnllPX&".+"&'+"#"546;2>32;2D l&%l =-l  #l-=P)9 **9)8X XVD%#!"&=>;2#D)!- )-&&| |+-E& jW. Nt#!"54763!2!!N  ?Ac L (T,82!2#".'!"547>#32>%." $   2++/  $!         - ?9 .   2 !  ,.v/1%/8/u8*y/6/2+n.-y/7/;n6#-v/(/7;n#0v/4u/1(-0v/(/27|=u/$/&.z&.u/%/(/uc7/6,h%rwje6/+Yv7:esd9//^y)BdXd8/7Dd9Uxud7/)8d)gwhf7/&Tr5-foe9/6Pt6,j~d7/6_r,4d+4t/(//'!t/'/&y+0t/'/&+3t/'/&+/t/'/&"3v/$/.p1$8y/1x/6<#9v/(/7%;x/& /$1{rl//(do0.l!9y/6/8%#6u/(/6&ad7/)Sw6;i_c5/6Eh*Kpib7/5[w6;gZe;/6Ej8M{b6/6gq38b!7t/'/(/~((%5432!5432 *U~~ U=g/1 M /. 9"=#+"=4234;29((|j%"5!"543!42((I(I_!j+"5#"542!42((3(D!_Ij Wj#%#!"=423#"=43!2"=#3542((_((f zN - 5#'N"zNUU@ #53#53#3@Xq Aa #53#3#53A__AA#4 #53#3#34__``XBB K !#53#53#53K__b!#3#3..h..JXX ,"32654&'2"&54632+"54;#"&547/mlkgxGKW  0lmkj({|}|^. XX 8"32654&'2"&546463235432#57>54&#"#"&/mlkgx>?4F2{/"D 0lmkj({|}|*DD+ ..r$M<*C XX G"32654&'2"&5464632#"&546232654&#"5432654&#"#"&/mlkgxK6FF&+#\B6S 22BG.,B/5")  0lmkj({|}|==+%$,$-Q= ""5 7!$ XX .2"32654&'2"&546#57332+32+"54;=#/mlkgxT|70lmkj({|}|/KtXX >"32654&'2"&546"#"=32+>32#"&546323254&/mlkgx'A 6MO"32654&'2"&546"&#">32"&5463232654#"/mlkgx; 5RN$]zf`R&K '%KaK0lmkj({|}|&\\ 1C7Rr[sq+ !<$mOXX )"32654&'2"&5465##"=3#"&547/mlkgx˦m  0lmkj({|}|FA XX -5@"32654&'2"&546#"&5467.5462&"3265"32654/mlkgx% J?BG#CnCQ'*$Z34,+50lmkj({|}|0+9FC<,-&(-:;,'(@K("r0'(..'&XX XXXX "/E"32654&'2"&546#"&54632'"32654&'32+"54;#"&547/mlkgxKD)(@80,Am( -/\# 0lmkj({|}|QusSaovM^J-L#ZBH`*'  X@!!X@(XT!!XTP8@ 3#(( 8T 3#PP X@ 3+53#53ddddd@((((XT 3+53#53dddddTPPPP8@  #=3#5@(((pd8T  #=3#5TPPPpdX@ #5;#!#53#53тFAAAA(((((XT #5;#!#53#53тFAAAAPPPPP8@  #5#553#5@(((((;gggg8T  #5#553#5TPPPPP;gggg8X@!!#@(@( 8XT!!#@(TP48X@!!#TP@( 8XT!!#TPTP48@@#!5@(@(8@T#!5@(TP8T@#!5TP@(8TT#!5TPTPX 3!( (X 3!(4PX 3!P (X 3!P4P@ !5!3@((@ !5!3@(PT !5!3TP(T !5!3TPP8X 3!!( ( 8X !!#3@((TP48X #3!!#P( ( 8X 3!!#(P@ ( 8X #3!TPP  (8X #3!!#P(4P48X !!#33@P(TP48X 3!!P4P48@ #!5!3@(((8@ !5!3#((P8T #!5!3@(P (8T 3#!5!3@P(@(8T 3#!5PP@(8T #!5!3@(P4P8T 3#!5!3@P(TP8T #!5!TP P8X@!!#!X(@( 8XT !!#!5!@(@@( P8XT 5!!#!5@(@P4(8XT!5!!#X(PP48X@#!5!TPX ((8XT #!5!!TPT P(8XT !5!5!!#TP(P48XT!!#!XPTP4X 5!3!(( (X !5!3!@(P (X !5!3!!((4PX !!5!3@(TPPX 3!!5P@ ((X !5!3!TPP (X !5!3!!P(4PX !5!3!XPP48X !5!3!!#((( ( 8X 3!!#!5((T ( P8X #!5!3!@((4(4P8X !5!3!!#((P4P48X 3!!#!5P(@ ( (8X #!5!3!TP( ( (8X !5!3!!#PP( ( 8X ##!5!3!T(P4P (8X #5!5!3!!#P((4P48X 3!!#!5!3@P(T( P8X 533!!#!5(P@4P4(8X !5!3!!#P(P4P48X !!#!5!3@P(TP4P8X #!5!3!TPP P (8X 3!!#!5PP@4P4(8X !5!3!!#PPP4P4X@#53#53X(((XT#53#53XPPP8@ ##@((( >>8h ##hxxx >>X|!!5!!XX((8| 3#3#((x(( 8X| !!!!@D(P(\8X@ !####|(P(@(  8X| !#+!!T(P(|(\D(8@| #!5!5!5@(|(P(8|@ ####5|(P(@ (8|| ##5!5!(T|4(4(X %3!!!(D\(P(X !3333X(P(  X  33!!T(TT\((D@ %!5!5!5!3@((P(| !53333|(P(( |  !53;!5!(P(TT((8X 3!!!!(\(P(\8X  33#+3T((P(( ( 8X  3##4333!(T(CC((\\(8@ #!5!5!5!@( (P(8|  ###533|(P(( (8|  #3632##!53|((tMC(4D4(8X| !#!=!X(X(\(P((8X@ !#####X(P(@(  8X| 5!##5!"%##654'43X(U(CT(((ww(\̈wX  %!5!%5!3!XX((P(\(X !533333X(P((  X  !!3!53#"56533X|tMC((D̉w(4wT\8X !5!3!!!!#!5!((T(\(P(\(8X ###533333##TP((P(( (  ( 8X  ##5)##33)533(T(((((\\((48X@ 3#"#46ddcQ(g@(Qc,ug8@@ 2#4&+5dug(Qcd@gu,cQ(@  =XXX "&53;ug(Qcdgu,cQ(8X 6"ʴ,8X 7X""8i  ' 7 C&""&"", 22,@!!,@(,@ 3#(( ,X@ D,8@, E ,T!!,TP,T 3#PP ,XT H,8T, I XT!5!5!!,,,(P8T 33#(P, XT!5!!,,,P(8T #3##P(, ,X !!X 8X!!XK}8X25!!X28X5!!X8X,!!X, 8X!!X8X&!!X&8X!!X8X !!X 8  !!  8 !!> 8w !!w 8, !!, 8 3# 8 3# 8K 3#KK ,8X !!,, 8& #'+/37;?CGKOSW[_cgkosw%3#'3#'3#%3#'3#'3#3#'3#'3#%3#'3#'3#3#'3#'3#%3#'3#'3#%3#'3#'3#%3#'3#'3#%3#'3#'3#%3#'3#'3#222222222222,222222222222,222222222222,222222222222,222222222222222222222222222222222^222222222222222222222222222222<8X #'+/37;?CGKOSW[_cgkosw{%3#'3#'3#'3#'3#%3#'3#'3#'3#'3#'3#%3#3#'3#'3#'3#'3#%3#'3#'3#'3#'3#'3#%3#3#'3#'3#'3#'3#%3#'3#'3#'3#'3#'3#%3#'3#'3#'3#'3#'3#%3#'3#'3#'3#'3#'3#%3#'3#'3#'3#'3#'3#%3#'3#'3#'3#'3#'3#%3#22d22d22d22d2222222d22d22d22d222222d22d22d22d2222222d22d22d22d222222d22d22d22d2222222d22d22d22d222222d22d22d22d2222222d22d22d22d222222d22d22d22d2222222d22d22d22d2222222222222222222222222222222222222222222222222^222222222222222222222222222222222222222222222222222222222222222222x8X  #'+/37;?CGKOSW[_cgkosw{ #'+/37;?CGKOSW[_cgkosw{3#3#53#53#53#53#53#53#53#53#3#3#53#53#53#53#53#53#53#53#3#3#53#53#53#53#53#53#53#53#3#3#53#53#53#53#53#53#53#53#3#3#53#53#53#53#53#53#53#53#3#3#53#53#53#53#53#53#53#53#3#3#53#53#53#53#53#53#53#53#3#3#53#53#53#53#53#53#53#53#3#3#53#53#53#53#53#53#53#53#3#3#53#53#53#53#53#53#53#53#3#3#3#'3#3#3#'3#73#'3#73#'3#73#'3#73#'3#73#'3#73#'3#73#(000000000000000000//2000000000000000000//2000000000000000000//3000000000000000000//2000000000000000000//2000000000000000000//2000000000000000000//2000000000000000000//2000000000000000000//2000000000000000000//d0020000200002002002002002002002002002002002002002002002//0400000000104000000001B0400000000104000000001B0400000000104000000001B0400000000104000000001B0400000000104000000001t0040b041+0b0b0b0b0b0b0b0b0b0b0b0b0b0c1X !!X } 8X 3# KK 8,,!!,, ,8X,!!,,, ,, !!, 8X !!!,, 8X !!!,,,)8X !!!X 8X !!!X ,,X !!,, 8X !!!!,,,+ 8X !!!,,,2&3!2 2&3!%!!24\ (2&;4>;!2!"'./3!27>75&'./!2 8  ,8 <  (  * }"  ,8  ,<   *  (2& 753!%!!4\}} (2& !5%!5!!5%!5!!Z\\\\(YKK(KKK(K 2& 73##!3##!!KK(KYKK(K (\\\\(2& #'+/37;?C%353535#35353535#35353535#35#3#35##3!@KKKKKKKKKKKKKKKKKKKKKKKKKKKKKsKKsKKKsKKsKKsKKKsKKsKKsKKKsKKsK(KYKK(K 2& !!5+5%332 'ު\ުx ̥x2& !#75373& ͥxޥ (ުƪx 2& !$'+%3'5'7'35'7'7'?#'?#'!!E(D}UUUUUUDDDUUUUUUrDD`ED lD`DDUUUUUU9DDEEUUUUUUrD`DaD( 353735#53ת((2&,3!2,2&,3!%!5!24\,(3!, 3!%3#, (?,3!22,?,3!%!7!22:&\,(#5)5 #5% ! (N()7%'!7mAA(jj(T 5 T %5 y %充 %'7]n;{@y % y%'%uSCH#5 5:#5! l v:%'%#7'mAj99  99W    ?充757n@{;^ 7%^_75%HC#5! ,    #5%7' ,  8  #5 %'77' ,    #5 2#"&467"32654&'2"&546.'9:()9:+_^]Zkܛl:P::R8~]_]\(ڜomY? 77+Ҝ?&#5 "32654&'2"&546/_^]Zkܛ]_]\(ڜom #5%/7?GQ[en2"&42"&42#"&5462#"&546'"&5462$"&462462"$462"%4632"&4632#"462"&462"&$      R        +            "     X  2    z       b  a  #5 !2"&54667&'>4&0kܛ'$#P'$$LZ08*01ڜom)@Ch)hCo7`I_l_#5 )2"&46"264&'"32654&'2"&546.2IIhIJ4#32F22 _^]ZkܛJ34IJhH(1#$22F2]_]\(ڜom#5 2"&5460kܛڜom#52654&'2"&546-]Xkܛ>]\(ڜom#5"32"&46+[]pܛ\]moڜ#5!4&#"4632#"&K„]\(mnom ]Xkmn#532654632#"&K\]monm[]pnm#5 "3267#2"&546/_^]kܛ]_] ڜom#535"4632#"&J^'mnom Zkmn#,!"&5463,ooon,5!2,onoX!"32654&Ԙ*:9)(:9XX8R:9)(:XX 2#"&5467"2654&%!!/V~XY~\pܛeXZY|2monm#,XX5>32!#!#5.#"K\X#X#gm, Z|}Y , ikX, 3!33267%!#"&'5##mgX\,, ki Y}|Z# 0 ##5>73/ X{(hzW g, 9 '3#5.'-h({X(g Wz,9 %3>753#- X{(h(zW g#0 %#.'53/h({X((g Wz# 55.#"#5>32 X\(mg Y}|Z ki #53#"&'533267 (gm(\X ik Z|}Y2&!&  2&)& 2&3!22&!2 Q "2654&'2#"&46-""0""'9:()9:)"""#(:()9:R82&3!'3#2 (2&)!#& 4\2&7!!Z4(4 2&!!\(\( 2& 3!#332( \\#5 %2"&46 !,( (N(#5%!  (N(#5%!, (N(XX "32654&'2"&546/mlkgx0lmkj({|}|2& 7!#!!35Z ( ̾2& %3!3!%35#@\4( (2& 5!35!!5#\ (\42& #!5#!#3 ̾\ (#5 2"&46#32654&35(pܛX^vPtmoڜ)Vw_Wu#5 !"&462'>54&#"35(kܛXWv^XtPڜom)W_wV(Qu#5 !"&5462'53.#"%#>0pܛX^vKPtmoڜ)Vw_Wu#5 2"&5463267#7.'0kܛXWv^XtPڜom)W_wV(Qu2&3% !2:^y^2&! 2y^ ^2&) & :^2&3!%!!26^ (2&3!2 }K7!%!!}^%K^&}K7!}^K^2& !& ^  DD#5AS_kw#"/&54>32%2#".54?6#"/&54>327#"&46;22#".54?65462"&#"&46;25462"&"&=46 * *2 * * * *8< < f * *   < <   5JL55LL * *6 * *| * *   f * *B< <   < < LjLL55L/#7&54632>322#J#.V?';):<@l*(2 <23"&5462326=462>2&#"&#"&#"&#"%+  (  !  !  !  !  3' 2 X`E[[E    5 &6F72#"&546"32654&7632#"&54'7632#"&547632#"&543IJ00JH2!12 #-/  s  sZ  NZMG32I(1"!13!2\ \  2P(!%"/"&54?'&546376272I  I 53о  o  P(!=%"/"&54?'&546376272"/#"/7632'&547I  I 53O%' | 6ss6 о  o  P ]^ x%25432#"546;'6  I%  J Z+5432#"546;'7!"&5!2  I    J  + #5 $463""32654&'2"&546_^]Zkܛ  ]_]\(ڜom2& q2&3!%!!4>32632#'&24\   K  !\ ( u4 2&/3!%!!2#"/#".54?'&54>327624\h     ((    &&3&547#"&463!53#5#"&547&54P$I!#$f!$\4 $D( ($" 4 22#37+#3!2+PP$!f$#!I\ 4 "$((!"$ 4&&%-5=3&547#"&463!53#5#"&547&547!"3!35#"";5#";P$I!#$f!$ $ ) d d\4 $D( ($" 4 _7(7_2& 2'@22'/7?#37+#3!2+'!264&#3264&+#3264&3264&#PP$!f$#!I$ ܤ d \ 4 "$((!"$ 47_7_78, 8(@P`?%"/"&='&546325#"&46;5#"&46;546232+32+T  h T X X  X X h \1 < 1  T  N N  T  < P`+#"&46;5#"&46;546232+32+"&5 X X  X X   Z  T  N N  T   :;%#"&=46235#"&46;2+35462"&=#32+"&46;   d  d   d  dd  d   d  d   #5 2"&546'67654&'7550kܛ SS'xUΦWvA8TڜomYuY8GWԋWEZ;#5 $.#.46724&#2"463232654&'2"&46`[>/2BG/.=`pܛ  6  \(58TEI-](moڜ2& 35!%5!%5!2  dddddd2& 53%#55!%5!^ ddddd dddd2& 35!'53%#=!2dddddddddd2& 53%#553%#55!^,dddddddddddd2& 7#553!5!5,  dddddddddd2& 53%#553%#=!^,ddddd dddddddd2& %3+5373+53'!!^ddd dddddd2& 53%#553%#553%#5^,,ddddddddddddddd#5&2<7"5462#"'."&463"6463"'"32654&'2"&546TzT ;T<Q_^]Zkܛ->>- &&    ]_]\(ڜom#5&2<72267632"&546463"6463"'"32654&'2"&546>-^    ]_]\(ڜom#5&07"2654#"#"&'.3>4&'"3>4&'"'2"&546\\A./A%  Ikܛ1DD1 )*0! 0 !0! 0 {ڜom DD#5AS_kw#"/&54>32%2#".54?6#"/&54>327#"&46;22#".54?65462"&#"&46;25462"&"&=4654&#26 * *2 * * * *8< < f * *   < <   >XY??YZB,/CA/.B * *6 * *| * *   f * *B< <   < < Y~YY??Y-B?./BAI#%#"567>4&'&'432>54.'Ԟ&GPPG&BUC/0EW?jCDk=*97I#.464632#"&BUC/0EW?jCDk=*9햹 44 v A"32654&'.543226763232+"&=#"&46;5.546.2DRvTTtT   x2@KkkLKj(Bh3>32356732+"=#"&54>54&#"#"W29=Y ( & ($ &=FG:ou    14X0+2- 8>g;"&5#"&5467356323+632;2+"5454&U/B  I I H K8bU^]Bj^E.A4   H F YY1F  K%(Kc=nFR#+"547654'&5463235423>76;2+"'.'##"&5467"32654&Z <7 gg :; Z(f 2C bb 7< f*>J0-M<(31 34|:^2 eXX^  /Z>bb>L=  Rcc[ 2^:G+-MH2/A &1"!165:ga%2+"=#"&46;5.=#"&54?632#"/5"&54?632#"'6=#"&54?62"' }(w wXl 7 7   7 7   87 mV  ii  UP 88 y 88  u 88 PUVm -%2#!"&546;:#4.#2>  %&F(:!(77]A53/WN@CMX-(  E  %2,A' (86Xo)"&54&#"#"54632632#"5654&#"?  J56:QF;>5_BW@1)V` W\J8-IaLMbG.6L:h 8%2>54&"2#".5467.54322654&54-2M)rss-AHAH)8J0<]1 J@DGus,9(VopUVgRB,7nL7.5463232>7632#"'632X4>v`26d2=0U'-T+ +9m>. *- 5 &.**56RU G88#+(4 r 1+  ")"%  Wd -9Z%2654&#"23267#"&54632#".547"32654&%".#">32#"&54>3231 34V"2Y8j,NJ01J9`j<2^?1z31 34#,gE*.LJ0-N'=VU01]@11"!165V! `W L/-ML.YC #1"!165V"K0-MI1GlA(J:9D4632.546323265462#"&54>54&#"#"&264&#"A7# B,kuw-6-0 "-  E20H-5-kb-5-A67Ax&))& /(4DOJ)nn,Z@X*1*% 6AI./`BS$YrGAk;4CD+H+-"!->Xi]j4>7&54&""&54.#""&54&#"&4632>32632>32#".'#"&7>54&#"/?$:%   "     $ %!C))  <- /#    Q %-&<1(36JJ6V '(B>V ,T  "#>#%V`S^uOY+ ? .WdZFtjX$U 07!2#!"&46232+"=654&"+"&46;546+ CZw Aj@  z\(   \e  OHJM  _bWhP#"&54#""&54.#""&54&#"&4632>32>3232>7#"&46;2"&5/(&*4"  8   + # #$  #1% $: o  % >;@@T (3%T 6J  7 "#YOT"0 %  o V[+7#"&546;2"&="/#"&54?'&562c       d      S]2B"&463!2632#"&547"&54767>732654&#" ! M8K #jlK\xYSby ' /&9>Qm]&55   1&vo`ov=6*n - ;T(ZL[d;Q8q%".#"#".#"#"&5463232>3232654&54632".#"#".#"#"&5463232>3232654&54632(  .(5 9$)#*&  :#(  .(5 9$)#*&  :/9//9/1:1q CU0;0/8//8/8  ,@A/9//9/1:1q CU0;0/8//8/8  ,@AQq;##"&54767#"&46;&'&5463236763232+#"'&       $a  V  T  aa  T  U  aHC)%3!527>7#"&54>7#":!$7u7$!*D,=$9=< -25)=,D+A##A+B=+%=26X7.M01'6 +=,,@:.'.5463267632">54&#"#".'&-CY2#F3E) )92H,9(3!+3  7=9&.+3'   / =qtAI&4F=$-.E0.\G:+=:D-!'UIJQ'*..'"=TD &'67>7.+/:=80||0j.%xC'3B1l,WV(4(J:8%!5>7#"&54632&5462>32#"7 &8$z$9%  A4JC0 KjK   0CJ4Y/%&/# M55J-)3HH3)- J55MHC !K%3&'3254.'3263!527>7#"&54>7#".  !@Z+0:82-0*E!$7u7$!*D,=$9=< -25)=,Dm $OP(#(G.,E'$*&*O-+A##A+B=+%=26X7.M01'6 +=,,?>32.'.54632-H,2H+CE? CY2#F3a=9E0,\UWm6=qtAI&4FQD &'6(/:=C'3B1lJ: 5v%3.'32654&#">54&"&#"32>!5>7"#"&54632&5462>32#".. N  g.>6,Y,V,  ]",6>.+# &8$z$8&   4JC0 KjK   0CJ47!c  _3-+' (! /;;/%#'+-3(#9(( M55J-)3HH3)- J55M"6@3"&54632462'15#  4$%  !@$3"&54632462"&54.''15#  !((!  "*:4$%  '"2(G/ 4G*!6<"&54632%#"&54632'15#4$'15#R4$% 'a!$% R!6<"&546325%#"&546325%'15#4$'15#R4$% a!$% R!aMam| 4&#">>32462:H!& 5k\%TO  '@9@amMMh xY[ #53%++=+7m793=V\575375377#5#5577YY++YYYY++YY+W(!((!(>!8J 6462"!!!&&< |&&ZzJ$462"&462"!!!f&&&&]< |b&&&&ZzJ$462"462"&462"!!!&&L&&&&]< |&&S&&&&ZzJ#'6"&4626"&462462"&462"!!!&&&&@&&&&]< |&&&&&&&&ZzJ'+/6462""&4626"&462462"&462"!!!&&"&&&&@&&&&]< |&&5&&&&&&&&ZzJ'/37$462"&462""&4626"&462462"&462"!!!g&&&&@&&&&@&&&&_< |&&&&0&&&&&&&&ZzS$#!"'&542632!S!(L(( l!! #5&  Sp $#!"43!42!S((((6P8(2"&463!2#!"&5463!2#!"2654&#"B\Z]]n}|o2DF0/FE\\\\7΍(vw(D12DDdCP8 "U8U2#"&54?654'.54Rm+P+H1Rm+P+H1\LTSJs5 \LSSKs6 U4632#"54>7654/&mR1H+P+mR1H+P+\  6sKSSL\  5sJST-+2#y X' 3D%#"&547632"&46;2+";2# yHPPH97;5S   |bcH76L 3D 'OD[Y+#"5#"4;43230(c(cMV4.543!2#!#"54>s & q-V<.(nwI~#5"y+ -+K &X@H%26=42#"&=42342@_(x|(^()e|fYn #!"43!422#"&54I(,(IL.Yn .5XR-X7'#"4;5#"43'75gggnnXwwww((;{<;{R-X 0@RIX?'%32+'#"4;5#"4;732+nnPnn(>>>>>>>>@;@;(ww((ww( I%#!"=#"4;543!2!Invv(([q I 3R\ 32#!!2+3Ebb(^^Sz\ #"543!!"5433b^S(z}[2 #&' 6   >[  df}["547 &547563    >y AC -+[2 #&' 6'2 #&' 6   >   >[  df  df-+[%"547 &547563 "547 &547563 ?   >   >y AC  AC 0O/2#"&54323264&#">32#".'&5432>cdc32#"'.5432.#"32>32#"&54632>11:oMRrrR%D*n>cb^-* ,% %, 'Kgrr &$%$%%2%$$#&$$&$&$'$& ".#"&546327#"&5463272#"&546#2#"&546 $%$&'%$&%$%$&$%#94$$%~%$&$$$#&$%$%p 2#"&546&$%$$%$% 2#"&546'2#"&546%$%$%$%$>$&$&$&$& 2#"&546#2#"&546%$%$&%%$$4$$%$%$& #2#"&5462#"&54672#"&546%$%$&$%$%$%$>$%$&$%$%$&$& 2#"&54672#"&546%$%$%$%$:$&$&$&$& "#"&5463272#"&546'2#"&546 $%$%$%$&$%#94$$%$%$%$%$% #2#"&54672#"&5462#"&546%$%$%$%$&$%$:$&$&$&$&$%$% ".#"&5463272#"&546#"&54632'2#"&546 $%$%$%$e'%$&?&$%#94$$%$%$%?%$&$$%$%p 2#"&54672#"&546&$%$&$%$>$%$&$%$% #2#"&54672#"&546'2#"&546%$%$%$%$%$%$>$&$&$%$%$&$& "2#"&546#"&5463272#"&546%$%$e$%$%$%$=$%$%$4$$%$%$% #/2#"&5462#"&54672#"&546'2#"&546%$%$&$%$%$%$%$%$>$%$&$%$%$%$%$&$& !#"&5463272#"&54672"&546 $%$%%$%$%%2%$$#&$$4$$%$'$& ".#"&5463272#"&54672#"&546#2#"&546 $%$%$%$%$%$&$%#94$$%$%$%$$#&$%$% "-#"&5463272#"&546#2#"&54672"&546 $%$%%$%$&$%$%%2%$$#&$$4$$%$&$&$'$& ".:#"&5463272#"&546#"&5463272#"&546#2#"&546 $%$%$%$e'%$&%$%$&$%#94$$%$%$%?%$&$$$#&$%$%p 2#"&546&$%$$%$% #"&546322#"&546$%$%$%$94$$%z$%$% 2#"&546'2#"&546%$%$&$%$:$%$&$%$% "2#"&546'2#"&54672#"&546%$%$&%%$%$%$]$4$$%$&$&$%$% 2#"&546#2#"&546%$%$%$%$$%$%$%$% !2#"&546#"&546322#"&546%$%$f$%$&$%#]$4$$%$4$$%z$%$% #2#"&546#2#"&54672#"&546%$%$%$%$&$%$:$%$&$%$&$%$% !-2#"&546#"&546327#"&54632'2#"&546%$%$f$%$&'%$&?&$%#]$4$$%$4$$%~%$&$$%$%p #"&546322#"&546$%$&?&%%$##&$$&$& "#"&54632#"&54632'2#"&546$%$%$%$%%$%$94$$%;$#&$$$%$% "2#"&546'2#"&54672"&546%$%$&%%$%%2%$]$$#&$%$&$'$& ".2#"&546'2#"&54672#"&546#2#"&546%$%$&%%$%$%$%$%$]$4$$%$&$&$$#&$%$% "2#"&546#"&546322"&546%$%$f$%$%%%2%$]$$#&?$#&$$'$& !-2#"&546#"&546322#"&546#2#"&546%$%$f$%$%$%$&$%#]$4$$%$4$$%z$$#&$%$% #.2#"&546#"&54632'2#"&54672"&546%$%$f$%$%>&$%$%%2%$]$$#&?$#&$$&$&$'$& !-92#"&546#"&546327#"&5463272#"&546#2#"&546%$%$f$%$&'%$&%$%$&$%#]$4$$%$4$$%~%$&$$$#&$%$%p 2#"&54672#"&546&%%$&%%$:$%$&$%$& "#"&54632'2#"&546'2#"&546$%$%$%$%$%$94$$%$%$%$%$% #2#"&54672#"&5462#"&546%$%$%$%$&$%$:$&$&$&$&$%$% ".2#"&54672#"&546#2#"&54672#"&546%$%$%$%$&%%$%$%$]$4$$%$%$%$&$&$%$% #2#"&546#2#"&54672#"&546%$%$%$%$%$%$:$&$&$&$&$&$& !-2#"&546#"&5463272#"&546'2#"&546%$%$f$%$%$%$&$%#]$4$$%$4$$%$%$%$%$% #/2#"&546#2#"&54672#"&5462#"&546%$%$%$%$%$%$&$%$:$&$&$&$&$&$&$%$% !-92#"&546#"&5463272#"&546#"&54632'2#"&546%$%$f$%$%$%$e'%$&?&$%#]$4$$%$4$$%$%$%?%$&$$%$%p "#"&546324#"&54632'2#"&546$%$&$%$&%%$##&$4$$%$&$& ".#"&54632'2#"&5467#"&54632'2#"&546$%$%$%$X$%$%%$%$94$$%$%$%~$#&$$$%$% "-2#"&54672#"&546#2#"&54672"&546%$%$%$%$&%%$%%2%$]$$#&$4$$%$%$&$'$& ".:2#"&54672#"&546#2#"&54672#"&546#2#"&546%$%$%$%$&%%$%$%$%$%$]$4$$%$%$%$&$&$$#&$%$% "-2#"&546#"&5463272#"&54672"&546%$%$f$%$%%$%$%%2%$]$$#&?$#&$$4$$%$'$& !-92#"&546#"&5463272#"&54672#"&546#2#"&546%$%$f$%$%$%$%$%$&$%#]$4$$%$4$$%$%$%$$#&$%$% ".92#"&546#"&5463272#"&546#2#"&54672"&546%$%$f$%$%%$%$&$%$%%2%$]$$#&?$#&$$4$$%$&$&$'$& !-9E2#"&546#"&5463272#"&546#"&5463272#"&546#2#"&546%$%$f$%$%$%$e'%$&%$%$&$%#]$4$$%$4$$%$%$%?%$&$$$#&$%$%l 72#"&546%$%$$&$&l 6#"&546322#"&546$%$&$%$X4$$%8$&$&l 72#"&5462#"&546%$%$%$%$Y$%$&z$&$&l "6#"&546322#"&54672#"&546$%$%$%$&$%$X4$$%z$%$%$&$&k 72#"&54672#"&546%$%$&%%$5$%$%$%$&k "72#"&54672#"&5462#"&546%$%$&$&$&$%$|$4$$%$%$&z$&$&k #72#"&5467#"&54632'2#"&546%$%$X$%$&>%$%$Y$%$&~##&$$&$&k ".72#"&54672#"&54672#"&54672#"&546%$%$&$&$%$%$&$%$|$4$$%$%$&$%$%$&$& %#"&546322#"&546 $%$&%$%$=##&$[$%$% "$#"&546322#"&546'2#"&546 $%$%$%$%$%$X4$$%7$%$%$&$& #%#"&546322#"&54672#"&546 $%$&?&$%$%$%$=##&$$%$%$%$% ".$#"&546322#"&54672#"&546'2#"&546 $%$&$%$%$%$%$%$X4$$%z$%$%$%$%$&$& #%#"&54632'2#"&5462#"&546 $%$&?%$%$%$%$=$#&$$%$%z$%$% ".$#"&54632'2#"&5462#"&546'2#"&546 $%$%$%$%$%$&%%#X4$$%$%$&y$%$%$%$& #/%#"&54632'2#"&54672#"&54672#"&546 $%$&?%$%$&$%$%$%$=$#&$$%$%$%$%$%$% ".:$#"&54632'2#"&54672#"&54672#"&546'2#"&546 $%$%$%$&$%$%$%$&%%#X4$$%$%$&$%$%$%$%$%$& 72#"&5462#"&546&%%$%$%$Y$%$&z$%$% "$#"&546322#"&546'2#"&546 $%$%$%$%$%$X4$$%{$&$&$&$& #72#"&5462#"&546#2#"&546&%%$%$%$&%%$Y$%$&z$%$%$&$& ".$#"&546322#"&5462#"&54672#"&546 $%$%$%$&$%$%$%$X4$$%{$&$&$%$%$&$& "72#"&54>#"&5463272#"&546&$%$X$%$%$%$Y$&$&4$$%$%$% ".$#"&54632'2#"&54672#"&546'2#"&546 $%$%$%$%$%$&%%#X4$$%$%$&$&$&$%$& ".72#"&54>#"&5463272#"&546#"&54632&$%$X$%$%$%$e'%$&Y$&$&4$$%$%$%?%$&$ ".:$#"&54632'2#"&54672#"&5462#"&54672#"&546 $%$%$%$%$%$&$%$&%%#X4$$%$%$&$&$&$%$%$%$& #%#"&546322#"&54672#"&546 $%$&%$%$%$%$=##&$$%$&$%$% ".$#"&546322#"&54672#"&546'2#"&546 $%$%$%$%$%$%$%$X4$$%{$&$&$%$%$&$& #/%#"&546322#"&5462#"&54672#"&546 $%$&%$%$&$%$%$%$=##&$$%$&$%$%$%$% ".:$#"&546322#"&5462#"&54672#"&546'2#"&546 $%$%$%$&$%$%$%$%$%$X4$$%{$&$&$%$%$%$%$&$& #/%#"&54632'2#"&54672#"&54672#"&546 $%$&?%$%$%$%$%$%$=$#&$$%$%$%$&$%$% ".:$#"&54632'2#"&54672#"&54672#"&546'2#"&546 $%$%$%$%$%$%$%$&%%#X4$$%$%$&$&$&$%$%$%$& #/;%#"&54632'2#"&54672#"&5462#"&54672#"&546 $%$&?%$%$%$%$&$%$%$%$=$#&$$%$%$%$&$%$%$%$% ".:F$#"&54632'2#"&54672#"&5462#"&54672#"&546'2#"&546 $%$%$%$%$%$&$%$%$%$&%%#X4$$%$%$&$&$&$%$%$%$%$%$& %2#"&5462#"&546%$%$&$%$$&$&$%$% "2#"&546#"&546322#"&546%$%$e$%$%$%$:$%$&4$$%8$&$& #2#"&5462#"&5462#"&546%$%$&$%$&%%$$$#&$%$&z$%$& ".2#"&546#"&546322#"&54672#"&546%$%$e$%$&$%$%$%$:$%$&4$$%z$%$%$&$& #%2#"&5462#"&54672#"&546%$%$&$%$%$%$$&$&$%$%$&$& ".2#"&546#"&54632'2#"&5462#"&546%$%$e$%$%$%$&%%#:$%$&4$$%$%$&z$%$& #/2#"&5462#"&5467#"&54632'2#"&546%$%$&$%$X$%$%>&$%$$$#&$%$&~$#&$$&$& ".:2#"&546#"&54632'2#"&54672#"&54672#"&546%$%$e$%$%$%$&$%$&%%#:$%$&4$$%$%$&$%$%$%$& #2#"&546#"&546322#"&546%$%$e$%$&%$%$9$%$%##&$[$%$% ".2#"&546#"&546322#"&546'2#"&546%$%$e$%$%$%$%$%$:$%$&4$$%7$%$%$&$& #/2#"&546#"&546322#"&54672#"&546%$%$e$%$&?&$%$%$%$9$%$%##&$$%$%$%$% ".:2#"&546#"&546322#"&54672#"&546'2#"&546%$%$e$%$&$%$%$%$%$%$:$%$&4$$%z$%$%$%$%$&$& #/2#"&546#"&54632'2#"&5462#"&546%$%$e$%$&?%$%$%$%$9$%$%$#&$$%$%z$%$% ".:2#"&546#"&54632'2#"&5462#"&546'2#"&546%$%$e$%$%$%$%$%$&%%#:$%$&4$$%$%$&y$%$%$%$& #/;2#"&546#"&54632'2#"&54672#"&54672#"&546%$%$e$%$&?%$%$&$%$%$%$9$%$%$#&$$%$%$%$%$%$% ".:F2#"&546#"&54632'2#"&54672#"&54672#"&546'2#"&546%$%$e$%$%$%$&$%$%$%$&%%#:$%$&4$$%$%$&$%$%$%$%$%$& "2#"&5462#"&5462#"&546%$%$&%%$%$%$$4$$%$%$&z$%$% ".2#"&546#"&546322#"&546'2#"&546%$%$e$%$%$%$%$%$:$%$&4$$%{$&$&$&$& ".2#"&5462#"&5462#"&546#2#"&546%$%$&%%$%$%$&%%$$4$$%$%$&z$%$%$&$& ".2#"&546#"&546322#"&54672#"&546%$%$e$%$&$%$%$%$:$%$&4$$%z$%$%$&$& !-2#"&5462#"&54>#"&5463272#"&546%$%$&$%$X$%$%$%$$4$$%$&$&4$$%$%$% ".:2#"&546#"&54632'2#"&54672#"&546'2#"&546%$%$e$%$%$%$%$%$&%%#:$%$&4$$%$%$&$&$&$%$& !-92#"&5462#"&54>#"&5463272#"&546#"&54632%$%$&$%$X$%$%$%$e'%$&$4$$%$&$&4$$%$%$%?%$&$ ".:F2#"&546#"&54632'2#"&54672#"&5462#"&54672#"&546%$%$e$%$%$%$%$%$&$%$&%%#:$%$&4$$%$%$&$&$&$%$%$%$& #/2#"&546#"&546322#"&54672#"&546%$%$e$%$&%$%$%$%$9$%$%##&$$%$&$%$% ".:2#"&546#"&546322#"&54672#"&546'2#"&546%$%$e$%$%$%$%$%$%$%$:$%$&4$$%{$&$&$%$%$&$& #/;2#"&546#"&546322#"&5462#"&54672#"&546%$%$e$%$&%$%$&$%$%$%$9$%$%##&$$%$&$%$%$%$% ".:F2#"&546#"&546322#"&5462#"&54672#"&546'2#"&546%$%$e$%$%$%$&$%$%$%$%$%$:$%$&4$$%{$&$&$%$%$%$%$&$& #/;2#"&546#"&54632'2#"&54672#"&54672#"&546%$%$e$%$&?%$%$%$%$%$%$9$%$%$#&$$%$%$%$&$%$% ".:F2#"&546#"&54632'2#"&54672#"&54672#"&546'2#"&546%$%$e$%$%$%$%$%$%$%$&%%#:$%$&4$$%$%$&$&$&$%$%$%$& #/;G2#"&546#"&54632'2#"&54672#"&5462#"&54672#"&546%$%$e$%$&?%$%$%$%$&$%$%$%$9$%$%$#&$$%$%$%$&$%$%$%$% ".:FR2#"&546#"&54632'2#"&54672#"&5462#"&54672#"&546'2#"&546%$%$e$%$%$%$%$%$&$%$%$%$&%%#:$%$&4$$%$%$&$&$&$%$%$%$%$%$&p %2#"&546%$%$$&$& $#"&546322#"&546$%$%$%$X4$$%8$&$& %2#"&5462#"&546&$%$&$%$Y$%$&z$&$& "$#"&546322#"&54672#"&546$%$&$%$&%%#X4$$%z$%$%$%$& %2#"&546'2#"&546&$%$%$%$5$%$%$&$& "$#"&54632'2#"&5462#"&546$%$%$%$&%%#X4$$%$%$&z$%$& #%2#"&546'#"&54632'2#"&546&$%$e$%$%>&$%$Y$%$&~$#&$$&$& ".$#"&54632'2#"&54672#"&54672#"&546$%$%$%$&$%$&%%#X4$$%$%$&$%$%$%$&p %#"&546322#"&546$%$%>&$%$=$#&$[$%$% "$#"&546322#"&546'2#"&546$%#%$%$%$%$X4$$%7$%$%$&$& #%#"&546322#"&54672#"&546$%$&&$%$%$%$=##&$$%$%$%$% ".$#"&546322#"&54672#"&546'2#"&546$%$&$%$%$%$%$%$X4$$%z$%$%$%$%$&$& #%#"&54632'2#"&5462#"&546$%$&%$%$%$%$=##&$$%$%z$%$% ".$#"&54632'2#"&5462#"&546'2#"&546$%$%$%$%$%$&%%#X4$$%$%$&y$%$%$%$& #/%#"&54632'2#"&54672#"&54672#"&546$%$&%$%$&$%$%$%$=##&$$%$%$%$%$%$% ".:$#"&54632'2#"&54672#"&54672#"&546'2#"&546$%$%$%$&$%$%$%$&%%#X4$$%$%$&$%$%$%$%$%$&p %2#"&5462#"&546%$%$&$%$Y$&$&z$%$% "$#"&546322#"&546'2#"&546$%#%$%$%$%$X4$$%{$&$&$&$& #%2#"&5462#"&546#2#"&546&%%$%$%$&%%$Y$%$&z$%$%$&$& ".$#"&546322#"&5462#"&54672#"&546$%$%$%$&$%$%$%$X4$$%{$&$&$%$%$&$& "%2#"&546&#"&5463272#"&546&%%$e$%$%$%$Y$%$&4$$%$%$% ".$#"&54632'2#"&54672#"&546'2#"&546$%$%$%$%$%$&%%#X4$$%$%$&$&$&$%$& ".%2#"&546&#"&5463272#"&546#"&54632&%%$e$%$%$%$e'%$&Y$%$&4$$%$%$%?%$&$ ".:$#"&54632'2#"&54672#"&5462#"&54672#"&546$%$%$%$%$%$&$%$&%%#X4$$%$%$&$&$&$%$%$%$&p #%#"&546322#"&54672#"&546$%$%>&$%$&$%$=$#&$$%$&$%$% ".$#"&546322#"&54672#"&546'2#"&546$%#%$%$%$%$%$%$X4$$%{$&$&$%$%$&$& #/%#"&546322#"&5462#"&54672#"&546$%$&>%$%$&$%$%$%$=##&$$%$&$%$%$%$% ".:$#"&546322#"&5462#"&54672#"&546'2#"&546$%$%$%$&$%$%$%$%$%$X4$$%{$&$&$%$%$%$%$&$& #/%#"&54632'2#"&54672#"&54672#"&546$%$&%$%$%$%$%$%$=##&$$%$%$%$&$%$% ".:$#"&54632'2#"&54672#"&54672#"&546'2#"&546$%$%$%$%$%$%$%$&%%#X4$$%$%$&$&$&$%$%$%$& #/;%#"&54632'2#"&54672#"&5462#"&54672#"&546$%$&%$%$%$%$&$%$%$%$=##&$$%$%$%$&$%$%$%$% ".:F$#"&54632'2#"&54672#"&5462#"&54672#"&546'2#"&546$%$%$%$%$%$&$%$%$%$&%%#X4$$%$%$&$&$&$%$%$%$%$%$&p %2#"&54672#"&546%$%$&$%$5$%$%$&$& "$#"&54632'2#"&5462#"&546$%#%$%$%$%$X4$$%$%$&z$&$& #%2#"&54672#"&546'2#"&546&$%$%$%$&%%$Y$%$&$$#&$%$& ".$#"&54632'2#"&546'2#"&54672#"&546$%$%$%$&$%$%$%$X4$$%$%$&$%$%$&$& #%2#"&54672#"&546#2#"&546&$%$%$%$%$%$5$%$%$&$&$&$& ".$#"&54632'2#"&546#2#"&5462#"&546$%$%$%$%$%$&%%#X4$$%$%$&$%$&z$%$& #/%2#"&54672#"&546#"&54632'2#"&546&$%$%$%$f$%$%>&$%$Y$%$&$$#&?$#&$$&$& ".:$#"&54632'2#"&546#2#"&54672#"&54672#"&546$%$%$%$%$%$&$%$&%%#X4$$%$%$&$%$&$%$%$%$&p #%#"&54632'2#"&5462#"&546$%$%>&$%$&$%$=$#&$$%$%z$%$% ".$#"&54632'2#"&5462#"&546'2#"&546$%#%$%$%$%$%$%$X4$$%$%$&y$%$%$&$& #/%#"&54632'2#"&546'2#"&54672#"&546$%$&>%$%$&$%$%$%$=##&$$%$%$%$%$%$% ".:$#"&54632'2#"&546'2#"&54672#"&546'2#"&546$%$%$%$&$%$%$%$%$%$X4$$%$%$&$%$%$%$%$&$& #/%#"&54632'2#"&546#2#"&5462#"&546$%$&>%$%$%$%$%$%$=##&$$%$%$%$%z$%$% ".:$#"&54632'2#"&546#2#"&5462#"&546'2#"&546$%$%$%$%$%$%$%$&%%#X4$$%$%$&$%$&y$%$%$%$& #/;%#"&54632'2#"&546#2#"&54672#"&54672#"&546$%$&>%$%$%$%$&$%$%$%$=##&$$%$%$%$%$%$%$%$% ".:F$#"&54632'2#"&546#2#"&54672#"&54672#"&546'2#"&546$%$%$%$%$%$&$%$%$%$&%%#X4$$%$%$&$%$&$%$%$%$%$%$&p "%2#"&54>#"&54632'2#"&546%$%$Y$%$&$%$Y$&$&4$$%$%$% ".$#"&54632'2#"&54672#"&546'2#"&546$%#%$%$%$%$%$%$X4$$%$%$&$&$&$&$& ".%2#"&54672#"&54672#"&546#2#"&546&%%$%$%$%$%$&%%$Y$%$&$4$$%$%$%$&$& ".:$#"&54632'2#"&54672#"&5462#"&54672#"&546$%$%$%$%$%$&$%$%$%$X4$$%$%$&$&$&$%$%$&$& !-%2#"&54672#"&546#"&5463272#"&546&%%$%$%$f$%$%$%$Y$%$&$4$$%$4$$%$%$% ".:$#"&54632'2#"&546#2#"&54672#"&546'2#"&546$%$%$%$%$%$%$%$&%%#X4$$%$%$&$%$&$&$&$%$& !-9%2#"&54672#"&546#"&5463272#"&546#"&54632&%%$%$%$f$%$%$%$e'%$&Y$%$&$4$$%$4$$%$%$%?%$&$ ".:F$#"&54632'2#"&546#2#"&54672#"&5462#"&54672#"&546$%$%$%$%$%$%$%$&$%$&%%#X4$$%$%$&$%$&$&$&$%$%$%$&p #/%#"&54632'2#"&54672#"&54672#"&546$%$%>&$%$&$%$&$%$=$#&$$%$%$%$&$%$% ".:$#"&54632'2#"&54672#"&54672#"&546'2#"&546$%#%$%$%$%$%$%$%$%$X4$$%$%$&$&$&$%$%$&$& #/;%#"&54632'2#"&54672#"&5462#"&54672#"&546$%$&>%$%$%$%$&$%$%$%$=##&$$%$%$%$&$%$%$%$% ".:F$#"&54632'2#"&54672#"&5462#"&54672#"&546'2#"&546$%$%$%$%$%$&$%$%$%$%$%$X4$$%$%$&$&$&$%$%$%$%$&$& #/;%#"&54632'2#"&546#2#"&54672#"&54672#"&546$%$&>%$%$%$%$%$%$%$%$=##&$$%$%$%$%$%$&$%$% ".:F$#"&54632'2#"&546#2#"&54672#"&54672#"&546'2#"&546$%$%$%$%$%$%$%$%$%$&%%#X4$$%$%$&$%$&$&$&$%$%$%$& #/;G%#"&54632'2#"&546#2#"&54672#"&5462#"&54672#"&546$%$&>%$%$%$%$%$%$&$%$%$%$=##&$$%$%$%$%$%$&$%$%$%$% ".:FR$#"&54632'2#"&546#2#"&54672#"&5462#"&54672#"&546'2#"&546$%$%$%$%$%$%$%$&$%$%$%$&%%#X4$$%$%$&$%$&$&$&$%$%$%$%$%$& %2#"&546#2#"&546&%%$&$%$$%$&$&$& !$#"&54632#"&546322#"&546$%$$%$&%%#X4$$%$4$$%8$%$& #%2#"&546#2#"&5462#"&546&$%$&$%$&$%$Y$%$&$%$&z$&$& !-$#"&54632#"&546322#"&54672#"&546$%$$%$&$%$&%%#X4$$%$4$$%z$%$%$%$& #%2#"&546#2#"&54672#"&546&$%$&$%$%$%$5$%$%$%$%$&$& !-$#"&54632#"&54632'2#"&5462#"&546$%$$%$%$%$&%%#X4$$%$4$$%$%$&z$%$& #/%2#"&546#2#"&5467#"&54632'2#"&546&$%$&$%$X$%$%>&$%$Y$%$&$%$&~$#&$$&$& !-9$#"&54632#"&54632'2#"&54672#"&54672#"&546$%$$%$%$%$&$%$&%%#X4$$%$4$$%$%$&$%$%$%$& #%#"&54632#"&546322#"&546$%$&$%$&%$%$=##&$##&$[$%$% !-$#"&54632#"&546322#"&546'2#"&546$%$$%$%$%$%$%$X4$$%$4$$%7$%$%$&$& #/%#"&54632#"&546322#"&54672#"&546$%$&$%$&?&$%$%$%$=##&$##&$$%$%$%$% !-9$#"&54632#"&546322#"&54672#"&546'2#"&546$%$$%$&$%$%$%$%$%$X4$$%$4$$%z$%$%$%$%$&$& #/;%#"&54632#"&54632'2#"&54672#"&54672#"&546$%$&$%$&?%$%$&$%$%$%$=##&$$#&$$%$%$%$%$%$% !-9$#"&54632#"&54632'2#"&5462#"&546'2#"&546$%$$%$%$%$%$%$&%%#X4$$%$4$$%$%$&y$%$%$%$& #/;%#"&54632#"&54632'2#"&54672#"&54672#"&546$%$&$%$&?%$%$&$%$%$%$=##&$$#&$$%$%$%$%$%$% !-9E$#"&54632#"&54632'2#"&54672#"&54672#"&546'2#"&546$%$$%$%$%$&$%$%$%$&%%#X4$$%$4$$%$%$&$%$%$%$%$%$& #%2#"&546#2#"&5462#"&546&%%$&%%$%$%$Y$%$&$%$&z$%$% !-$#"&54632#"&546322#"&546'2#"&546$%$$%$%$%$%$%$X4$$%$4$$%{$&$&$&$& #/%2#"&546#2#"&5462#"&546#2#"&546&%%$&%%$%$%$&%%$Y$%$&$%$&z$%$%$&$& !-9$#"&54632#"&546322#"&5462#"&54672#"&546$%$$%$%$%$&$%$%$%$X4$$%$4$$%{$&$&$%$%$&$& ".%2#"&546#2#"&54>#"&5463272#"&546&%%$&$%$X$%$%$%$Y$%$&$&$&4$$%$%$% !-9$#"&54632#"&54632'2#"&54672#"&546'2#"&546$%$$%$%$%$%$%$&%%#X4$$%$4$$%$%$&$&$&$%$& ".:%2#"&546#2#"&54>#"&5463272#"&546#"&54632&%%$&$%$X$%$%$%$e'%$&Y$%$&$&$&4$$%$%$%?%$&$ !-9E$#"&54632#"&54632'2#"&54672#"&5462#"&54672#"&546$%$$%$%$%$%$%$&$%$&%%#X4$$%$4$$%$%$&$&$&$%$%$%$& #/%#"&54632#"&546322#"&54672#"&546$%$&$%$&%$%$%$%$=##&$##&$$%$&$%$% !-9$#"&54632#"&546322#"&54672#"&546'2#"&546$%$$%$%$%$%$%$%$%$X4$$%$4$$%{$&$&$%$%$&$& #/;%#"&54632#"&546322#"&5462#"&54672#"&546$%$&$%$&%$%$&$%$%$%$=##&$##&$$%$&$%$%$%$% !-9E$#"&54632#"&546322#"&5462#"&54672#"&546'2#"&546$%$$%$%$%$&$%$%$%$%$%$X4$$%$4$$%{$&$&$%$%$%$%$&$& #/;%#"&54632#"&54632'2#"&54672#"&54672#"&546$%$&$%$&?%$%$%$%$%$%$=##&$$#&$$%$%$%$&$%$% !-9E$#"&54632#"&54632'2#"&54672#"&54672#"&546'2#"&546$%$$%$%$%$%$%$%$%$&%%#X4$$%$4$$%$%$&$&$&$%$%$%$& #/;G%#"&54632#"&54632'2#"&54672#"&5462#"&54672#"&546$%$&$%$&?%$%$%$%$&$%$%$%$=##&$$#&$$%$%$%$&$%$%$%$% !-9EQ$#"&54632#"&54632'2#"&54672#"&5462#"&54672#"&546'2#"&546$%$$%$%$%$%$%$&$%$%$%$&%%#X4$$%$4$$%$%$&$&$&$%$%$%$%$%$& #%2#"&54672#"&5462#"&546&$%$%$%$&$%$5$%$%$&$&$%$% !-$#"&54632'2#"&546#"&546322#"&546$%$%$%$e$%$%$%$X4$$%$%$&4$$%8$&$& #/%2#"&54672#"&5462#"&5462#"&546&$%$%$%$&$%$&%%$Y$%$&$$#&$%$&z$%$& !-9$#"&54632'2#"&546#"&546322#"&54672#"&546$%$%$%$e$%$&$%$%$%$X4$$%$%$&4$$%z$%$%$&$& #/%2#"&54672#"&5462#"&54672#"&546&$%$%$%$&$%$%$%$5$%$%$&$&$%$%$&$& !-9$#"&54632'2#"&546#"&54632'2#"&5462#"&546$%$%$%$e$%$%$%$&%%#X4$$%$%$&4$$%$%$&z$%$& #/;%2#"&54672#"&5462#"&5467#"&54632'2#"&546&$%$%$%$&$%$X$%$%>&$%$Y$%$&$$#&$%$&~$#&$$&$& !-9E$#"&54632'2#"&546#"&54632'2#"&54672#"&54672#"&546$%$%$%$e$%$%$%$&$%$&%%#X4$$%$%$&4$$%$%$&$%$%$%$& #/%#"&54632'2#"&546#"&546322#"&546$%$&>%$%$e$%$&%$%$=##&$$%$%##&$[$%$% !-9$#"&54632'2#"&546#"&546322#"&546'2#"&546$%$%$%$e$%$%$%$%$%$X4$$%$%$&4$$%7$%$%$&$& #/;%#"&54632'2#"&546#"&546322#"&54672#"&546$%$&>%$%$e$%$&?&$%$%$%$=##&$$%$%##&$$%$%$%$% !-9E$#"&54632'2#"&546#"&546322#"&54672#"&546'2#"&546$%$%$%$e$%$&$%$%$%$%$%$X4$$%$%$&4$$%z$%$%$%$%$&$& #/;%#"&54632'2#"&546#"&54632'2#"&5462#"&546$%$&>%$%$e$%$&?%$%$%$%$=##&$$%$%$#&$$%$%z$%$% !-9E$#"&54632'2#"&546#"&54632'2#"&5462#"&546'2#"&546$%$%$%$e$%$%$%$%$%$&%%#X4$$%$%$&4$$%$%$&y$%$%$%$& #/;G%#"&54632'2#"&546#"&54632'2#"&54672#"&54672#"&546$%$&>%$%$e$%$&?%$%$&$%$%$%$=##&$$%$%$#&$$%$%$%$%$%$% !-9EQ$#"&54632'2#"&546#"&54632'2#"&54672#"&54672#"&546'2#"&546$%$%$%$e$%$%$%$&$%$%$%$&%%#X4$$%$%$&4$$%$%$&$%$%$%$%$%$& ".%2#"&54672#"&5462#"&5462#"&546&%%$%$%$&%%$%$%$Y$%$&$4$$%$%$&z$%$% !-9$#"&54632'2#"&546#"&546322#"&546'2#"&546$%$%$%$e$%$%$%$%$%$X4$$%$%$&4$$%{$&$&$&$& ".:%2#"&54672#"&5462#"&5462#"&546#2#"&546&%%$%$%$&%%$%$%$&%%$Y$%$&$4$$%$%$&z$%$%$&$& !-9E$#"&54632'2#"&546#"&546322#"&5462#"&54672#"&546$%$%$%$e$%$%$%$&$%$%$%$X4$$%$%$&4$$%{$&$&$%$%$&$& "-9%2#"&54672#"&5462#"&54>#"&5463272#"&546&%%$%$%$&$%$X$%$%$%$Y$%$&$4$$%$&$&4$$%$%$% !-9E$#"&54632'2#"&546#"&54632'2#"&54672#"&546'2#"&546$%$%$%$e$%$%$%$%$%$&%%#X4$$%$%$&4$$%$%$&$&$&$%$& "-9E%2#"&54672#"&5462#"&54>#"&5463272#"&546#"&54632&%%$%$%$&$%$X$%$%$%$e'%$&Y$%$&$4$$%$&$&4$$%$%$%?%$&$ !-9EQ$#"&54632'2#"&546#"&54632'2#"&54672#"&5462#"&54672#"&546$%$%$%$e$%$%$%$%$%$&$%$&%%#X4$$%$%$&4$$%$%$&$&$&$%$%$%$& #/;%#"&54632'2#"&546#"&546322#"&54672#"&546$%$&>%$%$e$%$&%$%$%$%$=##&$$%$%##&$$%$&$%$% !-9E$#"&54632'2#"&546#"&546322#"&54672#"&546'2#"&546$%$%$%$e$%$%$%$%$%$%$%$X4$$%$%$&4$$%{$&$&$%$%$&$& #/;G%#"&54632'2#"&546#"&546322#"&5462#"&54672#"&546$%$&>%$%$e$%$&%$%$&$%$%$%$=##&$$%$%##&$$%$&$%$%$%$% !-9EQ$#"&54632'2#"&546#"&546322#"&5462#"&54672#"&546'2#"&546$%$%$%$e$%$%$%$&$%$%$%$%$%$X4$$%$%$&4$$%{$&$&$%$%$%$%$&$& #/;G%#"&54632'2#"&546#"&54632'2#"&54672#"&54672#"&546$%$&>%$%$e$%$&?%$%$%$%$%$%$=##&$$%$%$#&$$%$%$%$&$%$% !-9EQ$#"&54632'2#"&546#"&54632'2#"&54672#"&54672#"&546'2#"&546$%$%$%$e$%$%$%$%$%$%$%$&%%#X4$$%$%$&4$$%$%$&$&$&$%$%$%$& #/;GS%#"&54632'2#"&546#"&54632'2#"&54672#"&5462#"&54672#"&546$%$&>%$%$e$%$&?%$%$%$%$&$%$%$%$=##&$$%$%$#&$$%$%$%$&$%$%$%$% !-9EQ]$#"&54632'2#"&546#"&54632'2#"&54672#"&5462#"&54672#"&546'2#"&546$%$%$%$e$%$%$%$%$%$&$%$%$%$&%%#X4$$%$%$&4$$%$%$&$&$&$%$%$%$%$%$&XX& yJXX 2"&546.'!355!#>1x}_a _^X{|}|__XX "2"&5467&#"7327'654'1x-H\ag?=IaaI=@X{|}|e=YI``H@@I^`J+-& y(0'332653#"&5%32+#"=#"4;5432(+z^\~+ginnnn[jlYef4([[(f(0 !"5!"5((H(z^(0 !42!420((^Eq %5'?'S显ded|S33R3?+!7'&54?'&547637#"'&'"?7_   e2_ 3b?+ N@?, N@@+ N"GF@*.#!#"/7632!2.  F^ SE@ SE@?+7'&54?'&547637#"/"_2_ >+ V@?+ V@?+ V"G7#"/7632354632#"=    5 ZF@2& 2&@2& &@2& 2&@2& &@#5%7' ,  8&  #5 `X#5 `#5@#5 `5@WX3!VXWX3!%!!VX("h53x"}}"h35#53(((xJ-U}}R0%!&&q[R0 !'%!5^1_&&qJ"+-E %%'7-ؕ(||#%O%%%ؕ(%M'!!Mו)&XX 2"&5461xX{|}|Y!'7,Y%7''7,8x'+x'77+}}~񤤭LM%'7.L~QB'+WWUBs~~N?'77+WWUU ?s~~11::D> hD@D> iD@c"%#"&547%632'#"&547%632  \    \          6jvwG @S#"'4&54632RL_ OS#"/&54632RL S;26=4&+"+"&=46;2|!!!!(55((55(X###D55DD55DS,37632+"&=463!54&+"#"546;2!";254#"YR?;\6''6,!!+26'!!];Q*)).[4E+D3?""804EW"M"4,S!&546323632  wSS-4%+"&=%54&+"32+546;2;>3#"325*26'!!Dm'66'w!) 1;QQ+L;H804E ""-E44E " 6!)^4S8%4&+"32+4326;2#"54326!NT;d6l6'13!`"^X0F4EhK.S3#"&=463!#"=!";2D6''6U!!=4EE4h?""S *7#"543232#"&546;54&+!#"325|j6''6*.3%4!T`444EE45-"2"?S235#"54?632+"&=46;2+";265#"54,!  =  (55('6]V!!!,˜!  =  D55DD4"""1S%+"&5432;26=432'66'!!yE44E3""S12+;26=4&+"54;2+"&54632#"/,!!!V]6'(55( =  !"""4DD55D =  !S(.3#546?54#"#"54632732#"54;4&+3%>%!3NIj6']4!RZ)BP4(03[L9#8/ak4E1"=b+ S *72+;26=!546;2+"&=%4&+"!P!!w'66''66'!!`)""UE44EE44EA""=S!732+432732#"54;5!7!54&+|;dq6'd;#=![)4E|)"S-354&+";26=#"54;+"&=46;2#"54P!!!!Py'66''66'y7#""""$32#"'32+574&'#"&46;2#"54;4&+"3$$<+"//"2]:4))46']4!!!$$"/D/-2n24E1"F).3"54;5463!54&+"#"546;2#"54326=!"H '6,!!+26'13!!yE4?""804EK.""S84!#"54;'&54632654&#"32#"&5432>32y 6deXMV!7-W0]MB X C36VhY",M)$)\Y>O Z S8C32+;26=4&+"32+546;2+"';26=#"54;#!"&SyP!!!!Py'66''6 En$00$Z#""""$00S 3%#"3257#"'%632#"&546;.#"#"543224) `P@L2(5*26&* c7,c! v| ?`T"K5D57+!/1>"}fS S8;532+;26=%546;2#"54;54&+"+"'32#"&SmD!!w'66'mD!!'6 "5/O"" E44E-"" E4 m"2S *!#"&=46;2;26=#"54;-54&+"6''66'w!!Py'`!!4EE44E "")AE4 ""S/1#"!232+4&#!32#"&546;2#"54;4&!,6' 4!!31'60$oFm"l4EZ"".KE40>S(+%6=4&+"54;2+"&=46;2+"73'!5((55((5!ss1*#5DD55DD5#+ S ~S S8 S-"+"&532+;26=46;2#"54;4&#/C46'd;!/C46'd;!$A84EI"$A84E1"S8J!"&=4&+"32#"&=46;2;26=4&#"5432+"&=32+;26=#4C/!!7-'64C/!!7-'66'yP!!8A$"",ME48A$"",MjE4/D"# "l S S8C#!"&=32+;26=+"&=46;2#"54;54&+";26=#"54;$00$nE 6''66'yP!!!!PyZ>00>& r 4EE44E<$""""#S8 &7;5432#"543265#"&53'>=|!R04!j6'B5"iK."a4EIzOOQS#5373+"&=32+;267'#"S--(55(yP!!2llD55DA)"#.rqcS#732+34&+"32+546;2#|;d-\!!Py(55(0)w""*D55DS:7";26=#"54;+"&547.546;2#"54;54&+"32n!!Py'66'?'66'yP!!87h# ")AE44EK$*E44EA)" #4%S$%#"54326=#"&532+;5#"54;-7!j6'yP!RPyyM,"4EI"WS *32+"&=%54&+"32+546 ;2656''66'!!Py'b!!4EE44E "")AE4 ""S%%+"&=46;2'&+"26%;26="(55((55(7WhW!!HyD55DD55DA5FF##pD462"#m#"'%&54632| -_`'m"&54?62"'ԅ    C\  dd  Gam+"&=3;26=$0r0$)dm>00>%%Gam&  & 8)%";26=3+"&546$.B()#16VU)).'&>0A;<98)&  S62>32".54632\M(" A3*1?X )\;?"&#"354632#"&#"32+32#!"54;#"54;546323*'?,D 3'HHHSMM?," r22%;;5K 2%;O;5K OM\$(32#!"54;#"54;54632#"&#"#KSMM?,D 3'O;5K 2%;OG \%32#!"54;#"54;5463#"32+MtSMM?,XX&HH\O;5K 2%;G\:>%#32#!"54;#"54;54632#"&#"354632#"&#"3؄+B<=VJJ'HSMM?,5)||;., X?`2%MO;5K4k3A sI't43232>32#"& B&E,)I`.56Zq'<$7#"&546;3>7#"&546;#!"&5463B] I8 %""V ( AR )  I3*  TP  </4632>=#"&546;#"'32+547'&< :?5 ^| ;>5 ^| 8,~  d6| 8,~  f4|<!"&5463!2+#"&5  Y     ] < &74632#"&!2#"&5.'.'!"&546_   +JW   #'   PW %&%   <%2#!"&546;>=.'.'#"&5463T'- MS &%(  %& #>)NY   (&%%'   <i!"&54?>=!54632!  I  +  ~ u-+< !.'72!4?#"&5463hi (%&'- E*K h&%( )#>))*K  <!2#"&5.'.'!"&546P+JW   #' PW %&%   *2#"&5.'.'#+"&546;>7#"&5463}13   $'|+ $0)Z  !G2 %'# ˂  !65  H"54;543232#c3BW&1:W&3BW&&i1:W&&i<&<8&<'<0&&2<&<&@&&& @&V<8&<&<i& 0<&&<&<8&f,<&g42&18&<&3:&i&,@W&<0E&<E&<E&<i"%#"'&=4632>=#"&546; I  :?5 ^|   8,~  d6|o)-32=63232=>54&#"";264&# D=>PMX^GbP(?> E%D)"?2+E%D),L8EUA,,FF5E+Z- +  Q$iR D2 $      V  6   % V1  H       `N @ $  !" $& 'n*-d   2    Copyleft 2002, 2003, 2005, 2008, 2009, 2010 Free Software Foundation.Copyleft 2002, 2003, 2005, 2008, 2009, 2010 Free Software Foundation.FreeMonoFreeMonoMediumMediumFontForge 2.0 : Free Monospaced : 16-9-2010FontForge 2.0 : Free Monospaced : 16-9-2010Free MonospacedFree MonospacedVersion $Revision: 1.215 $ Version $Revision: 1.215 $ FreeMonoFreeMonoGNUGNUhttps://savannah.gnu.org/projects/freefont/https://savannah.gnu.org/projects/freefont/This computer font is part of GNU FreeFont. It 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/>. As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.This computer font is part of GNU FreeFont. It 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 . As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.http://www.gnu.org/copyleft/gpl.htmlhttp://www.gnu.org/copyleft/gpl.htmlthng?@O<V9NormalNormalNormalNormalNormalnavadnoNormlne1KG=K9NormalNormalNormalNormalnyNormalnormalusisvidjsNormalemenengahNormlStandardNormalNormalNormaaliStandaardnormaloby ejnNormal=>@<0;5=Arrunta2   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  softhyphenmicromiddotAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccentGcedilla gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflexKcedillakcedilla kgreenlandicLacutelacuteLcedillalcedillaLcaronlcaronLdotldotNacutenacuteNcedillancedillaNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracuteRcedillarcedillaRcaronrcaronSacutesacute Scircumflex scircumflexuni0162uni0163TcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9yoghtailuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0220uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233dotlessjuni0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cepsilonlatinrevhookepsilonlatinrevcloseduni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276 omegacloseduni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289 upsilonlatinvhookuni028Cuni028Duni028Euni028Funi0290uni0291uni0292ezhcurluni0294uni0295uni0296uni0297uni0298uni0299 epsiloncloseduni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4dzcurluni02A6uni02A7tccurluni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AF hsuperior hhooksuperjsuperrsuper rturnsuperrturnrthooksuper Rturnsuperwsuperysuperprimemod primedblmod quoteleftmod apostrophe apostropherev ringhalfright ringhalfleftuni02C0uni02C1arrowheadleftmoduni02C3uni02C4uni02C5verticallinemoduni02C9 acutemodifieruni02CBuni02CCuni02CD gravelowmoduni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6minusmoduni02DEuni02DF gammasuperlsuperssuperxsuperglottalrevsuperuni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F3uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF gravecomb acutecomb circumflexcmb tildecomb macroncomb overlinecmbbrevecmb dotaccentcmb diaeresiscomb hookabovecombringcmbhungarumlautcmbcaroncmb linevertnosplinevertdblnosp gravedblnospbreve_dotaccentuni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031ahorncmbuni031cuni031duni031euni031funi0320hooksubpalatnosphooksubretronosp dotbelowcombuni0324uni0325 commasubnosp cedillacmb ogonekcmbuni0329uni032auni032buni032Cuni032Duni032Euni032Funi0330macronbelowcmbuni0332uni0333tildeoverlaycmbstrokeshortoverlaycmbstrokelongoverlaycmbslashshortnosp slashlongnospringrighthalfsubnospuni033Auni033buni033cuni033duni033eoverscoredblnospuni0340uni0341perispomenigreekcmbuni0343dialytikatonoscmbypogegrammenigreekcmbuni0346uni0347uni0348uni0349uni034Buni034cuni034duni0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035anumeralsigngreeknumeralsignlowergreek ypogegrammeni questiongreektonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdanuxiomicronrho sigmafinalsigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosbetasymbolgreekthetasymbolgreekUpsilonhooksymbolUpsilonhooktonosUpsilonhookdiaeresisphisymbolgreekomegapi StigmagreekstigmaDigamma kappascriptrhosymbolgreekuni03f4uni03F5uni0400 Iocyrillic Djecyrillic Gjecyrillic Ecyrillic Dzecyrillic Icyrillic Yicyrillic Jecyrillic Ljecyrillic Njecyrillic Tshecyrillic Kjecyrillicuni040DUshortcyrillic Dzhecyrillic Acyrillic Becyrillic Vecyrillic Gecyrillic Decyrillic Iecyrillic Zhecyrillic Zecyrillic IicyrillicIishortcyrillic Kacyrillic Elcyrillic Emcyrillic Encyrillic Ocyrillic Pecyrillic Ercyrillic Escyrillic Tecyrillic Ucyrillic Efcyrillic Khacyrillic Tsecyrillic Checyrillic Shacyrillic ShchacyrillicHardsigncyrillic YericyrillicSoftsigncyrillicEreversedcyrillic IUcyrillic IAcyrillic acyrillic becyrillic vecyrillic gecyrillic decyrillic iecyrillic zhecyrillic zecyrillic iicyrilliciishortcyrillic kacyrillic elcyrillic emcyrillic encyrillic ocyrillic pecyrillic ercyrillic escyrillic tecyrillic ucyrillic efcyrillic khacyrillic tsecyrillic checyrillic shacyrillic shchacyrillichardsigncyrillic yericyrillicsoftsigncyrillicereversedcyrillic iucyrillic iacyrillicuni0450 iocyrillic djecyrillic gjecyrillic ecyrillic dzecyrillic icyrillic yicyrillic jecyrillic ljecyrillic njecyrillic tshecyrillic kjecyrillicuni045Dushortcyrillic dzhecyrillic Omegacyrillic omegacyrillic Yatcyrillic yatcyrillicuni0464YuslittlecyrillicyuslittlecyrillicYuslittleiotifiedcyrillicYusbigcyrillicyusbigcyrillicYusbigiotifiedcyrillicyusbigiotifiedcyrillic Psicyrillic psicyrillic FitacyrillicIzhitsacyrillicizhitsacyrillicIzhitsadblgravecyrillicizhitsadblgravecyrillic Omeghatitlo omeghatitlotitlocyrilliccmbpalatalizationcyrilliccmbdasiapneumatacyrilliccmbpsilipneumatacyrilliccmbuni0487uni048auni048buni048Cuni048Duni048Euni048FGheupturncyrillicgheupturncyrillicGhestrokecyrillicghestrokecyrillicGhemiddlehookcyrillicghemiddlehookcyrillicZhedescendercyrilliczhedescendercyrillicZedescendercyrilliczedescendercyrillicKadescendercyrillickadescendercyrillicKaverticalstrokecyrillickaverticalstrokecyrillicKastrokecyrillickastrokecyrillicKabashkircyrillickabashkircyrillicEndescendercyrillicendescendercyrillic Enghecyrillic enghecyrillicPemiddlehookcyrillicpemiddlehookcyrillicHaabkhasiancyrillichaabkhasiancyrillicEsdescendercyrillicesdescendercyrillicTedescendercyrillictedescendercyrillicUstraightcyrillicustraightcyrillicUstraightstrokecyrillicustraightstrokecyrillicHadescendercyrillichadescendercyrillic Tetsecyrillic tetsecyrillicChedescendercyrillicchedescendercyrillicCheverticalstrokecyrilliccheverticalstrokecyrillic Shhacyrillic shhacyrillicCheabkhasiancyrilliccheabkhasiancyrillicChedescenderabkhasiancyrillicchedescenderabkhasiancyrillicpalochkacyrillicZhebrevecyrilliczhebrevecyrillicKahookcyrillickahookcyrillicuni04C5uni04C6Enhookcyrillicenhookcyrillicuni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFAbrevecyrillicabrevecyrillicAdieresiscyrillicadieresiscyrillic Aiecyrillic aiecyrillicIebrevecyrilliciebrevecyrillic Schwacyrillic schwacyrillicSchwadieresiscyrillicschwadieresiscyrillicZhedieresiscyrilliczhedieresiscyrillicZedieresiscyrilliczedieresiscyrillicDzeabkhasiancyrillicdzeabkhasiancyrillicImacroncyrillicimacroncyrillicIdieresiscyrillicidieresiscyrillicOdieresiscyrillicodieresiscyrillicObarredcyrillicobarredcyrillicObarreddieresiscyrillicobarreddieresiscyrillicuni04ECuni04EDUmacroncyrillicumacroncyrillicUdieresiscyrillicudieresiscyrillicUhungarumlautcyrillicuhungarumlautcyrillicChedieresiscyrillicchedieresiscyrillicuni04f6uni04f7Yerudieresiscyrillicyerudieresiscyrillicuni0510uni0511uni0512uni0513uni051auni051buni051cuni051duni051euni051funi0531uni0532uni0533uni0534uni0535uni0536uni0537uni0538uni0539uni053Auni053Buni053Cuni053Duni053Euni053Funi0540uni0541uni0542uni0543uni0544uni0545uni0546uni0547uni0548uni0549uni054Auni054Buni054Cuni054Duni054Euni054Funi0550uni0551uni0552uni0553uni0554uni0555uni0556ringhalfleftarmenianapostrophearmenianemphasismarkarmenianexclamarmenian commaarmenianquestionarmenianabbreviationmarkarmenian aybarmenian benarmenian gimarmenian daarmenian echarmenian zaarmenian eharmenian etarmenian toarmenian zhearmenian iniarmenian liwnarmenian xeharmenian caarmenian kenarmenian hoarmenian jaarmenian ghadarmenian cheharmenian menarmenian yiarmenian nowarmenian shaarmenian voarmenian chaarmenian peharmenian jheharmenian raarmenian seharmenian vewarmenian tiwnarmenian reharmenian coarmenian yiwnarmenian piwrarmenian keharmenian oharmenian feharmenianechyiwnarmenianperiodarmenianuni058A shevahebrewhatafsegolhebrewhatafpatahhebrewhatafqamatshebrew hiriqhebrew tserehebrew segolhebrew patahhebrew qamatshebrew holamhebrew qubutshebrew dageshhebrew siluqhebrew maqafhebrew rafehebrew paseqhebrew shindothebrew sindothebrewsofpasuqhebrewupperdothebrew alefhebrew bethebrew gimelhebrew dalethebrewhehebrew vavhebrew zayinhebrew hethebrew tethebrew yodhebrewfinalkafhebrew kafhebrew lamedhebrewfinalmemhebrew memhebrewfinalnunhebrew nunhebrew samekhhebrew ayinhebrew finalpehebrewpehebrewfinaltsadihebrew tsadihebrew qofhebrew reshhebrew shinhebrew tavhebrew vavvavhebrew vavyodhebrew yodyodhebrew gereshhebrewgershayimhebrewuni10D0uni10D1uni10D2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10f9uni10fbuni10fcuni13A0uni13A1uni13A2uni13A3uni13A4uni13A5uni13A6uni13A7uni13A8uni13A9uni13AAuni13ABuni13ACuni13ADuni13AEuni13AFuni13B0uni13B1uni13B2uni13B3uni13B4uni13B5uni13B6uni13B7uni13B8uni13B9uni13BAuni13BBuni13BCuni13BDuni13BEuni13BFuni13C0uni13C1uni13C2uni13C3uni13C4uni13C5uni13C6uni13C7uni13C8uni13C9uni13CAuni13CBuni13CCuni13CDuni13CEuni13CFuni13D0uni13D1uni13D2uni13D3uni13D4uni13D5uni13D6uni13D7uni13D8uni13D9uni13DAuni13DBuni13DCuni13DDuni13DEuni13DFuni13E0uni13E1uni13E2uni13E3uni13E4uni13E5uni13E6uni13E7uni13E8uni13E9uni13EAuni13EBuni13ECuni13EDuni13EEuni13EFuni13F0uni13F1uni13F2uni13F3uni13F4uni16A0uni16A1uni16A2uni16A3uni16A4uni16A5uni16A6uni16A7uni16A8uni16A9uni16AAuni16ABuni16ACuni16ADuni16AEuni16AFuni16B0uni16B1uni16B2uni16B3uni16B4uni16B5uni16B6uni16B7uni16B8uni16B9uni16BAuni16BBuni16BCuni16BDuni16BEuni16BFuni16C0uni16C1uni16C2uni16C3uni16C4uni16C5uni16C6uni16C7uni16C8uni16C9uni16CAuni16CBuni16CCuni16CDuni16CEuni16CFuni16D0uni16D1uni16D2uni16D3uni16D4uni16D5uni16D6uni16D7uni16D8uni16D9uni16DAuni16DBuni16DCuni16DDuni16DEuni16DFuni16E0uni16E1uni16E2uni16E3uni16E4uni16E5uni16E6uni16E7uni16E8uni16E9uni16EAuni16EBuni16ECuni16EDuni16EEuni16EFuni16F0uni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBClenisprosgegrammenipsili perispomenidialytikaperispomeniuni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCC psili_varia psili_oxiauni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDB dasia_varia dasia_oxiadasia_perispomeniuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECdialytika_variadialytika_oxiavariauni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCoxiadasiaenquademquadenspaceemspaceuni2004uni2005uni2006uni2007uni2008 thinspaceuni200Auni200B afii61664afii301afii299afii300 hyphentwo hyphennobreak figuredash horizontalbardblverticalbar underscoredbl quotereverseduni201Ftrianglebulletonedotenleadertwodotenleaderuni2027uni2028uni2029uni202Auni202B afii61573 afii61574 afii61575uni202Fpertenthousandminutesecond primetriple primereversed primedblrevprimetriplerevuni2038uni203B exclamdbluni203Duni203Euni203Funi2040uni2041uni2042uni2043uni2045uni2046uni2047uni2048uni2049uni204Auni204Buni204Cuni204Duni204Euni204Funi2050uni2051uni2052uni2053uni2054uni2055uni2056uni2057uni2058uni2059uni205Auni205Buni205Cuni205Duni205Euni205Funi2060uni2061uni2062uni2063uni2064 zerosuperioruni2071 foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperioruni207Auni207Buni207Cparenleftsuperiorparenrightsuperior nsuperior zeroinferior oneinferior twoinferior threeinferior fourinferior fiveinferior sixinferior seveninferior eightinferior nineinferioruni208Auni208Buni208Cparenleftinferiorparenrightinferioruni2090uni2091uni2092uni2093uni2094 colonmonetarycruzeiroliramillnairapesetarupeewon afii57636dongEurouni20ADuni20AEuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5leftharpoonaccentrightharpoonaccentuni20D2uni20D6uni20D7uni20DBuni20DCuni20DDuni20DEuni20DFuni20E0uni20E1uni20e2uni20e3uni20e5uni20e6uni20E8uni20eauni20ebuni20ECuni20EDuni20EEuni20EFuni2100uni2101uni2102uni2103CL afii61248uni2106uni2107scrupleuni2109uni210Dplanck planckover2piuni2110Ifrakturuni2112 afii61289lbbaruni2115 afii61352 recordright weierstrassuni2119uni211ARfrakturuni211D prescription servicemarkuni2121uni2124uni2126uni2127uni2129uni212Auni212B estimateduni2132alephuni2136uni2137uni2138uni2139uni213auni213buni2141uni2142uni2143uni2144uni214buni214duni214eonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215Funi2160uni2161uni2162uni2163uni2164uni2165uni2166uni2167uni2168uni2169uni216Auni216Buni216Cuni216Duni216Euni216Funi2170uni2171uni2172uni2173uni2174uni2175uni2176uni2177uni2178uni2179uni217auni217buni217cuni217duni217euni217f arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni219Cuni219Duni219Euni219Funi21A0uni21A1uni21A2uni21A3uni21A4uni21A5uni21A6uni21A7 arrowupdnbseuni21A9uni21AAuni21ABuni21ACuni21ADuni21AEuni21AFuni21B0uni21B1uni21B2uni21B3uni21B4carriagereturnuni21B6uni21B7uni21B8uni21B9uni21BAuni21BBharpoonleftbarbuparrowleftbothalfuni21BEuni21BFarrowrighttophalfarrowrightbothalfuni21C2uni21C3uni21C4uni21C5uni21C6uni21C7uni21C8uni21C9uni21CAuni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5arrowsquigglerightuni21e6uni21e7uni21e8uni21e9uni21f3 universaluni2201 existentialuni2204emptyset Delta.mathgradientelement notelementuni220Asuchthatuni220Cuni220Duni220e coproductuni2213uni2214uni2215 backslashmath asteriskmath ringoperatorbulletoperatoruni221Buni221C proportional orthogonalangleuni2221uni2222dividesnotbarparallel notparallel logicaland logicalor intersectionunionuni222Cuni222Duni222Euni222F volintegral clwintegralclwcontintegraluni2233 thereforebecauseratio proportiondotminusexcessgeomproportion homotheticsimilar revsimilarlazysinvsine wreathproductuni2241uni2242uni2243uni2244 congruentapproxnotequaluni2247notalmostequaluni224auni224Buni224cequivasymptoticgeomequivalentuni224Funi2250uni2251uni2252uni2253 colonequal equalcolonuni2256uni2257uni2258uni2259uni225Auni225buni225cuni225duni225euni225f equivalenceuni2262uni2263uni2266uni2267lessnotdblequaluni2269muchless muchgreateruni226Cnotequivasymptoticuni226Euni226F notlessequalnotgreaterequaluni2272uni2273uni2274uni2275uni2276uni2277uni2278uni2279precedesfollows precedesequal followsequaluni227euni227f notprecedes notfollows propersubsetpropersuperset notsubset notsuperset reflexsubsetuni2287 notsubseteqlnotsuperseteql subsetnoteqlsupersetnoteqlmultisetmultiplymultiset unionmulti squareimagesquareoriginal subsetsqequalsupersetsqequalintersectionsqunionsq circleplus circleminuscirclemultiply circledivide circledot circleringcircleasterisk circleequal circlevertbar squareplus squareminussquaremultiply squaredot turnstileleftturnstilerighttackdown perpendicular assertion truestate satisfiesforces forcesbar forceextr notturnstile notsatisfynotforcenotforcesextraprurelscurel triangleright trianglelefttriangleftequaltriangrightequalorigofimageofmultimaphermitconjmatrixintercaluni22bbuni22bcuni22bduni22BE righttriangleuni22C0 narylogicaloruni22C2 naryunion diamondmathdotmathuni22C6uni22c7bowtie NameMe.8905 NameMe.8906 multiopenleftmultiopenrightuni22cduni22ceuni22cfuni22d0uni22d1uni22d2uni22d3fork equalparallellessdot greaterdot verymuchlessverymuchgreaterlessequalorgreatergreaterequalorlessuni22dcuni22dduni22deuni22dfpreceedsnotequalfollowsnotequalnotsubsetsqequalnotsupersetsqequalsqimageornotequalsqoriginornotequallessnotequivlntgreaternotequivlntpreceedsnotsimilarfollowsnotequivlntuni22eauni22ebuni22ecuni22edellipsisverticaluni22EFuni22F0uni22F1uni2300houseuni2303 caretinverteduni2305perspcorrespond ceilingleft ceilingright floorleft floorrightuni230Cuni230Duni230Euni230F revlogicalnotuni2312uni2314uni2315 propelloruni2319uni231Cuni231Duni231Euni231F integraltp integralbtfrownuni2323uni2324option deleterightclear angleleft angleright deleteleftuni232cuni2336uni2337uni2338uni2339uni233auni233buni233cuni233duni233euni233funi2340uni2341uni2342uni2343uni2344uni2345uni2346uni2347uni2348uni2349uni234auni234buni234cuni234duni234euni234funi2350uni2351uni2352uni2353uni2354uni2355uni2356uni2357uni2358uni2359uni235auni235buni235cuni235duni235euni235funi2360uni2361uni2362uni2363uni2364uni2365uni2366uni2367uni2368uni2369uni236auni236buni236cuni236duni236euni236funi2370uni2371uni2372uni2373uni2374uni2375uni2376uni2377uni2378uni2379uni237auni237cuni237duni237euni237funi2380uni2381uni2382uni2383uni2384uni2385uni2386uni2387uni2388uni2389uni238auni238buni238cuni238duni238euni238funi2390uni2391uni2392uni2393uni2394uni2395uni2396uni2397uni2398uni2399uni239auni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23A7uni23A8uni23A9uni23AAuni23ABuni23ACuni23ADuni23AEuni23AFuni23B0uni23B1uni23B2uni23B3uni23b4uni23b5uni23b6uni23B7uni23BAuni23BBuni23BCuni23BDuni23beuni23bfuni23c0uni23c1uni23c2uni23c3uni23c4uni23c5uni23c6uni23c7uni23c8uni23c9uni23cauni23cbuni23ccuni23cduni23ceuni23cfuni23dauni23dbuni23deuni23dfuni23e2uni23e3uni23e4uni23e5uni23e6uni2400uni2401uni2402uni2403uni2404uni2405uni2406uni2407uni2408uni2409uni240auni240buni240cuni240duni240euni240funi2410uni2411uni2412uni2413uni2414uni2415uni2416uni2417uni2418uni2419uni241auni241buni241cuni241duni241euni241funi2420uni2421blankuni2424uni2440uni2441uni2442uni2443uni2444uni2445uni2446uni2447uni2448uni2449uni244auni2460uni2461uni2462uni2463uni2464uni2465uni2466uni2467uni2468uni2469 lthorizformuni2501SF110000uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250BSF010000uni250Duni250Euni250FSF030000uni2511uni2512uni2513SF020000uni2515uni2516uni2517SF040000uni2519uni251Auni251BSF080000uni251Duni251Euni251Funi2520uni2521uni2522uni2523SF090000uni2525uni2526uni2527uni2528uni2529uni252Auni252BSF060000uni252Duni252Euni252Funi2530uni2531uni2532uni2533SF070000uni2535uni2536uni2537uni2538uni2539uni253Auni253BSF050000uni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254Buni254Cuni254Duni254Euni254FSF430000SF240000SF510000SF520000SF390000SF220000SF210000SF250000SF500000SF490000SF380000SF280000SF270000SF260000SF360000SF370000SF420000SF190000SF200000SF230000SF470000SF480000SF410000SF450000SF460000SF400000SF540000SF530000SF440000 ltarcdnrtformuni256Euni256Funi2570uni2571uni2572uni2573uni2574uni2575uni2576uni2577uni2578uni2579uni257Auni257Buni257Cuni257Duni257Euni257Fupblockuni2581uni2582uni2583dnblockuni2585uni2586uni2587blockuni2589uni258Auni258Blfblockuni258Duni258Euni258Frtblock shadelightshade shadedarkuni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259F filledboxsquareuni25A2uni25A3uni25A4uni25A5uni25A6uni25A7uni25A8uni25A9H18543H18551 filledrectuni25ADuni25AEuni25AFuni25B0uni25B1triagupuni25B3uni25B4uni25B5uni25B6uni25B7uni25B8uni25B9triagrtuni25BBtriagdnuni25BDuni25BEuni25BFuni25C0uni25C1uni25C2uni25C3triaglfuni25C5uni25C6uni25C7uni25C8uni25C9circleuni25CCuni25CDuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7 invbullet invcircleuni25DAuni25DBuni25DCuni25DDuni25DEuni25DFuni25E0uni25E1uni25E2uni25E3uni25E4uni25E5 openbulletuni25E7uni25E8uni25E9uni25EAuni25EBuni25ECuni25EDuni25EE largecircleuni25F0uni25F1uni25F2uni25F3uni25F4uni25F5uni25F6uni25F7uni25F8uni25F9uni25FAuni25FBuni25FCuni25FDuni25FEuni25FFuni2600uni2601uni2602uni2603uni2605uni2606uni2607uni2608uni2609uni2610uni2611uni2612uni261auni261buni261Cuni261duni261Euni261funi2626uni2628crossuni262Euni262funi2630uni2631uni2632uni2633uni2634uni2635uni2636uni2637uni2639 smileface invsmilefacesununi263duni263euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264auni264buni264cuni264duni264euni264funi2650uni2651uni2652uni2653spade heartopen diamondopenclubspadesuitwhiteheartdiamond clubsuitwhiteuni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi2680uni2681uni2682uni2683uni2684uni2685uni27c0uni27c1uni27c2uni27c3uni27c4uni27c5uni27c6uni27c7uni27c8uni27c9uni27cauni27ccuni27d0uni27d1uni27d2uni27d3uni27d4uni27d5uni27d6uni27d7uni27e4uni27e5uni27E6uni27E7uni27E8uni27E9uni27EAuni27EBuni27f2uni27f3uni27F5uni27F6uni27F7uni27F8uni27F9uni27FAuni27FBuni27FCuni2800uni2801uni2802uni2803uni2804uni2805uni2806uni2807uni2808uni2809uni280Auni280Buni280Cuni280Duni280Euni280Funi2810uni2811uni2812uni2813uni2814uni2815uni2816uni2817uni2818uni2819uni281Auni281Buni281Cuni281Duni281Euni281Funi2820uni2821uni2822uni2823uni2824uni2825uni2826uni2827uni2828uni2829uni282Auni282Buni282Cuni282Duni282Euni282Funi2830uni2831uni2832uni2833uni2834uni2835uni2836uni2837uni2838uni2839uni283Auni283Buni283Cuni283Duni283Euni283Funi2840uni2841uni2842uni2843uni2844uni2845uni2846uni2847uni2848uni2849uni284Auni284Buni284Cuni284Duni284Euni284Funi2850uni2851uni2852uni2853uni2854uni2855uni2856uni2857uni2858uni2859uni285Auni285Buni285Cuni285Duni285Euni285Funi2860uni2861uni2862uni2863uni2864uni2865uni2866uni2867uni2868uni2869uni286Auni286Buni286Cuni286Duni286Euni286Funi2870uni2871uni2872uni2873uni2874uni2875uni2876uni2877uni2878uni2879uni287Auni287Buni287Cuni287Duni287Euni287Funi2880uni2881uni2882uni2883uni2884uni2885uni2886uni2887uni2888uni2889uni288Auni288Buni288Cuni288Duni288Euni288Funi2890uni2891uni2892uni2893uni2894uni2895uni2896uni2897uni2898uni2899uni289Auni289Buni289Cuni289Duni289Euni289Funi28A0uni28A1uni28A2uni28A3uni28A4uni28A5uni28A6uni28A7uni28A8uni28A9uni28AAuni28ABuni28ACuni28ADuni28AEuni28AFuni28B0uni28B1uni28B2uni28B3uni28B4uni28B5uni28B6uni28B7uni28B8uni28B9uni28BAuni28BBuni28BCuni28BDuni28BEuni28BFuni28C0uni28C1uni28C2uni28C3uni28C4uni28C5uni28C6uni28C7uni28C8uni28C9uni28CAuni28CBuni28CCuni28CDuni28CEuni28CFuni28D0uni28D1uni28D2uni28D3uni28D4uni28D5uni28D6uni28D7uni28D8uni28D9uni28DAuni28DBuni28DCuni28DDuni28DEuni28DFuni28E0uni28E1uni28E2uni28E3uni28E4uni28E5uni28E6uni28E7uni28E8uni28E9uni28EAuni28EBuni28ECuni28EDuni28EEuni28EFuni28F0uni28F1uni28F2uni28F3uni28F4uni28F5uni28F6uni28F7uni28F8uni28F9uni28FAuni28FBuni28FCuni28FDuni28FEuni28FFuni2A00uni2A01uni2A02uni2A03uni2A04uni2A05uni2A06uni2A1Duni2A3Funi2b00uni2b01uni2b02uni2b03uni2b04uni2b05uni2b06uni2b07uni2b08uni2b09uni2b0auni2b0buni2b0cuni2b0duni2b12uni2b13uni2b14uni2b15uni2b16uni2b17uni2b18uni2b19uni2b1buni2b1cuni2b1duni2b1euni2b1funi2b20uni2b21uni2b22uni2b23uni2b24uni2b25uni2b26uni2b27uni2b28uni2b29uni2b2auni2b2buni2b53uni2b54uni2e17 lowcircumflex colonmodifier shortequalsSaltillosaltillouniA900uniA901uniA902uniA903uniA904uniA905uniA906uniA907uniA908uniA909uniA90AuniA90BuniA90CuniA90DuniA90EuniA90FuniA910uniA911uniA912uniA913uniA914uniA915uniA916uniA917uniA918uniA919uniA91AuniA91BuniA91CuniA91DuniA91EuniA91FuniA920uniA921uniA922uniA923uniA924uniA925uniA926uniA927uniA928uniA929uniA92AuniA92BuniA92CuniA92DuniA92EuniA92FEngsamiffffiffllongs_tuniFB1DuniFB1EyodyodpatahhebrewayinaltonehebrewuniFB21uniFB22uniFB23uniFB24uniFB25uniFB26uniFB27uniFB28uniFB29shinshindothebrewshinsindothebrewshindageshshindothebrewshindageshsindothebrewalefpatahhebrewalefqamatshebrewalefdageshhebrewbetdageshhebrewgimeldageshhebrewdaletdageshhebrewhedageshhebrewvavdageshhebrewzayindageshhebrewtetdageshhebrewyoddageshhebrewfinalkafdageshhebrewkafdageshhebrewlameddageshhebrewmemdageshhebrewnundageshhebrewsamekhdageshhebrewpefinaldageshhebrewpedageshhebrewtsadidageshhebrewqofdageshhebrewreshdageshhebrewshindageshhebrewtavdageshhebrewvavholamhebrew betrafehebrew kafrafehebrew perafehebrewaleflamedhebrewuniFFFD @H]-./0abcdpqrsijklBCJKLMTU\]denovwFGGHHIJKUVXYbcefrsuv !"'((),--.      dgrekhebrlatn.ISM $LSM $NSM $SKS $ccmp ccmp&dlig.liga4locl: (082tP6",bqcr28RX ,>Pbt     .     / $(,28DHLRX&8Z| IGEC ca_] usqo JHFD db`^ vtrp SQOM TRPN   BHNTZ`flrx~     @\grekhebr latn*markmarkr~ $*06<BHNTZ`fl):D)10 "*.028>BGIJVWXcdestuV\bhntz 4"JJ*. %D**&f  F&\  -- ! "(.4:@F*FN (RX^djpv| $*06< 3631>36363>303,4^363636363.43,36363.3,4/3.3.3.3.3':##[[.L'#&-3(3u $=DDFFHHLMOORR XX!"bc#qr%' jk "%& !(((*+ &,28>DJPV\bhntz&/89,,YȷDarkRadiant-2.5.0/install/ui/fonts/FreeSans.ttf000066400000000000000000025633301321750546400213660ustar00rootroot000000000000000FFTMT GDEFb2? d8GPOS7 THhGSUBt j3rOS/2)s@VcmapTcvt 6i fpgm/f\egasp d(glyf s&$headO<6hheat$hmtx+!Rkern_loca8Li$Rmaxp55 name! ULpostf k,prep\nchRX_< Ȼ Ȼ r+Zr:du/]1 B0GNU @  8,`XX|c4,,!y40MIM&(H2WM.W,+,f,", ,,#,+,.,%,&nnH-H2H2,M"O0YZcZ ,SdO,PAKL &[ &]0cU c@,,M,*,6,,(,,FB:DAF,F,$,6,ME",A N+dNHKz,4,,C, d,+Mr%-H(M.M^H2^^M\,A0WM'^Em(.e=e=o,` 0ZZZZG L & & & & &H_ UUUU [cC,*,*,*,*,*,**,(,(,(,(),$,F,$,$,$,$,$H2c,A,A,A,A+6,*,*,+0000Y,Z,(Z,(Z,(Z,(Z,( ,, ,, ,, ,,S,F',_TdBO::,F@,PB,PoD,PD,L,FL,FL,F.l,F &,$ &,$ &,$+$]ME]ME]M10"0"0"0"ccc U,AU,AU,AU,AU,AU,A  ccc,V0P,6T00?0O,64&Z/0c", yFBYO:Y0A@,F &&5$&$J0,6[0"cMccUpA U ,cc c  ",",@,6drdH2|YY  P PDLL F,* &,$U,AU,AU,AU,AU,A,(,*,* * ,, ,,O: &,$ &,$c 5YY  ,,v[L,F,* * c,,*Z,Z,({| &, &,$]M]M-U,U,A0"c S,Fc,*Z,( &,$ &,$ &,$ &,$ ,*2;,6 `Z,(,($("$"!T,,, ,F,F,F BDjDAFAFAFRTF7F,$ $.M M t MEMETs"6"6"+*5Tr"B , 5(4,/,/,/0 &F"o.F&&FZ,/,/ DUD-:wM M &g(J11WWWMMOMMMMM3M3MMMMM\MdMMSM#MgMgMzM{M M M M MMsM=M9MMMM:MMN(((((HHMMM1M3M.MMG:M nM M M=&1=)2,+5w XU}>e9CC!2h:Qk|;nn1LnMsMV<qAM OFR 5ZtS &dOAKLvA &P[t(c 4 4 T0'$382T024$'&$ (8<'< "$k92#"l"2.; *$2"$2$D64M l49$,,69 &H0PPFP00d8SStPPe SPPFP,"P 0PPxP!NPP.PP0ce ]0Pr8>PSPIjPP*P(&&&FFZ#&$7F7FFjF.F&&-FAF&$a&BF0FFF F$F%&%&AF&$F-UFAF7F.F<* 0&& PGF P<FYPFM  OPF >*"PFPF GP:FPF"P(F*0&0&c B L ~0 0v<0vPF/$/$P PF  P.FP;Fv<0ePwFP(&(& o"P#&/ /  0$c (P7FP7F.&& 0&& 0&&*$e e e r80FPFjPF0" &?$sPFFF<<F2FFFAFFTF#FI2A(<S- [(FBA-FFA-Fo<F-x-(MO5MMM MAA'F<#9A"AE#aF'A8F9#(FFF#)FF#8A(,AF#'A#AA& A!AHAoA(A(#-FtAB,#* FZM  fn @&y>Fn:O:V&n?>_'o?`=:&&9X%o?m0>z3_=K$?98? N3^>#%<#><8,X7X8423,jjjrTrW>>>#9>kkmIdsXakk>>~Ikc,}  >1?<F$X ;E X  Nl8c`4*Q(88B, 2T|L\3^sG,  c,rqxqvs?Z?q?X?d?N?e?e?"??mj.,8?Xc,"=4/XtD#s#e:+ Y3?- R5w@'/6J/d )RtAM191`$yy(! m+ew_0\@eYZzuzvqpelrf]B-" !|!nn@M <R8 .b obYfz #hYs lN;l?l%5 <, &-r)t.O_) aef K<l<</<X<S<<}Pn<U<<Z<[PPK<o<YP<O<W<,<[<NP<T<P<TPx<Q<tKoxv<N55\5y* $5}5 5O5Gg 32 5.p5  #B\5x]J J x$2r553n  , n q#4275Fi555'[ %$m\$J Y2522222222222222222r2r2r2r22r2r2r2r222[22I2I222222!2222222*22222222222222222222r22r2r22r22222L2222222222222222222222222222W2W2W2W2P2P2P2P2P2b22P2`2222222222222222i2i22222P2P2P2P2P2b22P2`2`222`22`2a22s222a22q22i2i2P2P2P2P2P2b22P2`2P222P22P2P22b222P22`22Z22Z2K222222222222d2<2<22<222222222222222d2<2<2d2<2d22<22<2<22<222p22 22 222p222l222l22l2l22}222l22|2252m2522;2;2[222h2h2>2>2>2V2V2V2V222222222222222R2222222|222222222222222222t2222>2>2>2>2>2>2,2,2,2,2,2,222W2j2p2121212128282828222222222222222P222222922222 2 2 2 2 2 2 22P22k2k2k2 22k2k22P2P2P2P22222 2 2 222 2 2 222222222222222222~2~2~2~2~2~2~2222222P2P2P2P2222212121212v2v2v2v2222222222222222222222222>2>2>2>2>2>2>2>2>2>2>2>2>2>2>2>2>2>22222222222222222222 2P2P22222222222>2>22222222>2>2>2>2>222>2>2>2>2>2>22222>2>2>2>2>2>2a2a22222222222>2>22222>2>2>2>2>2>222>2>2>2>2>2>2>2>22222>2>22222 2>2>22222>2>2222222222262r2k2 2$2"222"2222 2 222 2 222222r22222P22P22222>2>2222,282|282|2282282222222 22i2i2Z22622?2?2222,!,22Z22222P2(22v22222222?2c2,*O,6O,6O,60Y,Y,Y,Y,Y,Z,(Z,(Z,(Z,(Z,(cZ ,,S,FS,FS,F,S,FO:O:O:,P:,P,P,PAKAFAKAFAKAFL,FL,FL,FL,F &,$ &,$ &,$ &,$[,6[,6]ME]M;]M;]M0"0"0"0"0"ccccU,AU,AU,AU,AU,A   ccc,F+*,*,*,*,,*,*,*,*,*,*,*,*Z,(Z,(Z,(Z,(,Z,(Z,(Z,(SN_9 &,$ &,$ &,$ &, &,$ &,$ &,$&5$&5$&5$&5$&5$U,AU,AUpAUpAUpAUpAUpA    T0T0T0T0T0T0T0T0''''''rco$$$$$$$$ UU,8BA"$"$"$"$"$"$w22222222_$$$$$$$$oT0T0''$$8"$"$22$$T0T0T0T0T0T0T0T0><U\C;$$$$$$$$PUKN$$$$$$$$jh5&)T0T0T0T0T0T0T0@MtMtM M $$$$$AkSRBM  xDQM 2222929222  1;""M $$$$$AAP.4MRMvM,dM.M.,,8K6kA@A@M0M1M/M1,&,&^2^FWstW P 111111x 11nb,|,M,;;#H 5NNyNXMBMB|,.0 (?;(z<;$<W1<<<<\<<<^*^^^^^^^^ ^ ^o^Nh-^^E^^^^^^^^^^ ^ ^o^LknhE ^g&Y c,,AFG%[]o2e<6$c:"'Z[! 6wxx)2142wrCP)0Bf)[)0,30@00+(!g.,<V):^)L<rX4[ &R]B]]"c 4 O"o,(2-)L)8:P:V&Cd$:0kFRP ,,P,P $eEeeEeeeeEeeEeeeeEd,dBddd,P0YAKBBBB    BD,AF [-1[-    0-  |8# -|8{  ['-[,['D ,+Z7  H7H7H-H7H7-7i7i$)H(H2H2(H]HK 8KT[[((,ddd,__HKHK=K%H3H*H(H(H2H2H2HIHI*H2H2H(H(H-H---*H-H-H-H-HHH-H-H(H(H-H-H7H7H7H7HH6HH6(|4|P|B|B|E|E+++++LXL`L|>|>|>|>==W@>H(H(|B|B7'bH_I,,d;;II(((,,XXX77, ? 3 3 G = G 5 =         O Y Y Y Y ? G Q ?  D [      Y, k##########|||||||k 2 2 2 2 2 2 2 2 2 2qq d d  |#|#|^|^{^{]|#|#|^|^{^{]$$$ ' ' ' ' ' ' ' ' ' ' ' ' ' ^ 2 2 2 '  ' ' '^2 2 2 2 2 2|#|#|#r 2 2 2 2 ' ' ' '        Br"#S"#r"#S"#M7+777By<2"'''==H(`(h8h8HHH 000HHT6HH>HHh8h8$H0<H 4H6X:0x<D6 HHZD6&?|6h8h7H <8h8h786H8H0H<<6H<6HM.FFF F FRFFFFFFFFF F F:F:F F FFFXFXFXFXFFFFF8FzFeFFFF FHF FFHFFFFFF FFeFFFFFFFF\F6F#FSFSFFLFFFFFdFdFHFF FFFFFwFwFF1FFwF8FzFF FFF FFFFF|FFF@F@FFF@FFFFFFSFzFFwF/FFPFFFFFFyFyF-FBFqFwFPFFFFFwFwFFFF{F FIFFF F FFiF/FRFFBFF F4F4F(F`FF(FeFFLFLF,F,F,FFFF`FvFFFFFFFFFFFFFF FFFSFFlFFFFYF FFwFFFFFFFFFXFXF>FF>FFF\FFFF4FF%F^FFFF-FFFFyFFFFFFFFFOFFlFlFFFFFF-F-FFFF!FFF\FjFFF#F#F#F#FFFFFFFFFFlFzF&F9FF FFnF,FFCFF+FFfFFFFFFFF0FFFFFFFSFFFFFwF0FMVV222{2;,,^LL)&NS'pA/iM' 24 G&-!# T 5 0MK/:w#2"5xFxY|O   } +8:8:r+++++KG]q!:!VwX X  ,1`?n?++!-Z-PP@@  nmq  Dh~3   gHk[[+yy+.VX_HjjezSCO O/Hhh0%%   .F:+O} ?--  +pq+t+ rq    ? q  x _CoJ$3* CGt:+ O3} ?- Hy+ yyWaa n2FHcq8|ULL' a\vuo?]c')B'iN)g'>>|)C|2bxjjrtJ**jUU  5]Jk5])u# = '!|||LLLU,LD[8c?z]s*U QY)*; b(.&bNE9EoE8c(EE9E3E[b& A  r H uK;,(v,/./6h[Q*^KXh[[u2 +*)L\33\ ,G:<01,T$q0`.^)V2[/X1*K*Kb obYfz #hYs lN;l?p% a T 1 @ > [ B g RQ1rr{uojmugZ hZ]Cl a b _ aL AAA AA:18K%z:P&h? 9R%i?%#H2<<<<:::O:V&n? _ `=&&9X%m0z3_=?98?N3^>#%<#>O:&98?H  a?\J.d>-a|,dqV 2C#I C oM}#pn_C1a\<K9I&776OF09057^1F?:9Y=&259\FV9#OHI+N8d03DN/0r7HJ1<n6A8L.`O N7777777H67777&77@7u77M7A7R777 77777b77,D, D~37ouz~asV_ ,J 9 N U r    ( 0 3 6 9 < B H M Q \ ^ u EMWY[]} d q ! !$!3!"I"M"P"b"e"k"s"}"""""""""## ###*##$$$i%K%l%&,&g&o'@** -e-o.+6<>ADO $7Ptz~p|1Ya0 < P X y     * 2 5 8 < > G K Q Y ^ f  HPY[]_ p t !! !&!5!>!A!J!M!S!!!!!!!!""""""" "#"4"<"@"M"P"`"d"j"m"z"""""""""### #)##$$`%%P%&,&`&i'@** -0-o.8>@CFsqgYQO93" $"! @4uig]WOLE#"!  {vsgfjh X ~b~$377Potuzz~~0\ceagps|IM1VSY_ya ,0J 9$ < N^ P Uq X rw y                       ( * 0 2 3 5 6 8 9 < <" > B# G H( K M* Q Q- Y \. ^ ^2 f u3 C F O R h o q v      5      E HM 2PW 8YY @[[ A]] B_} C b      d p q > t @ [ ` n t u !! ! !$ !&!3 !5!< !>!? !A!D !J!K !M!N !S! !! !! !! !! !! !! !! !! """ #"" $"" &"" /"" 8"" :" " <"#". ="4"4 I"<"> J"@"I M"M"M W"P"P X"`"b Y"d"e \"j"k ^"m"s `"z"} g"" k"" u"" v"" "" "" "" "" "" ## ## # ## #)#* ## ## $$$ $`$i %%K %P%l)%%F&,&,&`&g&i&o'@'@*** * -0-e-o-o..+A68<>>@ACDFO1Vd       !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a rdei xpk vj Y ;s \ ]gw $ 1 /M El|6cn 9T U 'm} b :  k   y f|qxyzz}{g,K*PXJvY#?+X=YK*PX}Y ԰.-, ڰ +-,KRXE#Y!-,i @PX!@Y-,+X!#!zXYKRXXY#!+XFvYXYYY-, \Z-,"PX \\Y-,$PX@\\Y-, 9/- , }+XY %I# &JPXea PX8!!Ya RX8!!YY- ,+X!!Y- , Ұ +- , /+\X G#Faj X db8!!Y!Y- , 9/ GFa# #JPX#RX@8!Y#PX@e8!YY-,+X=!! ֊KRX #I UX8!!Y!!YY-,# /+\X# XKS!YX&I## I#a8!!!!Y!!!!!Y-, ڰ+-, Ұ+-, /+\X G#Faj G#F#aj` X db8!!Y!!Y-, %Jd# PX<Y-,@@BBKcKc UX RX#b #Bb #BY @RX CcB CcB ce!Y!!Y-,Cc#Cc#-KPXYF+X!YKRX!Y+\X E+D E+D E+Fv+DY+ R]]((((T$ 8tdLXl8 P H L | , ` |8t4Xt$x <4$xpPTp$88x0Pl,tt  , | !!!t""""""##,#D#$0$H$`$x$$$$$%@%X%p%%%%&&x&&&&&'4''''(((0())))))**(*@***+ +$+<+T++,,,,D,\,t,,,--(-@-.P.h......//(/@//0 0$0<0T0l001x111112 2$2<2T2l2223<3T3l33334 4X4p44444555L5d5|555556 6@6p666667777788 888P8h89|99999: :$:<:T:l;8;<< <== =8=l===>>>4>L>d>|>>???????@@(@@@X@p@@A AABBdBBCHCCD DtDDEE EEF<FGGGGH@HHI$IIJJ`JK8KLLLMDMN<NpNO,OpOPPXPPQ$Q|QRRPRS SXST4TTUU U\UlUUUUUUVV,VDV\VtVVVVVWW$W@W`WWWWXXX4XPXpXXXY8YYYZZ Z[ [$[<[T[l[[[[[[\`\x\\\\\]],]D]\]t]]]]]^^^4^L^d^|^^^^^_ _$_<_T_l____`L```a<aaab$bbbcc,cDc\ctccccddpddeeefdffglgghiiDij`jkkkllllmmtmmnLn`no<ooppppqXqhq|qqr,rxrrs(s<st tdtuu0utuvDvvvvvwDwwxxy,y@yyyz4zz{({<{X{| |||}~<~D|,HDXl(8Px X8\p(<PpX4L`t Hh$8L`|<\$8L`8Th|$8XDp88L`tL,@Tl0H||4H\0L` 4H\p (\l $4D\thx8H|h,D\td,Ll|4<X0H`x \0H`4DTl|LhxDH D(\ ` lH\8(8\l<pp td| |$<Tl@P` ,Ld|8x0È,Ĉ8hŘ8ƌx@tȼ@ɀɼ(\ʐt ̤$͘H΄θ4lϤdФ Ѭ@ӄӠӸ$t\՘D|ָ 8Ph״l؄؜4Ld|ٔ٬ټ,D\l|ڔڬ $<Tlۄۜܤ hDވ8ߤTT<( |\h,(pL0T@8Hp$Hd h` p$lP$`dH0|0x , X|,D\x0HdD,`@ (X0(t@h4d        0   P   0 l   4$hPD,dTxP$P(X, 4|8T $  !!"\"t#`$$%&@&'''(()*,**+p+,D,-,-..\.t./,/00T011,12D2\23<344455<5560667X77788d889(9D9d:l:::;$;l;;;;;;<<,<=>?\?x??@,@A ABBBC<CDD D8D`DEEEFXFG0GH HI IJKLLLM\MNO0PPQQQRSSTlTUV\VWTWXXY$YpYZ@Z[4[\\x\]H]^P^_(__`0`h`a,aabb@bcc$c@c\cdLdehefgh`hxhi@ij,jkXkltmmmnpnno(oppHpq qqrrsdttuTuluuuvlvw wwx|yypzzX{{||}$}~0~xH8Xlx,X|PX(X4 t H<LX,|xx H0H`@00d|p@PPLd t$l l$L8\4(DX<l<X$D |4 ÄT\ŰxxȄ0ɔʨ`̄pdlЄ$,0Ӵ0ՠ,xHذ p ژڬۈ۬@ܴh0ބ޼(ߘH<P<00dh8dPdx0Tl,p\<D|L $HT$8Pp,D\t4Tt(Hd0H`4Lt ,D\,D\|@\$<x0H`x(@Xx   8 P     ` x     ( @ X p        0 H h     8 P    4Ll @`x @`(@`x0Hh(Hh$x$<4Ld|D,h $<Tl4T$<l$<Tl ,Ld|l`   x  !h!!"","D"\"t"""""##,#D#\#t#$$0$X$p$$$$% %$%<%T%l%%%%%& &$&<&T&l&&&''<'x''( (L(d((()0)H)))**4*d**+ +8++,,,<,\,--t-.4.L.d../@/X/x//0 0$0<0T01,112L223 3`3x334,4\4l444445 5,5D55606677h7778 8$8D8\8|899:`:;<;;<@<<=0=H====>,>`>>>??4?L??@@4@h@@@AADAAB BhBBBCC`CCD,D`DDDDE,E`EEEEF(FXFFFGPGGH HLHIITIIJJ J8JxJKKLKdK|KLLPLLLM M,MMNHNOLOPPtPQTQRLRS,ST,TU8UXUhUxUUUUVVpVW8WPWX(XYYZZZ[h[\H\]D]^(^__x_`t`a8aaabbXbccdc|ccdDdde e$e|efDfffg<ghh|hilij0jjjk k`kkklll4lLldlmTmnLndnohopqqrTrst tuXvvw(wx,xyhyzhz{`{|}}p}}}}~~~4~L~d~|~~~~~D|4xH4Ltt8pH<h`(X|$xl,Xd4($\t4Ld|\(H`(@Xp $<Tl $<Tl,D\t4Ll<Tl,D\t,D\t4Ld|4Ld| $<Tl,D\t4Ld| $D\t4Tl,Ld $<Tl4Tl $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld|”¬ $<TlÄÜô,D\lĤĸ(@XpňŠŸ0H`xƐƨ 8Phǀǘǰ(@XtȌȼ $<TlɄɜɴ ,Hdtʄʘʼ,D\tˌ˸8H`̀̐̐̐̐̐̐̐̐̐\`΄μ,dϰШ(DtѤ (҈0`Ә 4hԸDtֈ֨,Hpנ$8l4`Htڐڨ(<Pdxی۠۴,@Th|ܐ0ݨތߌd| $D,<lD(@l $HDXt,<(p(Lt$dt0xPd<   8 L         0 P h       ( @ ` x       ( H X h x      0Xp0dD(D$Lt4L\,tHp$x@d4ttt@Tx,L<L  (   !(!@!"D"\###$$ $t$$%%(%&,&t&',''((,(H(d()$)d)x)*,*D*\*+8++++,,,,@,,-T-..,.@.h.../ /4/P/l///00,0X01112@2223343h3334 404d4444455$5D5X5l555566<6d666677<7\7|77788,8T8|88899D9d9999::4:T:|:;;$;<4= =>,>?d@4@AAAABB@BpBBBC C<CXCxCCCCDD,DHDdDDDDDDEE0EXEEEFF,FTFhF|FFFFFFGG,GHGdGGGGGH H(HDH`H|HHHII(IXItIIIIJJ,J\JpJJJKKKHKtKKKLLL8LTLhL|LLLM8MLM`MtMMMMNNN`NOOO4OLOhOOOOOPP,PHPdPPPPRVYY(Y<YXYlYYYYZZZ0ZLZhZZ[[`[|\H\\^^0^T^p^^^^__8_d_x____``,`T`h`|````aa$a@aTataabPbbd8deXeeeeffdfffggggghh,hHh|hhhhhi<ihi|iiij(jPjxjk k(k<kXkkkll,lHldllllmnoop pqhqqrs,sxst,ttu0yyzz z8zz{{T{{|4|p||}}4}`}~~P~~0<h <@@p@lDHtd$,Px P<(`<xHTp(pH4$p4\HlX|dLDl44TP@X,XtL|$tLDlHx,XØTĔŌH4lthht,| `@ψ(pѰdӠ8\ք\؀<ڀTܸ8pެ߄Tl@DDP$lt48lL`HxDLHd H8t\Xp   `    (  \HLl,0 LHH$\Dd@ @ !<!"t#$#####$$$$L$$$%%,%L%l&&`&'0''(8(()T)*`**+`+, ,--..//T0 0012X23L334t45D556x67X78,8H89H9:(:;8;<<4<===>4>>?@`AABC$DDEFGTGHIPJJK0KLM8MNO(OPQ8RRS`T0TU|V(W8XXZZ[\]^_`abLc$cdefgh<ijjkl8lmnpXrsttv wwxylz|{|}`~<x  0H<xx P( 8 xD@Dh\pLhh\t xT$\4TœØ<, $\,ͼx\ЈL8d@0ܼ݌ޠߘd@4x`,D tHDX,@p<8 D\\ ht P  <  t  \8h(|( p<p !"#$&'@(()*+,-D.4//011234578899:;<=d>P?(@BBCtDEFGI JDKMMNOHP8QDR8SU4VXxYZ[@[\]$]^_d`D`apabcLcdeXefg|hhiijjjkklmm|nnoop pq<qr@rsttuuv@vw wxxDxxyLyyz\z{|}4}~X$|8LT \t(D<0`4$8`pT``T\\ `dl$LPxhTL$Dl00¤4àXŘ08|Șxɸ,`ʰˌ@̈ d$ΐ4pPӀԔTՠ <x,4ڰXPܤ<ݰXެ$ߜh<`8,4d4L|4L@LhXd||<,`,tLXd xD ( <D   8  p (  T`p@(0pl(4| l !!""# #$d%%D%d& &'x(T()t)*+++,<,,--h--.L../L/h//0x0120234X456L67889X9:x;;<,<=h=>??@,@A\ABBCDDDEdEF FlFG(GGH@HHIItIJJJKPK|LxLMNOOOPP P8PPPhPPPPPPQQ(Q@QXQpQQQQQQRR(S STTTTUU U@V,VDVW4WWXPXYYLYZZZ[\|],]^^^^_,___`,`x``a,aPahaaaaabb b8bPbhbbbbbbcc(c@cXcpcccccddd0dHd`de eHeef4fg$gghhhihij8jjk kxkl@lhllm mnpnnoloppq8qr4rrsDst@tuuv0vvwtxxhxyXyz z|z{t||`|}d}~ ~XpTdh8x4,(< Td|8$X(8 0d$p8d,8(hL4$Xd3!%!!X Gf'HI| #'#5'STRhh41 3#'73#'4]']'ooo3#3##7##7#537#537337#3$]jiu'L&|'L&erly$L$}#0| }DDDD!.4;%#5.=3&'&546753#.'%5>54&'oi;emO $]!Ge`;[gO@243"81ZzJ716[ $02"&46"32654&%3#2#"&46"32654&HcdedF)<<*)<;oBuBHceEGddF)<<*)<;dIEdeeF;*)<;)+;^'VdHEdedF;)*;;(+;4}*3%#"&5467.54626=3#>54&#"'322 ;D\qE]/"d\?2Eljp&;Y$]]$Y;&2 ##5#5353FF FFWmh 73#5>=#Wii#32#"& 3265+.@;!wqrvvJFWYM0ܐf[#5>73#f>:X? .R;"632!!>?654&#"2e~dA8u(Ro\`P< t\R6#B-Wu?47U9L $"#632#"'332654##5>54&L9Xesc@8mXHHEN% _OCxSEh[l'ODexMJKCK,A8A %!533##5GBAiiXު]4O6#!!632#".'332654&#"#;Hfk)E-%XrHSTG):Q5W+ip)/wXLO[%+%4>32#.#"632#""32654&+"3G?#Qk X >.LQ:i_zeARR>=PJC^R3bW38O~chSBD[WCGS. #67!5x^(|JϪ W%("&547.5462&"32654"32654z҅y6*ww*spCC87D9RQACSu:waz{av:"@1SjjS2@=32=<24M|NM?>&##"&'33267"&54632"2654&"3G@#Qk X >.LQBze=PJSRk^R3bW38O~chMXCGSTAE[n 7#5#5hhhhhhhhnm #53#5>=#hii#< hh\x&&4-75% -vEOO2oa!5!5aFFFF25-5v EOOM#54>54&#"#4632#5%45%Z%54%F:I8UolatZ%.J1-9 /7%B00=#3?RJswhhh"r5A332654&#"327#".5467>32#"'#"&546322654&#"SZ @hя؟Orpr|yTJBYVLn`BLF]eT&A[6(?b:#X}ꝏ C*kmbC54&+ova(=Z-eF?}/1>D?^r$;;p/T+49E04>32#.#"32673!".00GyL+_^Wj{mZ``!KwG/d2bjO4NNej3Ni`Y3!2#'32654&+YlpplRZe !!!!!dLRRRZC #!!!]t\LRR,$326=#5!#'#"&54>32#.#"$9^]&#&=4&+#!2'4&+326 +q:>]Pks6+LCJEh&0* ")8.IAC?e^>RH6<0m0%2>54/&54632#.#"#".'3V8N%x~X^WJY6BJQ(>gA)KN8%X+U;*)S#0%cswkGMD7*01`C9=. #7\<64Q##5!b]<yRRU3#"&533265(]]eUXdnoOOTJ!#33ddci !# #333ffhdhWPP # #33sqqov0vc #33]socG !!5!5ELfTRR5R@,#3#ggHH#/7,53#53ggHHS,I3# #IEzyE,PB!5B~22P#'`<䔔*)4632327#"&'#"&54>7>=4&#"26=Aad- #()T^O] -<$1&=8:<SEXeJ<6qIF)? %(MTG& &(-0I*^  -P,6 3632#"'#"32654&6S9ihx|hk;KBPPBDUSVZCoooY^p#.#"3273#"&54632T@4DMNEkTp^k}j[m\7;ob]j~`kb#5#"&54632"32654&J"O6jz{fl5DTTEBPP'E1+}Qp[\on[^o(%!3273#"&632!654&#"~+Sf#TvZnmJq&T=?RH(DiV`J@7,AYW###5354632&#"WSFFA: : D8DY8?E5R&("&5463253#"&'3326="32654&\|{db>M0_I\nUqT>#K!CNNDBLK}[LJKeV*TJX_a2,n^_mk^bmF4&#"#3>32#C)@NSS%K8LYSk70aP0'MBtB#7#5SST  ii& 3#"'53265#5FS  $SS mG'%ii: 73##kgQSU޵PD#T'F!3>32>32#4&#"#4&#"#FM"K4a,%H4IOT3.3HT3.3HT J/*P,$KGwi27P9i27P9F3>32#4&#"#FM#Q:LZS:2@NT X70NAtk/8aP$ 2#"&6"32654&q}np}~oFPPFEQOMmmm\an6& 3632#"'"32654&6M;lhy{ea@BPPBDTSO^}NoooY^p&##"&5463253"32654&S9ihx|hk32ATTTM$C( Jg _;3"$732654&/.5462#&#"#"'z5E7C&+N]InbXf3<.3PNFuc/7-%" C>IWTMT+$# E:LZ#327#"&5#5353V'14GGS DF+(DA !#5#"&5332653K%P:L[S:2ANSI4,NA/8aP)  !#33[^^ W !# #333*_je^\kdfgh^e hh #'#373$a__~~^ & 3#"'5326?3Z)^ Y l KSh !!5!5EV9 JIKxI+,!#";#"&=4&'5>=463%$.R%.49",,"94A'EF&x'AE>E< E ;F>Ed,3#d<<S, 5326=47&=4&+532#%RR%/4:!++!:4A'x%&x'AE>E< E EK "#>3232653#"/&/571u5i'*bsNFKE%&?z3 J 4t673#5.546753#.8`TjW*euue*XcT9XEg tZmab wz ZYcV254'#53.54632#&#"&3C;?!h(+*9J+#580E8nQ"e5>,X@M ,> 7+'RD'B8.%B;V.&(791![{ 1W9FC'&%'#"''7&547'76327'"32654&:5(<5)380 687,5:(;97,>?,+?>65391+;9-7575:7)42/>V==*,> !#3##5#535#5333着XUUb3M33M3cBd,3#3#d<<<<oo++4?32654/.5467&5462#54&#"#".57654/=)+571+27,kaT5,&3 =0mkP.4'oII' E50&2&u"J12G33E`bO/70%"^'J6s)"4 H`*J0{'?=3m-<(d4&yYy%0#"&54632#.#"327 #"&54$ 32654(!@(VlmX#G -(9DE8V<ᢚ8@$fi2+]OHb|ߞ✞%/M#-!5%#"'#"&5467>=4#"#63232'3265@"/1808DYC";~zTS.9%6b33_,++4,11 , l^z /(-j 757757-jj5jjYzSSSSyYzSSSS(V w!#5!(FNw.8#58HH#.9#&54574&+#32'3254&+& #"&54$ 32654O"F΍K#(=<ᢚh/ -#%($ ~"*C# ߞ✞w.!5.FF 2#"&46"32654/>YY@=YYfN87&(8Y>@XZ|Y98&'87('2o ##5#5353!5FFFFFFF"#6323!>?654&$>@R`?! 3H:;1 F8R/ #:IL& / +B%"#632#"&5332654&'춮&.#?@K9GUFHO>,-*0 3@.)-,@8;L=IJD.++&'3# $\P-3#q<A$  %#"'#"'#33265332 !S;h6$SS:1ANS-1? SS/8aP)L)0O .546;####an^9@R@^e@JWKy'*32654#"'73632#"'< ! . *& &)B9/N%\8# +2"E #57>73#c6$ +>D.0W(/D!52"&546"2654&:DJKKKC'--N-,b33ZTOZZRQZ4@78@?79@.jm =Q #57>73#3#%#533##=c6$ +>:<:.CC>D.0W'x; 5d=L *#57>73#3#"#6323!>?654&c6$ +>:<:$>@R`?! 3H:;1D.0W' F8R/ #:IL& / +D%)47"#632#"&5332654&'춮&%3#%#533##=.#?@K9GUFHO>,-*0 3@.):<:.CC>-,@8;L=IJD.++&'3# $5'x; 5d`' "] #k$#j$#f$w#|$q#j$#z$ %!#!!!!!!#Ug+n`dRRR(_0*7%2673!632#"'732654#"'7.'.54>32#.#"zZ_`! &)B90M. 8Xy܁!4@r!q3=HFYkbR"6$ f2zK*"ksD*"jsD*"fpD*"|qD*"jp D*"zlD*U/;B%3273#"'&5#"&54>=4#"#632>3226=%!4&#"01e%TvY88e;L\*FGf$vp Tʀ/\6IpFZgG86&P@?R4K%iUak:4TG&9$ N]R&,J@7pQe  ,('-GYW*5.54632#.#"3273#632#"'732654#"'5N&j[mT@4DMNEkTt\ %*B9/N.  8=P,b]7;oc]j^m!# +2!)%("kvH("jxH("f{H("ju H"k)"j"f "j $*4>32&''7&'77"&"32654&$-<8'';a$U4,%Ja(Z*F+"||FPPFEQO@e<' (1-'(,/-%)#MLs>Rmmm\anF"|tQ$"kmR$"jnR$"fnR$"|nR$"jm R2 !5#5#5&hhhFFhhphh##"''7&54632732654&#"?,}og<= B0~oeA:\+?FP)EFPGIm>EJGsAA0m_B 4m_DA"klXA"jlXA"foXA"jh X&"jQ\6& 3632#"'"32654&6S8ifz{ea@BPPBDTSP}NoooY^p&"jV \f#i$*"inD#x$*"xnD3"&5467#'!#3#327 h8L3. KMcx7'H%0px4*$:',6 - L+3T4?632327327#".5467&'#"&54>763>=4#"26=Cad- LH&*:1%9QaO]%(@5)1%u:;QEXeJ<6qIF)?=7 - ,!( CMTG"3" N-0I*^  -P,0#j&"jnF0#f&"fcF0#y&"y\ F0#g&"ggFY#g'#qG #53!2#!326&+3YEE]kppkSCCSC-!3##5#"&546325#5353"32654&??JFbf|{ea@SDTTDBPO5;R|N5LnnlY\mZef#i(("isHZe#x(("xrHZe#y(("yq HZ3"&5467!!!!!!#327@8L3.=ds7'H%04*$:RRR,6 - (4.6%327#"&54>?#"&632!32?54&#"&)(&"&)?L  (pmJq~01e%T=?R)B,(0 - 0( J@6b4K%iAYWIZe#g(("g|H,cf @7 *&"fuJ,#x*&"xaJ,#y*&"yX J,'R*&#JS#f+F#f2K'!5%53!533##!##5w,]w],,]^,ZZHFLF4&#"##53533#>32#C)@NS??S%K8LYSk70aPX5LL50'MBt0i#|,"|f#i,"i*#x,"x3"&5467#33278L3.^7(H%04*$:',6 - 3"&54673#327#5r8L-(S 0#H%0T4*!8 *6 - ii_s#y,T #S  d#-,B&;#ML #f-&&fO'4.:'N: 73 #%#k0gS  F#j/@#jOP' /B'OOP'\/DP'\OP#y/D#yO( 7!!573{(PP]yLzR9M8u 7#573BBS??S3@392@2`L#j1F"jzQL'>1F'QL#g1F"gtQ._&\Qxl$".#5232654.#"#3632(B?l)@>Dm]]:)VR5-LVGNt;dMM\%HRtc0F&#"'532654&#"#3>32 &:2@NTM#Q:LZmG'/8aP X70N&f#i2$"ikR&#x2$"xlR&#}/2$#}R+-4>325!!!!!!5#".7327&#"+(=RM'y>HUNCu(MR<'])94};;|59'dXT6XLRRRJ^5QZIn?' xex'@n$*1%3273#"'#"&54632>32$"32654!4&#"01f#TvY>?q||q=e=Jq،PPFEQX&P@?R4K%iV`ggj28J@7pm^`ml^a3GYW]#j5EK"jU]'H5EA'SU]#g51P"gU0m#j6""jWV0m#f6""fVV0*mJ%2>54/&54632#.#"632#"'732654#"'7&#'.=3V8N%x~X^WJY6BJQ;wT&)B9/N.  ,YgX+U;*)S#0%cswkGMD7*01`C(ED+ # +2"(%E[64"*@732654&/.5462#&#"632#"'732654#"'7&'z5E7C&+N]InbXf3<.3PNF4.&O&)B9/N. /7-%" C>IWTMT+$# E:/N # +2"(%G0m#g6""gTV+Q"32654#"'7##5!##632#"' ! . * < &)B9/N%[RRy7# +2"%/32654#"'7.5#53533#327632#"'B ! . &(*GGSVV &)B9/N%S*$DDF/# +2"Q#g7#^WQ535#5!#3##x<]eHRRHe #3#327#"&=#535#5353VOO'14LLGGS DHF+(HDUi#|8A"|pXUf#i8A"ilXU#x8A"xlXU#z8A"zjXU#} 8A#}XU3#"&5467#"&53326533279L"'9,]eUXd]%44%H%04)/ pOOTJ4W9046 - A4  $327#"&547#5#"&533265#*$'>J`%P:L[S:2AN ; -2+>1I4,NA/8aP)#f8:#fZ #f<&"fV\ q#j<G#j="jZ]Gq#y="yP ]G#g="gX]3##5354632&#"SFFA: :DY8?E5-!533#632#"'##5"3264&YS@ae{|fbFJ?$COPBDTTLL5N|R;X5m\Ylnn0* 0%2>54&+4+3%"5463!2#!#"3263J1>D?F{=G~=Z-eF?vaR1 R+49EM!1N$;;p/TB^r:OPpx6  "32654&!!632#"'BPPBDUS9ihx|hk;oooY^p3IVZC732654&+332#!'@RQA]]dƀRR=@PhxfcË3 "32654&3632#"'#'CBPPBDUSS9ihx|hk;K]oooY^p VZCË0 &0(4>32632&#"#.#"32673!".00GyLHP _^Wj{mZ``!KwG/d2bjO4O;ORNNej3Ni`.,"632&#"#.#"3273#"&54632R T@4DMNEkTp^k}jT>ER7;ob]j~`k0L!%32654&+"5463!2#!#"3263glppl{=GoR1 Rǚ1N:OOo754!35!5!!".6;#"Ot=Y-] 8''8R'+EF:.)-,6  "3264&#5#"&546325!5!&FSUDBPPK;kh|xhi9p^Yooo3CZVI&;$ A6Ze !5!5!5!!5!sdLRR'R/'0m/".547&54632#.#";#"32>=3MQv<ix~X^WJY_?XlC`%N8;U+X6Y\+GG&}1-lcswkGMD7.[RT.)+47Ji5&C!!!!#"'532Zt\QH DL%RRG58Q&##"'53265#5354632&#"W &FFA: : DmG'DY8?E5R,.326=#5!#'#"&54>32632&#"#.#"$9^[cEgI>K57FA+#"&54&#"#3>3232>54&'7/714C)@NSS%K8LYI-F&5Hf1?';d+(/70aP0'MB/#1=+.t3&}; Pv:34632&#"73#:A: :kgQe8?E5޵PS #5333##||T}}TSCCC,"273267#".' #'527.#76*:o0!! z`%6 !$5_LMFb\tMbR @ !#5#"'#"&533265332653S"K4a,%H4JT_/-3C_[3C_J/*P,$LFV27O: iO: &33##"'532Le}XiMG DL%T'OD58LF83>32#4&#"#FM#Q:LZS:2@NT X70NA3/8aP& #"&632.#"!26^pm ԌaVԐ}|Fu& ""2654&#"&63232>54'7t菎>lB @%k=򥤄΢V$$ 6+8]a$5w !"32654&7#"&63232654'FPPFEQO%\%np}~n8R mmm\an+8VEg# & #"2654&#"&632632#4&#"t菎qVT`SI^%&*X򥤄΢V8=gTM5i$8 #"32654&#4&#"#"&632632FPPFEQOHT"'#4np}~nT8@>F@mmm\an]M'*I|))I0#3264&++##"3263#"5463!2gCKKCq[]R1 {=GluDxD]q:O1No6&  ""32654&'632#"'#4632&#"BPPBDTS;lhy{ea@T>9 :oooY^p^}N?9>E[3264&+#332#&=4&#AIIA]]hxVI+q;=DxDxqct:+o.IAC?0m0%2>=3#".546?>54&#"#>32G;U+X%8NK)Ag>(QJB6YJW^X~x%N;46<\7# .=9C`10*7DMGkwsc%0#S)*"$%3#"&546?>54&#"#>2326sXcuFNP3.<3fXbnI]N+&C7E4ZL:E #$+TMTWI>C "%-7G 5!!!!5 fmTRRR&= "2654&#"&4632327#"5i%%2%$2II29B& $$$#IdHD8T'Gm&.5#53533#327#"'53265),GGSVV &+$DDmG'Q4>7!##+"327#"?2]+R16 {Z4+RytO3#327#"&5#5354632&#"VV'14GGA: :^RDF+(DY8?E&Q#5!#327#"&54'%]eUXd]X+8f SnoOOTJ Ao#5#"&5332653>54'J%K%P:L[S:2AN]+8kHI4,NA/8aP)  6U".5332>=4.'3N7MM(](('GF3 4)+l,aJ - '=a?8ciDA &#"#3632  ]s֭-A  i#@  &$!#"'5326?3632&#")^ Yr)^l KU hAl KG3#!!5#537!5EMWfTCRRCR 3#!!57#537!5{SEV]} JCIKCI E7!5!#"'332654#52>Ef ,1|X Qj ҵRT +F,dyMAK  E3"3273#"&'454>7'5!! jQ X|1, f KAMyd,F+ TR q '5!!#"32673#"&546Ə܏0NEHHXmaJIKCKJMxeXc"6 )#"&#"327#"&543232654&+57!5!,aW=6Wb]Baa`\0aSH0Pb &E"LBO40+;KIJ"632'!!>7'57654&#"2e~W(A8u(I_S'`P< t\{O$MD#B-Wo|9"MB7U9L@#%#"&5332654.=#53533#x]hsO?H?H&66&GGSVV&66&]c|nD^8:)5=.DD-"&D6&  !>54&#"#3>32#O'? 7*haTT=_NBW%FGa;' h9$A6F"))P;%^N1aSDB&d,_d,"_n_2,33#3###535#53<<FfFCFfF|Y#?'Y#@'#@,GP#-,/P&#M,/D&w#MOLn#-1L&k#M1F&#M,Q#g$*"gpD"#g,"g&#g2$"gnRU#g8A"goXU#j"8qA5"jn"XqmxU#j"8vA"jn"XvU#jp"8gA"jn"XgpU#j"8CA"jn"XCI( H)#j"$q*9"jp"Dql|#y"$q*!"yl"Dqnd g#q*U#q,-3267#536=#5!3##'#"&54>32#.#"$9^M $5kL\nn^_mk^bmX(4$@2,}[LJ[3468TJ,#g*&"gZJO#g.:s#gfN&#&"&5467.54632327"2654&8L-)Nd0#H%0t菎4*"7Ξ԰eg*6 - p򥤄$#&"&5467.54632327"32654&8L.*\e~nq}qd0#H%0FPPFEQO4*"8vy*6 - mmm\an&#S#q$#"qk E#gyq"g_2&&gY#='Y#]'#],G,#v*&#vJ[D!>54&#"#3>32#O'? >#)k5__=_NB^#.S8d"+9$A6F"!*/!P;%^N,[EP.BL#C1F"CNQ#z b$@0v*#v #v*U#v=#v #v#$#D#$*#DZe#(#HZe#((#H{#>,|#?*#;,#%&#=2#R&#-2$#R]#!5A#|U]#5-Z#kUU#8#XU#8A#X0m'*6"'VQ' 7'lW )"#632'>54.''67654&L9Xes1b0;PX> @[{%LCxSEh[1O1{/O3:WB\$) "K20<8Az("#4632'>54&''67654'5VbOL`"F52\:8 #ZE9.Ja,80PfXD(: #]4,$J "5E' )QS#g+Fs#gK(G!#"'5326=!5!5EL &)fTͽmG'JR5R( )5!5!!#"'53265u9E &KxIJmG'#y$*"yl DZ1e&32654#"'7#!!!!!#632#"' ! . 'ds &)B9/N%URRR1# +2"(/832654#"'7.54632!3273632#"'!654&#" ! . 'esmJq~+Sf#TmT &)B9/N&T=?R%VzJ@7pH(DiR_1# +2"AYW&#q+$9"jm"Rql|&#q)$;"qr~&#y2$"yiR&#q&$""yi"Rqke S#q<&"qT\& 3#"'53265FS & mG'* DA$ #4&#"326"&5463253327#"&'OF?HUC@DKplZBS- #(+&JWySQxwvzPBL)? %+$"# 2 6  ""32654&4632&#"632#"'BPPBDUSA: :9ihx|hk;oooY^p3e8?E5VZC F  -%265.#"7#"''7&54632#.#"676328D-][mN800D0j[mT@4DMW)+-6 M,0I)*<6Ilb]7;obN0H  7&L $"32654&#"&546323327#"5 DTTEBPPY"O6jz{fl5S"' p[\on[^ox1+}Q&GmF $"32654&7#5#"&5463254632&#" DTTEBPPJ"O6jz{fl5A: :p[\on[^oE1+}Q8?E(732#"&'332>7'.#"(mnZvT#f.3!R?327332>7#"#".Y8?R!,#f#TvZd?YE '    -*O=E#m0L0# MSW-D%iV`>]T r-GB^&=0G""#%3#"&547&5462#&#";#"326sXcuVAnbXf3<10>J+;@6E4ZLV&%KIWTMT+$!5L6%-7$":>732654&+532654&#"#>327332>7#".'&'#"'z5E6@;+J>01<3fXb[`7UE (!  -**">AVuc/7-%6L5!$+TMT0OT r-(SE:>K%&VLZ!(>32#"&547"32654&+532654&"az\p",*rcyoG?GO6=;+5)01?WI(5;-LZYsm_-%6L5!$+&  33##"'53265#53lSMM &NN HmG'#K1\|{db><9 0_I\nUn^_mk^bm^b2,}[S9>EJKeV*TJX& /"32654&"&54632>2&#"#"&'3326=CNNDBLKT\|{db<$> 0_I\nUqT>#Kn^_mk^bm}Y#. H%KeV*TJX_a2, !326=#53#'#"&54>32#.#"vMb.`,Lln$8^;]vV C?UVajUB=HV|#HO;'ZR3,k 254''4733". `4ZZ47L0#/(*/NB(F<+4  #/2632#4&#"#"&5467&"#>32>54&4:;5>`L2 (+<-.=^8)%.4LbE-! ,??Af@%6O)Zf)?.Y)T}mG?3'1gF3  K, F4&#"#4632&#">32#C)@NSA::%K8LYSk70aPe8?E60'MBtF&%4&#"#4632&#">32#"'53265C)@NSA: :%K8LY &k70aPe8?E50'MBmG'#5533##5#5TSPPSKii_HHB 327#"&5'14 UF+( 3#3#53#IIRR HHH|"#>32332653#"/#&~/571T 5i'*T"sNFK2 %&]533##"54>5"TT?*("Mq&$R(D& 3327#"DT& mF'GDqJ#"'732654#"57!#3!Yam5I7)ENTT cXexg.KCK=JF  P@ F8 !##"'#"&533265332653M"K4a,%H4IOT3.3HT3.3HT/*P,$KG27P9I27P9IF&*3>32>32#"'532654&#"#4&#"#FM"K4a,%H4IO &3.3HT3.3HT J/*P,$KG mG'27P9i27P9& 3>32#4&#"#"'53265lM#Q:LZS:2@N & X70NAtk/8aPrmG'F&@3>32327#"54&#"#FM#Q:L[& :2@NT X70NA('Gm/8aPF ##3mSl c c$ 2#"&6."!26q}np}~MN)N|NRZ[QFNXX$ "3275&#3#3!5#"&6325!FPPF_&$n7Np}~nN8Jmmbb LL&%/%4.#"326=332>%4>2#"'#"&c-EA*x3:7,T/3(4,G|{G,$<=!`0.b^`:T1 qsIc9!"85H5)JU=('=SL*Fh4ZZ.8`   UM   7>53#5#"' TTTM$C( IJg'_;3 &` 7>53327#"=#"' TTT&  $C( IJg'Gm;3E8A #3>32ATTTM$C( Jg(_;3E&A327#"53>32ATT& M$C( Jg'Gmy_;3Tt 3#4 #4&'T T""mm!%%8. 4&'#54 ""T U%%!mm6 #'##324&+32RCgET,@![6%[wHL 0/#.'6   "(0332654&/.5462#&#"#"'327#""X5E7C&+N]InbXf3<.3PNFucN/& k/7-%" C>IWTMT+$# E:LZL'G&#"'532654632&#" &A: :^5mG'8?E&##"'53265#534632&#"W &FFA: :8DmG'@D-8?E5&!4#"5632327#"5u: :A& 5E?8'Gm& &3265&#"4>324632&#"&'#"&\#(0#<"#M/8 31A: :"SGVLg  *5 e8?E5 HpI W&#327#"&5#5353V'14GGS DF+(ODF !#5#"&=#53533533#!326=K%P:L[IISSNN:2ANI4,NAxHHW/8aP "F *#".5467#5332>54&'531?';d>Af9& ?1nH5%N4,E'5H&};54&'7714SI-F&5Hf1?';d+(U/#1=+.t3&};54SNH9rB4+Ht &"_6)``KxIJ->5   q #"'332654+57!5!4YamXHHEN0 cXexMJKCKIJ4v )4&'#".54>32654+57!5!"3267&,Ya!%nI)</8  '0##(-G E cX H /2&&*5   KIJM$/"#632#>54&L9XesC8T_OCxSEh[9f,A8A/.#"#&'.54632=H:CO_T8CseJNA8A,pJf9[h/ 40+4>32#.#"32673!".500GyL+_^Wj{mZ``!KwG/d2bjO4NNej3Nh`1&&2F "$#"&547&54632'";#"32654&nzcrVAp\~^3?10)5+;=6OG?ZLV&%KIW|/+$!5L6%-_msY[ *326=#53#'#"&54>32632&#"#.#"vMb.`,Lln$8^;c;N :V C?UVajUB=HV|#HO;'27E533,kF &!#54>323&'#"&73265&#"LS/8 31S"SGVLM#(0#<"#ii*5  HpI* &7 NF| 333FT @L&F $"32654&54632&#"##"&54632 ETTEBPOU<9 S9ihx|hgp[\onZ^pI9>ES/"#6323##5#535>54&L9XesC8mmTii_OCxSEh[9fHHX,A8A/.#"3##5#535&'.54632=H:CO_iiTmm8CseJNA8A,XHHf9[h #"32654&%!!5#"&546323 DTTEBPP'E$"O6jz{fl5S!p[\on[^o?JIE1+}QI[q*6#"'732654#"57!#5#"&546323!%"32654&Yam5I7)ENJ"O6jz{fl5SDTTEBPP cXexg.KCK=E1+}QJ p[\on[^o 7:"32654&"32>54##5!5#"&546323!354632 DTTEBPP &"ISNH"O6jz{fl5SrB4+HV#p[\on[^o   )6)``E1+}QJ->5'^1732654&/.5462#&#"#!"&5#53533#2H&+N]InbXf3<.3PNFvb05GGSVV6.$" C>IWTMT+$# E:LZ+(DD &#+#"'5326=#"&5#5353354632&#"#32 &s14GGSA: :S^5mG'C+(DY8?E7A#327&54632#.#"354632##5"'#"&5#5353"32654V a.j[mT@4DMNFB4+HSNHP: YM14GGS 7 DFib]7;ob]j->5=6)``*+(Di   90#"'532654&#"####5354632&#"3>32JF &:2@NTSFFA: :#Q:LZ58G'/8aP8DY8?E5RX70NDz(7#332654&/.5462#&#"#"TT+F7C&+N]InbXf3<.3PNFuc22&5-%! B>IWTMT+$# E:LZD- !!3ET$ JII_-/Z*)Z*)wh!#5!#!#5!#FFFFhCC߉CC: 4&#"563232653##"&5l& C)@NSS%K8LYN'Gm(70aP!80'MB&c%327#"&=#"&54&#"563232653 : :A%K8LY& C)@NS\5E?80'MB'Gm(70aP! 0K) 0) |M)&{U)pUpfp>=3327#"=#"'776  Y,1 K0Cz.GG(Sr \qfrZ);q\)1 1'  W #5433ii#<x&&4W 3#5>=#Wii#<x&&4W #"=<#ih4&&xC_<5OC "3"&546$44,,4$#45"#>32#5>54&2%9IEBK,$7>3+6-JKC<%B u*%*5.#"#5&'.54632(/&+3>7$,KBEIg03*%*u B%3#'#!c&MN&3>dM-O33#'#t_`?QO@``O2#'373_`?QO@O``LQw.q\P-vPCd#53HHt/qS7$C=#7jg"/qL"qg"3g~?"rz_!{_, B7533!5Fx۷FF BtM' nB %##5#53533ByFxyFxyyFyy B-!5B-FFU< 332673#".;T(0; $<4B" @sd#5hhh=1 2"&546"2654&,>?V??+$>,+=>+*?>93 "&546733278L3.F7'H%04*$:,6 - e?23273#"&#"#>[X :Jd :-%%h&%16N\3#%3#=q<q<䖖A:57332>7#"&'JE/# -*-NcKN&r-_UGID0 ?q):rO)1}V);q[)65)( #!5H H( #!5!5HV 3H( #!5!HV nHj( #5!5!HV H3( )5!3bVHHH^3!HHO_HHf#3!HHOfI86gQ. &qcq1. 37dM&.7d#.b$/c:znkn'kkl'jje cC|nAMA3##PHH#5#5HPH 8B53!53HHdd 8B!53BHHd$M!'&/76?M%33%h +00+!PCPvOfe|wq=us!5sHHUx&dydj4}#532654&#"'632,, %(04F@ 2&$31zO3}Og1Ly#53HHLL&L=N#'##'`<=`<䖖U/#ycxU x1)G +2A 3#5265#X/)-I+/*,A #3"&=|-)/J*/+I+A8C8v3#5#53HH3SH5G!#5#FwC +53>54'%-I+8pT  &xs$853533SHSHSSH8##5#5.SHS-HSSH ##5#5353.SHSSHHSSHSSXdwM}U&- '3#"'53265SS &-mG'&X- 5327#"=& -y'Gm[Zy^jzzrW 3+52=#d4.77Lg8A&N*zi3{ E;!#5##FFx8|9kew.qIq9Ch3#YLLhC3#YLLZzPw!!75#}'D}}R4632632#4&#"#54&#"3%&'$3,,%34$3 ''7'7N __ `_ __ _GHGGHGG!FR5654&547w61%&h%%", g7 ,  eMcQCP\|2AXV[@Q/&,&Hɻ3#5#=S]WS#7&#"#>32733273#"C+ :-!%'>1  :J4G%16@T%hh4#y0#y|cF=,#|_| G  Y #5'67&'9 4.8+ A , -19% 5 !c:M'3 #y xuPQ~j#~P{$`& 57'5%3#'#鼼K!c&MN&uJ:;;=_/|%yJmR37''7'7/PQ5'./&4QPDT,CFFC,0Ib2>32#".1#"&54>32>7".#"2654&#"W   & 2$  $2 &!  *    1   "8   8" \(     &M(&     " 37#7#A%9?' e_9N267>73"&'.'33232653#"/&3C+WL**,"C,317 =A%*$FKE +); ?xNU+ 5!'7'7E%9YY9%E.?3@?3@9dDBQ:JHCQ?UL?Q:HRCQ:L XCQ;^FDQ:VXGCQ9XPK:Qm7j B@82>73#"=3 !1JL*OQO,nm sPDdlM  3#'#5!#5Za:SES USSSS V #5h hh&#ze '%5&'se" D '60"F$Oo%R6!#!6|`{5)3!bt{;Ze(G=S+&!5!#"&632%"2654&)Pt菎@R1VԂ򥤄d,O.3#3#tcxh'uK0L1Ac !5!!5!!5!S9gI"SsSaS&2P!#!#!^^0y[i3(^ )55!!!^tU#RRQ7 <%"&5463532##4&'>={|Q}{SoVWnVon_}yQQx|_vVk c r{ lUYq;4%##5#"&5;332657"^"zvZKWVWKZlaaQA7AQ7kb4!%>54&#"!53.546323!;D.qmiMOZhTL /MwAqnc#LH.ei,H##j' #j20G#>'"{BJ#D8+"F2"`R0G(4>353327#"&=&5#"4&#"3260 1S4o3H +%#_BxRBJFHN!CJ9%FRW E  8Nni[f2:)4>32#"&'#4&#"32654+53262*Q;_iho_;F!VH?4;)qAE2*?G7RE$dXw%_w$3?)jTJDJI: #5&#"'4632 V $.'v F #7~$-74632'5!! #".7";27>54&$v_    y =A1FRPD/ %RsHH%$/$8]gO[]]5Rb'$%#"547&546732#.#";#"267 mgffpf ch LV;@@..?AHrXPB_`Q?CO'%U/(;(/,(%'&>".5467#5!'654&&7L=+gdqED&L" .T6BH<@ℎ.A7aE!#J4&#"#4#"'632632'.>X  $CaR[Xn{lLH6SU.v8 7327#"&53 .,<@XQHE< !##373l>XXr < ".#76323267#".' #  6 !$0!! z`') R !0:JFb\<8"&'#332676=3#5+6XX 8)*=VP; "x##*17j>%%  Y>/%2'654&+".54>7.5467#5!#";#"jI'J#&8Ca1'3"6D, dt*@LXRnJd^F*C=^V"!78.H) K9+HFFT0+E@RxD$RR %27#"&5###5!#$ 0.,^F(@@ >0FT:FF28"4>32#"'#2654."20B>!r>D+Gc5X>\11410/1=_8% wlm&G\l8Q)*RpR*"J&4632#4&#"'>54&'.'."tc|[JB01(H;/H@0Z -(FI2.9vu_@F'L4.B%  -<,n P %p"N4>32!##"&732654&"",KO,@qsZ[=IOLLMo9RRf{|k[lZVpr "&5#5!#3272I1! #CQ3II+O 2 7327>53#".531S<# XjnEZ.XbHD<$~V:H7F.8 #"&5467;32+#54&+3267yZF*3CiNXyn X]OK] nZ$D!d;NiŘznOhf ,<+"&/#.##56323327<$=ah   B `Ã$ 3&hN<$*8l %##5"&53332653luVtZGUVUGZlljn6YQNQY$'%2>54'7#"'#"&54673>=3.4(4lP*F$<=!`0.b^`<4Pl3:7,T"55H/u"NFh4ZZj<)lIc7 "jF2"jhR$#L2"qR$#V6-4"34&454>323##".'33265C# $8$,! CC 1L24N, P>-AIGSJi 46_5D# " 3'@*#HAeX8 &8SG+;^@v452>32&#"#54.'&4Df>! PK> '&'@2&`.&-`3ZQ3^E$. 9\xETp_+4 "]4#j]98!463532##5"&73"4&#269Xz?nJXzTa_b^_ae[uy8^M+xSk|oOSki$+%2>54'!3>=3%##"'#"&5467#5.4(4NM3:7,T#>#$<=!`0.b^`!>"55H/eeIc7 ߞF0y6Fh4ZZj(3F(  2733#"'5326=&5#7654&# cgjϐ &Ɣh:ka Tc}NqmG'OFc8*0!>3273327#"&5#7654&#"Z1;Ocgj-#/,6]1=Oh:%.+(9_e}NF9F.4>32#"'3263"#"&572654."90B>!r>D+Gc54)-1'-Dm>\11410/1=_8% wlm&GR4Rl3[\l8Q)*RpR*&a0%"4>3"3# 1S4CFFBN!CJ9%LVLBJVP[&|CP[&|jl2+53254+##5!#)J[ 54Rc9B]WE,? BBK9PPP9#jz0 .#"!!32673!".54>327^W^x lgZ``!KwG/0GyL+NNsR~ej3Ni`12bjO40m6d,#j,-S'!##52>=!32#'2>54&+,F.%ua 1>M@{Ch_T,^!DNteGtY^rR+48FS!!#3!332#'2>54&+^]1]|w` 1>M@L;uZ\rR+48F2#4+##5!#)J[]B]WE;9PPPn&jP#C [#xSy !#5#3!3P^v^y'$Pp%#!!!3 4.+32>p-Y=t] 8''8$FE+R.*,-Pp%P93#!!]tR"y %!!>=!3#5!#5KE*[PPR5hv ٥qчP[(  # 333 ##nEt]tEv]Dwb44D0m1%2>54&+532654&#"#>32#".'3V8N%`ClX?_YJW^X~xi(>gA)KN8%X+U;*).TR[.7DMGkwscl-1}9=. #7\<64P 33##PX}eXi['VP#xPn 3#33 #]]1oz|2M!R!#52>=!#,F.%^RDjaV-^"FPvgG'P0P+.2P!#!##^1^y'P^30&Q7 [3#733qqo{0-#/;32##5".54>;532>4.#" &CcF3'?6K._.K6?'3FcC& _0H5''5H0_0H5''5H0|7jH6W8( ^^ (8W6Hj7]%JdJ%z%JdJ%;Py %33#5!3#^CP^Rqчy8"#"&53;3#MX]L]]\A)9t'P %33!333^b^^R'yPy1)333333#o^^^CPyyq 3#5!32#'32654&+Dd@RQARxfcRR=@PP %#!33232654&+#pd]=@RQAm^cxfR=@Ph'Pp %#!33232654&+pd]=@RQAcxfR=@P* 3267!5!.#"#632# `Zgl x^W^_+LyG0/GwK! je~RsMO4Ojb21`iN3!P#"&'##33>32%"32654&v^]yr|}rq~|aŗ좡^ 463!###"&6;5#">vaI]oneqQ7>53"32654&w,oq}np}$F2?)/J1%S(:FPPFEQOFLU  ( N=mmm\anF %4+3265'4&+326'2+y[*1.;bb;.DRFE2.TGF+$,)P4Q"5!3#5!#573#W0"S@FnFݬ@4xx t;&H 73#'#5#'35kjSjk $#732654&+532654&#"#>2#"'|5E6@;+J>01<3fXbnAVuc/7-%6L5!$+TMTWIK%&VLZF 33##FTlSm ccF"xvF 73#'#`jS   !###52>5lST !2  b%F$ 33###FhhT2T FkCF 3353#5##FTTTT &RF !###FTT @F&S&F$ !###$tT L@& \&&;&2632#"&'##"&5463253"32654&!"32654&-dflmd1>#S?5egke^1S@CC@CFFdCBBC@CB^#+-&^lll\`nm^_lk]am [F$ 33333#5FTT=F @4x0 3;53#5#"&50T"TT;V B9Fn %33!333TTTL @F )333333#dTTT32#"&'33267#53.#"*m[j}k^pTkAM I:4@\\ck`~^TMIP;7F#4>32#".5##3%"32654& 1W7q}n7W2! ^TTJFPPFEQO51D8'&8H> יmmm\an 4>;#5##7.7;5#"F!@,TEgCR[[*1w/0L=F+$&&Cr&&jy]'4&#"##53533#>32#"'5>5B*BKSTTS *;#KZ7PP 5{\70bNT@EE@!JD?k@#(|\F|"j&%#3273#"&54632#.#"3_MAkTp^k}j[mT@4:I T^~`kb]7;PI$VFL "j&M  72>=!32+##%#32654&SqmkaF'B8||:81P,yCXEVa6,+(F1 !#5##335332'#32654&TTTqmkbk||;72 BXEW.))4&#"##53533#>32#B*BKSTTS *;#KZS\70bNT@EE@!JDF"jAF#C&"xD\F !#5#333:FTTxx @!#&53>53653#&'6fKpdFG-*6dBKPRd)EB&f[38~ٹTW^둕o߾@ӽ 3#&3>53653#&'^Rk31RkdR^C#"+pijwy<5*8l U0 #"&632.#"!26^pm ԌaVԐ}|Fu& 2#"&6."!26q}np}~MN)N|NRZ[QFNXX'{|'* ''7'H'HA$''#7'57'573@87;?K7MCCBBc 53353#5#HHHcCBU+<`@ f4@C"#46323".}' @[QJFN (N>~ '$I]!)!;  Y&F'FDy'Fy'F3'FF'Ff'FF33Uk "/<IT_3#5265##5463"37'654''&5477#"'7327''7632&#"#"&5335532#4&#X/)-YX/)-?3 ?3 >4"  >4" I+/*\I+/*JI+/*\I+/*>4" >4" ?3 !?3 X/)-ZX/)-Py 332673#".333#5##;U(0; $<4X}eCPKiB" @[qчVF- 333#5##332673#".FTl7#!#53533#32&]vaYY]\\1M+4^rFxxF| %4+3267#!#53533#32[)2[UF>>THHvphN* CS~FHHFKLP^ 654&+327'7+#!2KC62L$0]-lu<:2!6a@TM;lhys7U^poo@2?2CNO^P993#!53!]]t`Fmu353##FTT i@ > 3###535!!jj]KKtFYFR ~ 5!#3###5H6DDT>HLxFFPR93#!!32+532654&+]t\]]\40+55+RfZZfR/-'-/Fq 26=4&#"#!#>32+)C$*=3T'A9I>YLF079.[Vd L0'IFBM y%3#5### 333?@P]nEx]xJчDDwb44* %3#5#%#5# 35373JF!Sg0kSk@x*3g:%#"'53254&'.'332>54&+532654&#"#>32gv6L80%H!uX+U;8N%`ClX?_YJW^X~xi\x "7*4 - 6% t64*).TR[.7DMGkwscl-1"32732654&+532654&#"#>2#"'53254&'&'z5E6@;+J>01<3fXbnAV_S6L80%H!/7-%6L5!$+TMTWIK%&VDX%5*4 - 6&Pyn%3#5##33%IP,]]1oJчM2F %3#5#'#373@F$SS`@x P'#375373#%#0]]0FxvtF)*MR?F 7'#375373#'#1SS1FgkgF) 1k%gqt* 3#3 ###5353``kxdn]UU]oF;D)Fj 533#73 #%##5ASGGk0gS<..FjF> #5!3 ##Dkxdn]R;D #5!73 #%#̸ k0gSLPy%3#5#!#3!3CPQ^]v^JчL;F$ %3#5#5##335332+9)C$*=3TTA9I>YLF079.[Vd@ 0'IFBM02?"&54>3"327.54>322>5#"'7654&#"c#K^~{mm 1:1JD_cz  J-T741?Ls6/%+?ƜPyFR0HZBui (!*wKnT-fK8y&p%."&5463"3263&54632327#"'7654#"vt}KiFL ?[4AS< ;: KA6.-):7$xM^fuaDegjeOGC$&Dh+hvA9d03,#"'53254&'.54>32#.#"32673L80%H!Hm:$ 0GyL+_^Wj{mZ`` 5o*4 - 6&CUlL"2bjO4NNej$&3'#"'53254&'.54632#.#"3273jL80%H#Xfj[mT@4DMNEkT\N7o*4 - 6&qb]7;ob]j~Vh $yQ %3#5##5!#bCPP32'Y8?R01f#TvZj~PN3*$+: " c0L0# .MSW4K%iV`xU>9A-'!%+m&=0G"/;r <.#"#>3 !32673#5.=.54632#4&"3 &C-Vu_ 3>Z4,{jW^_(HAg>)fgM?7AP.C0!-9"f[ :17'MO :Ne\.uSN];7 4*6B$;/6%3273#5.'.54632#&#";>32%!4&#"01f#TbJH[lPN3*$+: " c0L0# 'X9?R4K%iL^ nU>9A-'!%+m&=0G"DMSWP#^' #x}#xPR2+5326=4&+#33\]]\40+55+]]kxdwfZZfR/--/;Fq %4&+#37372+5265qC)lSSkMLYYL)CY70 MBzBMI07 y!#52>=!3#5#,F.%GPURDjaV-^"FPvgGqч !3#5###52>5dS=FK !2  4x0)CXDG.PAAqQDPR!!#3!3+5326#^]v^^\40+5L;9ZfR/Fq 26=##3353+)*ATTTYLF07 BMPy!#3!33#5#$^]w]GPTL;qчF% 33533#5#5##FTT=FKT 4x=4#"#632>3226=%!4&#"01e%TvY88e;L\s3$vp Tʀ/\6;W1FZgG86&P@?R4K%iUak:4TGQN N]R&,&9VL0Qe  ,('-GYWP[#x|&"xr/ #7326?# !654&#"#632 &C-Vui/GwK{jW^_+LyG0!-9"f[h1`iN3eMO4Ojb /#j' "jp( #j&}#j0m#j~$"jJ Ey(q 2Pg#iF"ivP#jF"jx.#j&"jw0&0#j7&"jm8*#j$"jQ [g#i&"iT [#j&"jV [#}&#} 8"#j0"jXPy9 73#5#!!CPPtJчRF| !#3#5#F6=FK LxP#jFl#j0m1%2>=3#".547&54632#.#";#"G;U+X%8NK)Ag>(ix~X^WJY_?XlC`%N;46<\7# .=9}1-lcswkGMD7.[RT.)*"#%3#"&547&5462#&#";#"326sXcuVAnbXf3<10>J+;@6E4ZLV&%KIWTMT+$!5L6%-7RI!#52>=!+53265,F.%^\40+5RDjaV-^"FPvgG'R\R/-q !+5265##52>5dSYL47 !2  BMI(b%&4$&T : ZPn3#37'373#'#]]ru2Ko}hu(z|β](K}U MF 7'373#'#'#qXu.`Z`u ljS qH.YOl F%2653'#"&53YrO_W;[ 1z^&:Ipv]De$$c#6"(Fy2#54.#"!!#4>aRp=_'O=1D&/_&>e-XlIRQ:LA)LE8];ZR4<&%##5#".54>3234&#";k_?_1:kLBd9# n^K5J*/@U8:MZ5$*:4TSq=RR_ #K:rO_ 8m+VmL^8HC"pvQR/F^<+Fy%26=3".53!!YsN_%=dDSr=_/ %:LovQQ9XR6!+XlL] 6"*2.32#4&'!#5332>54.#"3!54>32(4+{S!$YZZ ;[O:,GF"YE]E}Y:hH=! 37,BvI=Y-siQLud65}F %#33#@`OOMFy#54.'"!!4>32y_'O=9I& &>eDRp=9KBAM9W;ZR4-WlF 3"2=#".546;54&'#"#4>;23#E.4Y_5pQ8Q-Wx?U,1_.KL)i66p8^U8%AC''>7$iBAE:- :U+q\KNA|%"&=4673533#2675#"mjm\c`EHAE[po^~\oK~:O=QkFx4.#"#3>32#%I8Jg ^b y/Ts>^8MA45%:"-ZlKGF^333F_^F%2>=3#".=##33$#-+_Tp9k__ %II,`K,YjL@9KA #".3!2.#'#"&54>7'"=4&#"26v\^$ A~'8fC4Qkpk`Fy%".532>=3#5dTr>^$G2.C) _a \,XmL)4C'!">.)L$42&'"#.'%6,%' /t-g.0" 82O/8"'54632&'"&54672>=4."327&KD[_oteV I/(9OFA:$55?U)2#5 2*WG=BXo?c< ( "''K*>C+ (Y,+D%0#9A|23#4&'"#546.ja=QCK_q\@K@F3&'7672''"j+;?)/<  ?BwC,@<+XA 13/ `y^,G  9=C5/2f (L_><%2>53#".53\8J%j>qSRp=_NK BL7]MmV*-XlIXvo-&4#"&5332>54.+53254&#"#4>32)Bny](2 )##!5-X45G`.[?5T2! cOds{S!35!-= Ms:35,77"&31y ".'#53326=3o=&9%sN_< 'UmQJ^X!6")ovIjY(,467'5'"2>=3#".5(nRXK*=)`q;_8%MsTaS[R[ F2@FY%7A3Fy#4.'"#4>32y_'O=9I% ^&>eDRp=b9KBAL:c;ZR4-WlBG#%5>=4.'"547632G-JT-n61!GW_@Ks>c5$ 32t_,+'a^&>eDRp=bBV0 Z*+;ZR4-Wl-9674.#" 632#4&'!#5332654.#&'54>32 )*HL+0L, _(4+{S!$YZZDk>gI)n)>iB2K?:UH?\."3A5]thH=! 4793'4Y#GP<(%7F3##4.'"#4>32yKN\'O=9I% ^&>eDRp=Q9KBAL:c;ZR4-WlFy%2653#"&53YrO_:sTz^$GIpvbFh\0c(3E)A %".=332>=33#5_Tr>^ &:&.C) _Z \,XmLed !9)!">.)^$-g.%4.'4632#.'"#".53326 0M^`O6kHl4[VRJZ2Oa`O2qFnB+YHi(3'O9\i(5D&C>75$0)O7\z 2D> ?Fy#54.'"#4>32y_'O=9I% ^&>eDRp=yx9KBAL:c;ZR4-Wl<9(34>32#"&5332>54.#"'67&62654.#"O.bFX)Fsq]WI)-"#/$h#jJU[RxP1;!: 9<$e% kNb}w`?M6#%95A `@79(3FY33##F__Pu-'-%.54>7532#3674&'\<^E42D]=^;\L8!9@H4^,=/"^ob0,?tIGp>,77 (?vL54. =E2"n)OM0!3C=eϚz '?pI~5;5#"#".533'".546;32C/,..--Ym")flAc7# ]-48#ih_.j~0D ?+hGG_{$4-mp ;)X=pbOC_5 53'65=^35mfP9G"NL73X7iLA2>32327#"&#" 6=,0../8%x"0705PC L14>32#"533254#"# #E/:C4*U7%Hn721?/,3a)7e*=3'?3! TJQ=2TP7A !%2653#5#"'#"5332>53(E;XOT4t=cX$8()X*B_ZI$0[[h?@ A.19<F8%#54.#"!!#3632X &KXO323##4'"#3 !BLddXNGEXS   D>>6\_K A%2=3#5#".57!!WO:k$9% XQ/AaJU'!1BDG#8;7"&54632533#2674&#"Wrqf7GRc1HC:CACEAmu+6g;LTitKVtrFM %'5#33#&LVJwBA8"!!3632#4.#I_O323#2=#"aEBHYB=>:(? VNT%B^+)(PH $G^JC>XA79&7.A,"$81P=#"#mbYbb}_1O. '4 z hDfz%9KE"FQ;F8"#3632#4&'*-XX>]dOX4"H3GhaI?>F8 #3X9eF8%253#5#".5'##33XO9l1D$XX+?"JV%D7-}BF#$#"&54637&'4734.#"32"e}~P=U3;^+ZJBIy\Lvu*C=64(2@D8I1ce[mF8%2>53#"&'3 *-XX=_fKXG"H3, Eia ~F"#3632#4&'*-XX>]dOX4"H3GhaI?>#&42#5#"&54>74.'47332>54&#"  KVM D9dx1HJ>@D)HEF*: H9=R- _?3v4bH;0 -. PUs.J8Un}A8.23#4'"#3>8)=dQwXRB*)?2\ W$,747'767!%4&',@1>fh1':eb ()#1@2{T0'F],IT=A-4.54>;#"#5#"&'332>7u+3+-,MT6**T) !!AQX$*,: 6&@&$D%&+9O8G6-+2*8 2>53#"'7& X9I*{ !^X KF%273#5#"&54>;"_XPWOAQ%M3A_@1?FS]F49B-=7>#3(3.+53(E;XOT4t=cX(()X*B_Z,$0[[h%) A.19<&7#5;#"54>54.54>">54&A].7*kƅ%+%!0/!))4 9/%%(9.GB#," "?H0#2-$+L4.G) JB@!7(*@)79R&ERA72>4&#"#36323# GD:4XO%2>533#"&'3*-XV=_fKXG"H38 EiaG~A8&%253632#4.#"#5#"&53U.&/ LXpaHSTA8"#3632#4.#XO5#"&"32654.#wj\;Q "1J/0<'UA1)89Wiu5W.-8V-/sJ?:)>*/ ;),.;1-D?`f;V*ai7R+F 3#~ 6B A88)%2673632#4.#"##"&53M;T32'"254&umQ15VddQ E;%HG+4LE:@g}ABLLBFA47kvU_HfV^# %".54>32"32>54.+NL. 1C>!m"1E; 1/]<2/01 9pM>b;&wDh:$ +U9ka-U98S+*8o#+2"&54>;32##5#".533332654#'35"Ia!=I/>=Yl*:7-W/(9/!NW%DD5638PC*; ߆i<_9%7#JnlS-%!F ".53326=33#5+> V%*GHVO>-# `-+KDBR$Z5353Zdddee^dd &27"':'0AU+Y  " P&f']&&&zzn&W&&__ }P3# GG3>Wd 3#GGGFn  :FX##5467'36=3>Q;f&,WPJ[sCVL(CJ)LLX%#73'.#52#BuQ3G36TC82X˧2C L,V9&X#!5!W  LL?:X #.'#53@{lWAZWXamtQ: Lw>X33>WX'(X##5!WW  LL?;X 4&+#!2#aJW\Wt=[X}a=:X%26=4&#"5632#"&53?Y Rzd[Zz5c:X3:WWX#&8X 2#4&+5iWaJXY7>=!53!!'.2W%,4 [W5V+, *bD?Rn(*@5?;X 2!4&+wxTX_X+@U@0:X4&+##532!53uWA:X:N{wAT L`L>8X3>W 3FX 2!534&#GqRVX^L(>Z=9X  "&5!24&+3265ڑtWWTWPL[[zvhrJN>ZY?$X737>737xYs# W +C:C  &!X5:9'LO98 X4&+;#"=32#N]y,@"(~}WtTD1Lsk?X*2+532>=4.+;'.=Aj=*(GqA753 [UWc#WKc/6 +3X!5!3>=3 f<&Fd0-W@jL 4'>8&X#53>7!5!#&l|>7$5#ooWlbL)Lr%X 2#4&+5uWO\XuitNJL<X6753+;2653#.53bWpFd x>WBW{R}W,YG^8\nLRXZW#{X4+#5265#5!2#$ityMI<yWtukLDM/Lqm>jX!3!3XXXX<_X3354.'33##:D! U ;!,kWHV,,54'* S*IC25^H7Z,MuG)5!5!5!ZXa #"&547'!5!73#".5'2632 tmM 8#&'f Z-=Z #}a%#"&547'!537373#""&5'2>33 tmb(M  6?'f #Z-=Z#} kq#532!5!4654&#yyB\oF(TS 1)Z a Q/k2!5!4654&#\oF(T 1)Z a Q/m".5463##'2654&#"2>+_UdY /\&44*&>9'X<_,EX!gOI14XY+6Ld !"&54632"3264@bz|`ay{1bNQ43J~WZ|}Y[zQK/2KHbV[s.54632'>K"2X ): 3_2@(HdfO"!#'#'!53547O$JzppssZ\r_ '!5!73#_J.DZ,>Z__ '!5!73_JZ,>%'!5!5jieO`ZS #/;U2#"&5462#"&54632#"&546'2#"&5462654&#"".546335'##" ! K" ! " ! K" ! h&44*&>9.2>*_Udj[ /> ! " ! " ! " ! "NI14XY+6LO)V;_?ObD,EX!ga(%#654#"%632Z\@DtaE\I;@ؕWHO]qZU;I@np)5!7pHfUJZCkv )##5!5#=SO^Xk?ks'545):5-AASXDfm(hj7&$ "&/.(# #xOaI8UZ-_Y+M Y :1 0#`n2"6E2632#"&+"''7.'&54676>54&#"3">=4&5):5-AAWT<  "dH*h "  7&$ "&/.(# 1#xO_J.2 H+".1` +M Y :1 0#`(2%#"&'!5%277327kF3C$&8,A(E,71D1Z)m+>CK@ %"&546?!5!'7327SdVF9? >"pA0(F)OL8F3Z!."$@ #"'!!32654'./;Vd%>  ?)F(0J""LOZ@3*$"$#!5!".54735'#"&'i)t6/i #% W||W Z8V|9_ SI>O] "J?<9!& 3"&'#53!'5#@F5AdGUdk`#532!5!4654&#{{n\oF(TR 1)Z a Q/!#"'!5!'#5!3/\  ;;ZZYck%"#"&54>732632&\ n69KN(Gn*i :((/C9Ee*lSJ*+99&T''7'77''7' &%Y(,$"5'=d+jA!577''7'77m &$Y(+#"5(>+jA!5,O3#3#,####s*/'K7K/'K7Kq5 2#"&54'2#"&546.! !n! "! ! ~ ! "* '7''7'78!8 8KK7^_`]_ 77'7!9 8 8KK7^_`]_3#'3#r####K}/'|KiK@KL[ 535#53#3HHIIL"< <" D 535#53#3 HHII"< <"Ov 2#"&546>" ! B ! "6{ 2"&546/537$/++U y!;9 2#"&467#'7yi.++-$ w";8jv 2#"&546>" !  ! "fq#53qbb]jv 2#"&546>" !  ! "Ov 2#"&546>" ! B ! " x 2#"&5472#"&546).! !7" ! V !  ! "w 2#"&54672#"&546?" ! ! "! ! " ! " #2#"&54632#"&546'2#"&546" ! " ! K" ! t ! " ! " ! " !4632#"&74632#"&2#"&546"! "! ," ! n! 0"! 0"E ! " %N,`H% RHf7!#7777''7''7''7''7'$"$$#"  ; ; ;> ? ? @ @ ( D C C C B @ B A V  )3533##5#6#"&542#"&2#"7#"432V'SR)U, , ,  OO%QQg, z- ,J%J'72673#"&'3&IDI+FIoKLnG*!AICI-6N]_L+>M'7[TZ\T\?)'&&XQ23275#5354&#"#"&=3326323##"#"'"&'73254&#"'254'"'4>8P9T"/hm! o(8A]5DLGJ)J;.)5PC*,HG5; 3IeS32: hVJ,/eE.;-)5PC*,7AHI3;7\):Q9W@ II A*(<e@23275#5!####"#"'#".'73254&#"'22674'"'4>32.'"'46322674&5XN4*""&R* XN4"ZH39 "0d-'D",,5)NhkM62 ;$F_vE] `IIM ; ,R?8"-[ 8+Fe+4&'!5!##".'72654&+'326 ~Fr+7,/hK(N<9'!?*Ed072D0aHDEEIIC2A(U1OZ';PIL+&dd3&4LI$e=2'>54&'"#".'72654&+'32654&'!5!!6{325!5!!674'432")::PCJ7(J,b %-2 VF5|{F7 %,?>:R71C *?ԟ;oN8DFIII4 J)!-d$Ae>.54632675!5!#327#"&54>54&#"#54&#"sh2-AuX@Q+K_89'77'*(9';DFd'77'7%&4J2,)@@SDa73cIIf J20Q4+'!=)M;%;**>& )/"FU >{?'gB3{X72674'%#5!54&#"#"&=3326323#'>75##'& *J# m$75##'' *%U<1Y7 M7IIF3 ( @={#2674'%#5!#'>75##'#'7' *%U<1Y7 K;R:3(!/f# r&;A`7DRMGLK(.dF.;%8?Q=2-6BGN/;6\eU:A* I1"!0'M57 TJ MMHMI(;K:.)6PD*,6AHI3:6[eO@A*&I:J ]m #)<'P:3 <7.CI, 0+WD23275#53%7'73####"#"'#".'73254&#"'22674'"'4>:S9T"/h1d?UMGLJ)+`I1;r[SC*,7@HG5;6\eY6A*&I6$J 8MN#P;2 <50CI, 0+e<7267!5!!632'>74&#"#5#"&54>32."#"%H9I8]yFT.#7IH+@m"L2 % #/>(  IIP[9X0m1!,/' $`D'O:P4)4- eA%#".573275#5!##4>32&'"3275!#'32675#"&{";9eE9" < 2' HI#*  =L($D)0/$8X2(94D:Pq7)@PLD%$."-IIX1F: 7Λ&3::%:(3.D e#".54735#5!'# E.(; GH :DII$Ne).547#5!##5#".54547"327!;"6ENIIoW2=*(2.*Vx*IM*^I0%IIܼG 9&6-$P'(/Le/346;5!5!##32>2#".573254#""&#531>6.>.,"9="4lRB$34P_-q H6y__y"!`II1= I 4L(6MQ7."FX7p"]le%2675!5!##5"&547#5!#8e $lGIt5@NEiY&;,`:IIEFb-#46 uIIg)9PCHnX%*-g'ie0C%"#".546;5!5!##5#"'.'"'46322674&7##"3>3232C YN4"HI7T&-'D",,5)NέH 7. '&)E] `II|-F ?8"-[!8+.M ( e(6323275!5!##5#"'"'73254&#"pN*)'II3%D*Ur0INpK4 & II!2&V:Fv95 8e746;5!5!##"327#"&Ag68z)5+6(_3DV{lMmpIIL()A$V*te&732654'7#".546;5!5!##"s(1'FB96LzX6_<"`Hl$ %;#197'1*iP`.HS'De}II#ce/46;5!5!##32>2#".573254#""&1>cY6.>.,"9="4lRB$34P_-q H6yy"!`II1= I 4L(6MQ7."FX7p"e%.74>;5!5!##"&54632#"&>54#"1A+m)2YM>4#5 #:<g>(2.7@,hII>.?i'-4B,& *= L);31`e####"&=#5!26=#`GJMC@U*`[-$?YU>I)1V4e4>3!5!5!###"'.*1'\4GI?%4 !! 84+hIIr;>6 !*+04*e3%275#53##5".'3254&'"327"54>320rahHI!&*5eC)(15.B; "3+K^-II  0FH {)6.0Cs 0 eEAe&46;5!5!##"34'432#&'".*E4H!(F&,/b$In?' &?W`II+!C&1*M >B)93(Qe/%2675#53##5#".547.54632#54'"63"e9`HJ_r$: 7-5G3.CI(1:0S ư89II_ & ;*W*0HG(%0"R ES48e463!5!5!####"*JY8GJ B(%IIOX 8e'^X6Le 7"&=#5!##5'275#Mh*IH*I<7;X@II'J7%-e'7"&=#5!!632'>54&#"#5'3275#Mh*D1E^{E"S955H2;-;8ۦX@II?WDV0j2)&&1#%-0e$%67!5!##5".54672&'"327'M HIA=5L#YF G8+'7.#$ IIܮ/)<0Eg PN,6vBe&235#53####"&546;54&#"#&546.IhHI b )4N Fc/7II c r!/0=e46;5#5!####"&%5# )^HI bg "II NpI,e4'#5!##5#"&'326275#9,HJ=Q`$)5LPD)A54)II5xd ,Q,30ZLe#"'.'732=!5!#dK6 :y3 "'0?;J>O3BT 6")3D[)-).IIhe'^Te%73".54632675!5!##'47&'"s4F?%NN8cLYCBHGJ/b B&1./F$K3!@-Fe0E^GB|II`r*!!(. ,:e/;>75!5!#"'#"&5463232654&#"'.#"32>P >%A,RK^'(`HS\DWR:-"+(( I3)VV' `4kIIn 1;,UfY]gVQh'NK0+<  *Amu- e'^We&'"327!5!##5#"&5467202,.!"B<^I@CGfWHFl9'( <!IIܮ4j?Dh e )&'"327#%72>7#".547#5##7E'(d+yP2./6@,28:.bMGM*!z8EW45( =(*6&5IGe 7"&=#5!##575#327'Mh*IH**:.EʚXAII'&,)e#)###'#"'.'732=!5!5#GK&D 2a,3 "'0?;J>&%,Iw.6")3D[)-).Ie57".547&546;5!5!#!"632'674&#"{Y=_R;AK#K&:!99!;,=09X{7K< *I-98K>&`II ; 7' .! :#53__O]K##".'732>54.546;#"?ZZ?bK)\A+3Z`.)?[[?>3 N +;470W2"'632#"33254k@ 6=S&7 rP\G wBxA!.:)'AS`]L527#".54632#.#"B6.9)(@R.SQ4a^(%42>7#".546;#" &)D;='E9aTC9 <*8%8IG6&A!/46;#"63>767#"32>7#".547&E9aTC.!"&)D <"  ')E=:$C{8IG6$ <* !  ;)6#0#$J;2673#"&'3q+FIoKLnG*!-6N]_L+cEX#54&#"#"&=332632E?# p$L7#-H###53'2673#"&'3MHMq3@HoLLlG1/H6.N\^L%1\###53/54&#"#"&=332632MGMX? l&:B\5CH2 0'M58 \ ###53'73MHM==YH:3###53%7'73MHMB1d>|RH7#^9'39!W se##3MHd8b#53"&533265232>32#"'732654'"##".5732654'.#'2>54&#"'4>{RR}zFR96];P5" C7ECRPB:* %!+Q#(1G( /3(S<29{Q)(.%$>3' 8*YLTqqT6FE7ZAF& 2;2o\Ys1G0@/ .- -@!3HK2!/"3 G0 ()  (.2 6#5D6*2!5!&3,M2.#"676322(1(! 0!R +1$&;<>32&#"!0 !(1(A;&$1+ jJ!!72673#"&'3t+FIoKLnG*!F-6N]_L+e'^9h e'^: e'^x;e'^m@hce'^mEhe&F^Ee&O^Oh,e'^ESDeH%"327#"32>7#".547&5467'#5'%&'"'>325!5!!7'432+ML>88O#7( &)B=<& +4)H+b"+$o?|B-&A-=8:% <*7#5$&>/C )=ϛ9lM7 S]-HH5J* -eZ.54632675!5!#32>7#"32>7#".'47&54>54&#"#54&#"pg0,?rV=O,Jhr4="00",1 '*H="'*E*4)";"10"3$%7G,"2=>SE\61`HHd L3*G-'+/ <* " =)1 8$&>$>-+6!$(%DR"0x@f77.5463267327#".54>54'"#54&'"J& /WA.=6,= %.%"$0&'9 '.'K'6%#.a/>4E*'$ #B(.#, # 9&34"3>'v:SfO7.54632>32327#"32>7#".'47&54>54'"#54&'"J& 0XA0= /&2'/'$#$$  + !8&.,'.'K'6%#.a/B(J*."?14 $,  .+*/;'34"3>'s'C;eE&P{Z] #"&546322654&#"i]^fi[\j:CB;6E@'aigc`kmG>:DF<>Cqe 2##"&'463254&#"M]I#9J\i8!a?"$5egRX7YFI^,/\'26Xe>32'#"'472>74&'"X}/Z}#72<eB;/0SN?5^#.qU4W7 +J4/q97D/de3%4&+'3254'"'>32'&545463226b6A,kpfO-'%66!@&W?P7osB/B1?KMDB  ;(T!"8!HX t0>*ENe#,#"&547.5467>74.'3254k_GE[eZD-?(d,CJX$9X]eDUiWI@OOCHW?L3 B $=]+L ) FKK9ef2#'#"&547327.54'%XML-5Og:D E.- ?I*n[_m"f0G=8^ee0;#"3274'432'"#"&'47.54632&#"V$HZ/@@0 @!'8C8" RjV43_BH!)G(+DIH)(7 V(1'#_K`2K(74&#"3L#fP +! $ WR"+ 2'^/e%7,H_ *  1BY%%/Y*#jQ) 2#"&54>32654&#"1I49:" (,!))A42F0 #n+",!E#53x ;'g)e'^7'^j'^;@,e 4'#5!##5#"&'326327'5#9,HJ=Q`$)5<6#]<2p)4)II5xd ,!,L+,8e463!5!5!#3!5!##"*JY8GG B(%IIIIX e&%4.'5!5!5!#3!5!#".'7326v*GGA.#T97b@% ;>b-#46 uIIII*)9PCHnX%*-g'Xe>32#52674&'"X}/Z}&;?HOmN?5^#.qU3Z<&{A7D/ce/46;5!5!##32>23!53.'73254#""&1>cY6.>.,)P0\,34P_-q H6yy"!`II1= I P/II#hD."FX7p"e(327'67!5!#3!5!5".54672&'"s7.#$ HH[A=5L#YF G8+!,6vI IIIIq/)<0Eg Px#"&'3274632#".: @#,M0& !8GVN#h " +( 3254&#"'7#"&54632V&/"2*$;(1C9%5D&)!6*_1)@42&C"0 !-72654&#"7#"&54632'2654&#"7#"&54632 3%k4&(AA"D_ 3!o,.90@#(8c80$?3;'1080FB,$4@.%!5!##5'#".'732654&#"'632>G3R4C;aA* 3{F(7?()/S<^2 [?33\~I:bi:=-'N< ek={6&'##5'#".'732654&#"'632!5!533##4-3R4C;aA* 3{F(7?()/S<^>   3JJ3:\~I:bi:=-'N< ek9 [?3 M3 >3!5!654.#"#"&54732632'&/32654&#"'>32nX $l6J0 42Q9HR@o'";+*9(EA4*?&547#"'73274&#"'4632>3'23!5!4+"&=33G)..48+5G"3'!=513>(.^J(c>.]:E1+$hkUUkr:d F;#%d1[0N +.N)#,5=33?,J23!32?67#".'32>54'#"&=#5!6=4&#"5473V-BS' 'fP9^8)32R.(< "  7.>f- 7W6E3)2 (/!ZoD_|V'!jwU#3)*%( +-3 f01@/C23#327#".'&'32>54'#"&=!5!4&#"#"&547326h.DU2@S'AA#QE3313-$B'2"<>)%-TSR- ==T7H3"p9u2J%VL^R#/ >*4S:3.8.21X092675333##54&'#5&/67.'#"&547^O>393TT3 632$eo  L&*'"$pC; #8"6#'%y\3j;D+t`N8H4 21& G/3$ t5y;2>54.'&547#"'7>54.#"&54>:F55&8,7 F.6R^R6d,* , %* &-#gK>!+;%4 2J.1&6($*R8Mi*1+ %'/!9-#;.2#.#"#"&547326324&#"327#"&546(23GG ORR,BI>F, #;3O9H_5A@1:'+Sz %/9#LYG#4.#"#"&547326324#"327#"&54632654/&5467LW3 >.DTQ,2BTD.+ #7# %\Z%*IhiIW((/G_A5@1/1+T#x"+'8(0:&9(D$)!*"I>2#".'732654''254&#"&54632/ "2)(H75%)3 5Q]L#".'732654''2654#"&54632654.'&547QP5,"2)(H75%)3354&##5.'7z86H0/")( [63 Eu6")C`*.33.&N4#B%'6MfXOMFX/+33##5./>4'"&54732677W$Ld3443:3:*4(%1( &'=5FaV3R,1-45)*%/DJ+{+2533##&#"32632'>54#"#"&5469M.3443V^!F O",&"'*I N  W15HM3`7#)"5"% O %1^.%27#'47#5!##5./7#"&& !&')At533JN &H $ #a133AAZ (R8  8B>32#"'326?#".'732654'#"547&'74&#"32 I4 '.',69(#)ZJ>g@&3"5R0<3% =!o#>(<$UE$) W<P-JcM|B @ykAE.0,"4!W>1 .72>54.'&'#5!!2#{#bDLgH"L6: G5Yi 33=.(L./:.#532654&'#"&=#5!!22654'#"'%8/$22D"9rGWw%.\30%[ML  C1  ;) %> $@t3\2&?^.33! (/K&Y.@7".547#5!!327&547".'32?#"&'&5332654'(3 KYK9<10 #BP2$*%,SAH3(4&"@(2.a1337'!kmHTjN]X !!# B!-"')!3_x+FS}A) 6'$&3$675!5!33##5.'#5.#7p_ 63JJ3(3+@Y+)Ca,+3AX3=W8?-KV1:CY#"&'#4&#"#"547326324&#"327#".5463267>32'"2654&#"532654&0g,[3EG g!}/69`:, @*  "#! *7%!/,=j7_-X83%'/(240gCI:E9 C<9*#S )5. $ $>Ab!>-4lxE49!,3%F'223!32654#"'632#"&5#5!654'".5473.I;d&!"-:c4,'E<[ #,!*a7>54'#53&5472654'/2CfHFV2 P6&):4I<"G0,N73k*EFHZD N=%3]G54#"'62,'M 35% *>.9d?'33q 6Y1&"!=8[?#327#"&54>32533##4&#"r, )F>)3& 3553Y$5@?3^+1:('c3W+w=&U.+/&54>32#".'332654&#"%5!, 8&KcfS5+# 3  $2BLF57va '" '{y 1&@)I%%7;X/:\IFa!w33033##5./67654&#"32727+"&54624\%jI3QQ3";,C&,n'B7) ? (V~Q8P)YX3I% 58<"*$3- 2DJ.'#5!!67&54Mc3Og,./j2"33!PO_ Lcb-5%"753##5./7&54632&#yR3GkMp^),*' yU-J1& DJ3VGK)[F=8Y325!5!##5.#"c4#4,b333B$&3"&D/F"f33BE--,  s&>32533##.''654&#"72767&#" n6F4==4* $%.% # 3#43B1PE3:H "&!$ 9 ,+2&) S.(3254&##5./67&'#5!!32#"' I<38b($f4R9>(69vKZr7 #1?ZFA_ %M#6@33*8&1,'aOaP'% .%.#'675!5!##*M!~/3%Y*6b++33IX,G5.,0#".'32654'#"&54632&#"326%5!%`K9bC:"9%T,P"5R3O)4! K%3|5 #!9 @s1Jm_:Q?3Z7?#00:233.!.'5#5!##54&#"#"&5475>N)33`#B2U)D>=79.&!"33C+P,],82N.67.+5!##5.')9Ye 633)9?Y7?J"1#9CE33LCR F7'2j."675!5!##5.#754632#"&23*@Z+09twF&A^6&33Y8?., \'".&3"&546326325!5!##.#"4&#"V+8>FG;1.+&k33 3%'9#.%&;0S>@R0+ 33!JL0)56#"'73>54&#"563263533##4&#"327#" 4$E5N3::3Q$!, <X&)-1=8k_  $W3^s/6 +.&'#5!##5./6&'5-~:3BdE*I;0|UL#A33@]G:%3(?+7qc{.(6325.'5#5!##5.#"#"'7326s@5"'2 B_$53 :'30A,-84EK7"33N4?4j" E.#2&/32>54&#"&546'5!6lY&_:&: M$B0dY:o2[p!H9!(# $<"%*9+K33_ 4632#"&"i2".$5!&/32>54&#"&54635-AY&_:&: M$B0d/3NK+o2[p!H9!(# $<"%*9+K{.+532533##L)Rb3BB3.38M33###53'>32&#"BIC3G9* DXV#BUe33:IdNJ&Ww)$23###53.#"'67."&546ц AA3@@f,[o -@Hi)m33Rv$ *!$%H*iL"'2654'7&'#"&5463267&#"0 *:()(0M9< */)?/*8BA9/"9S @$463'7&#"327'#"&B1. !+1( )3&0b)5?"!#&r/%)?+dsj2QG~8Q%) 7'?'/+dZi!j2QG~8l@~8/.7267#"&5467#5! CL^O<&[rD3  :fK$31n5qL,7467#534&+"&47326323#3267#".(GA-Y)*3,2 K@<G>=0 C0H"Q/342H #6M3j@~  :9TD6{'5J4'I/Z'7+{({ 13"'654.'&54632#".'732654+9 &-0 J[g7_G:OC# & /8- Li 4 ,#H/5.PBTA4(84'  >426323###5374&"#"&547c)B&GRR3VV3(B`T*4(,33$63!,7).1<%"&=#5!!>7#".'32>54' 4632#"& %) S(4wY=a9'3Q)=#   * 6(33[%p"YoIg]$$6-2(   4 a.(3"&5#5!!32654#"'6324632#".#4C9e"(Hv!- /733h0C# "mP_-!. '!##5&/674632#"&eW83C,=,E$y/(" !%&]23Lp/8B*5j "fX '&R:2675333##54&''75&/67.'#"&547HpCD960O>393NN37ds42$eo  L&8!$/3#(8,8"6#'%y\3i=C+GT*8jU`N8H4 2!$& tyu2>54'.'#"'7>54.#"&54>32>54.'&547#"'732654&#"&5465,".]," #,* , %) &-# :F55&8,7 F.6R^R6BCT G0 .8(!*4F2!$19"  *1+ &&0!9-K>!+;%4 2J.1&6($*R83W )$8 3 %1   #&$,32654.5467#"'732654&#"&54632R=2GG2 LZL/D7#!+45,$/",( I5'9 &-  #("2'fAA 4&#"326'2#"&54>pA4>I@4O<8DR^IXncK*8. Ml,)%#"&546332654.'&547ls=.A?1   19O+$==* 1EE1Mc09/ M=4/!1 40 +0=b1#%#'&/&/33254./&'7^2a"HC39@!* R'u  H:''*"6E@~$@;#''=.V  *!&#-?,'2#"73>54&#"&54>&/;1!)BD$a3+mG$6O>$1: 3 #4X7A^/z C!*4#Dz%1= &,9C 262654'7>=&#"#"&5467.54>32\>s(>h%:r"B+%3~aGER'D@=&7/43(J/#C.@ $:O0", *1b8JL?'13M/%8 8&+61%23&5467."&546?6?&'>32#".'732654 Z #+) .! cI>jF+3=o@1BL2228* #3-(8.!(! A*ZwEo? Hm]E?`0;6363637&54&#"+"./57"#"'4632375' 89/"NF 6 l6(,x&CWL),*&v=g 4X:7 $  0"32>54&%#"#"'4'76326323;2?*-,;3N#dJ/%,$/-9/'   18#@>/#  JOg3"# >4%>5.'.547?654&#"3"&54632H('E6Q &1$&\xwR:)&:?G;7Kp I4%  - " *#/=pN` 3 0=+!7.258El: . '.+'675!5!##*9V$ %9!~A3@>e X,X.6b++33.!7.'77.+'675!5!##`:,n69!~A3H''%&&Q . I7 .6b++33$X,ba %.#529{M]M/b3=aOIf$ 3#2654'7#"&=#5354'80AA/F-,4_D99VV1$19F3Z/&R*."'<=sJXB3KNba 72673#w63/M]MOa=(K&$ %>75>54.#"&54>32%< 3@" '%*8(1[,Z :3+$'$. 4)34P: <1732>73#"&5463232654&#"&54>32#'"@8K}S5 3AR-2BY., " .9(7, ,!L;h7H# V]nѫ"D8#3(#3'(?=8N/' 73#"54#62i %*L9TG&7N#V.(7"&=4#563232>323#"&=4#"l + 2" *1&3 N?M?)" 34)51e+%72654.#"".=4>321K)! 2,# 1G$#G/98#`P(#3 $ "3 3 554<)"C.Qdt $326?63"#".'#"546326.,O+&?43473#"&53%/YHJX 7,( RlkS%0 wX 4632"&00!H $!&000'(m+82#"&54>4&#"3262#"&54>4&#"3262GC84F.e2&$ "!)C #+C86D 6k.  )("mD86DD8 ${&"3 ( 6%5CC8 #,| ) #'+h>26?53###"&54>5&'#"&54>54+5326766+;džAN/7 '$+$BJ;& G8\>]P "3[9'Ai JE>&9)H+&WI]7/$;"A /X8DaPB26?5!#####"&54>5&'#"&54>54+532>6+;@AN/7 '$+$BJ;& G8\YWP "3[9'AFi JE>&9)H+&WI]7/$;"A /X8 aJt05.#"!##"32>2#"&'67&'###53546323598G4׈iYK$>! c6\w@}}kc43 q-{L:bA!f# (asINagn@A:g)Q783567&'#5!4&#"#4>323####"32>2#"&-{MwZ2JC*@#B/am@_3LcPK$>! d/uA|T[gnAf>H&@;"g:A@>/!f# 'co@"@&"@'"?(h")X/@3>7%!2#"!##".5#54&#"#326} xJ[J0C]=Z5%biMFu'L54W?2! DY- +Z= '9S26F@Q0NCA41 *H8*AXo#Dk64 +EC]Ic>+A 8[0OG(5h"+%87"&54632>7!5!##&''327.#"Pny]cn-p! C A3 .% jZ#  7H7rSXkyEAA!-%3,3M:3"UD'9 $ J#%2675!4.'#532!53##5#"'763%T )@E ikY ȇABcL%0@Z=1 AA54'#53232>54&54632'*ʉA$0EA 3:p <>2SDHJ&)4)! A"Bd#'KANE.J3A'+c 'TZz.}'074>327654&#!5#5!!32#&'#"&7327&#"<>*>\4+B:M/E"_6,E")Y^D]@;*L;KHY+)265-"AAv/.NM1mH4SL8(?L!4632!5!5!##".'#"5!32>F3#<’pZ1MG*9#]O!;1S.?6AAsOd?zY~~f3&.74>3!5!5!#!"632#".54>3&>54&'#;$ 6 -t+o&tnrn,  "54&#"#"&5467'32>7;4=q86:;kb 2 ; (K'' &% )3!W- g\'"'(l*";%=KAAATW2OQ? &8, - 3M  KA!4%2;5&'#5!#!"3!#327#"&'.'".54[ K l>2O+#! ;+)X  @=;xx7AAA$,@ )70  h%#"&'6%5!5!#"32>32zM>k<1&,SAOK#)/#>"! euC!AA )G/3L )%4&#"32>".54>75!5!!wWPu2K, \VT3/[<4(5,0A1 4$".4>32!5!5!#!632#"'&'#"254#"77%!(%EaG

h4:}YK"3# $ u(7AAA!1X-mL37:K}9@(,.76325.'#5!!&'#.#"32632#".&Z<; I3 QEG2$i!HGU Bn0 PiW0W AA#W < @5*P7 3-"N*#".4232654&#"#"543232765!5! 69x$2/6f(H@;mZ*LM?l") @Mq.?e(>% .5?=>8B5/AA"7>54'#5!##5#"!5275!0% `AXeb !Jh >,(,>AA=G!.R=,Q"4632!5!5!#!32632#".5#"[+#Si(~ )Z5Id6H>5RJAAoz0 .+*QdE7>54'#5!##5#"3270 g@Y_b >YEhB-+2AA=3.5fKQ<\)".54675!5!!5>54&&Ay!3%,.PR  3 wm]H1=A.B/[AAY+" A=1H]#732>73##5#".54654&+532vOW75͍@Lb(X@)L%*-H AE((  '7!5!5!#!">3274#"26wEl1#B2% *)A$?Ta!W+ZQ>90MO%;tAA>&!1 &G8;IB1)+*}!*!#5#".'>7.'#5!#!352675#Az&Q^ %5!I?>0m 9_Vv3E32/ (1AA?S-.;oJz.8!".543232>54'#".54>3265!5!#3267&#"*pP6`A(; -hi); $K4^HK-p^y!)1I,=Ij+!1'=*a-'+,JPaAA]:!WqO!&(<74>54&+532!3##!#"+"[giʊ@&)5YD?o?WEAB)Av"4'#5!####"&'76732=35!f `?''D'@9g+5s RAAi$5 m*O|Lq#K_x".54;5!5!#'26=#"t;eH~K7$LZ]i=lAAeYM?0&(=bb&*%2654&##'"3"&5467'#5!##!BZS>]\?T%/! \}iINMGj|_Q|{;[AGY^B%; >}][p AA qZXD 157#"&546322654&##'"3"&5467'#5!##!R3BZT>\\?T%/! \~jHMNHj|_Q{|*!& [AFZ^B%; >~\Zq AA sYXD+!5!5!#!"3!#327#".'.54> ?$'+N +6%8 ;*)H/"+)=xAA.0@.= 1*?5 4% #)%74>4'#5!##!#"&!54632#"&3/.$sA  )z$!Z]wpAAEtv!74>4'#5!##!#"&!53/.$sA  )z$Z]wpAAEtvt 325!5!##".54>;#"v!6U.]I~01;^ 7$1(LDG3`AAc.D"LF #*@]d 2#"&54}&2%57##5!#@dBFAA.#"3###535463298G4@}}kc43 L:_A@A:g*P6"B23###534&#"#4>;am@\\2JC*@#Cg:A@Af>H#@<$@h0#"'5327  Y_cUX^ (A('h0#"'5327#".'5327  Y_cUX^iY$G%cUX^ (A('' A(' z2#".'&#"547=}!h&=!4Z(J&x)$*3!#'|&4632.#"&5432.#"+#4hL=]j&%T*)%(")("2;#M:T\HP &  @2 !m".1&#"#".5432  -U $) 1++/-#4 9)#".54&#"&547232632#"'67&#"2] $I/a3E& C!+2+ 5/)?4&5$&K, 53& #?1&#il8# 10  #.'7; #?1&#i8# 10  $.%2675!4.'#532!53##5#"&'76#"546323%\*q  ik0' ɈAY"=" !32 /^) A[\A33X[0  !*7#"&54632746;5!5!####".73275#"_\Hv@7!5!#<^TA8,h32#"&547#".7"32>54 0W8eh  8M5w-I+ FW#4)29',3%e]')E8/aRb?(0$?2. ,p+#463 #"./&543232>54#"#"i C^!>;h2!^H*l{\%t4;7[5~H2=DJP1+2)[-3?YI.,t9 $ !*))+532654'5".54>4.5474&#"32SZ]`Uj"+GGJGG,!jUNPQN5ZX7Z@"%? ! 5%.C!!C.#3 "@%"@m<..<`3273#5#".54654&+532*$WOMe@@Lc7nCL%*=/<(8HFE&9'5&'&54632&54632#"&#"yH; ]#<`VJ9p1!AO5mY%47CHK @ -(/("=$7@0 G/V" ^F#7" q BO D DB )$< G,2#"54?>54&#"#"546Qi *"9(E*&u8:FhK*kJ66  ,z_!;4,*!$"4-^GN-C`+" 3=YX"4632!!32632#".547#"V*M-IS%/k4dmQ4@2TG@"6`jK1 #=]c j74632!&'.54632#"&#"#!32632#".5#"V* @KG @ !-.#`#K_%/kcR1@2TG%) 0H (*7 ZxB #>^wH!2#"54654'&#"#"&546(; $ ()=1M'%+&C!1#<I 5B#"&532>7 YHIY 8J7 UilR$1 4!&767&'#5!##"32>2#"&35.wZg75F;8L$>! a0nD-{VZgnAA . e# 'boќ *0<.#"#4>323##"&/47#532674&##326@^v7R/ @ &=kFTv8cbLEt'L4Ozy29Z,kaA6Fjn+=5%EO9&5ZW2AZn"Dk65 в$AAQb7[5 Nfrx2#".#"#.#"!# #".'&5#53>32>#"&547#"&54632'2>54#"%3>74.##32>sI, %4\;@z  #N80$ )  !fTBhC1// {yM" ) +\FMOdhihW,4$@tu4.?,^>+ ,;:) $($A! "! EkI]+:iN>: A]ELw7 AMD$&   t}Hh  2#"&56#".'3327w%( #1X98X2$ oxi!.C1$#0"aa< 2#"&546y"*+!5*.0%'"+-N 2#"&5462#"&54  )!+5 B)4"*$!')("+&#+*%2>=3353###"'#532654+532# (/ kyssyi8%I9C$A*? 5"xDPcW( BRUD%, u1#32>=3353###"'#532654+532#pp[)/ jyssvi8%I9C$A*;,X 5"xDPcW( BRUD%,  $)%*726546;#"#"'47&546;#";#"(J8&d)_FCOX7!UL/I#,  ?0W CU>1!Y8GS6'X.! /246;#"#"'4>3&546;#";3265f4!c*&z^;PX!?UL#  8'-F1>5H2/ES'S7IS1?F $;<"3674&+'32654&+532#".5467.44@F1Z[('d3B> p>_5! |X42$= Sd B0AM*'V( !SK7k GX(6)Kq,D;346 #4&#"32674+'325&+532##"&5*pj'IH+P;8JHXYIHd3C_olme 3l!B-#@-'OSK=SK7$7 eJUfP62#"'732654.'#5'74&'"'4>327537&54S.L6fEX29#(<)1sNR#*O?*Y08]+AsP9OAB@OT12. 0%:OܠMd"c @Bdq&": IE1%3267#"&54>54&#"#54'"#54632632D& .&DZ-6--[G? SH8S'5OQG(0(K4 G XP;=XE5U+/'30F'8F'=F'1G '9G'9G B.4>32&#"3%#".'732654&#'7"&G"9; M^ 6?f(  Ն-*)(^b-6KXb. P/% 'P8Y  D;/%*G3UH 232=3353###"&=&+7~!1>Ckkggi8J,Lg:+2=;g9JV?TJ)#34.#"5632#"./4'7326Jhh 3#L->vn /Q32"WE'9;-)( X ls.9. # AM9'732673#5#"&547.54632&#";#"n3nYg-,,L~}K 9?l.[JC*9'TNY.:747"&54>;#";#"27#"'4672#".32654&#"X#G(! J<)I&(?TA44 ~PxRp'"%'}H/D3"4 S4#X.\}Of0dEP ;/#&51346327#"&5467.#"32>5#".WF*U>3L $2;49((" .     /:(EOa):>+R _7NJ=f $($'0 X #JS=653##".'532654&#"#"'532>7.#"763632u, 76 ".4*,;;W*@-%6 0*.B$9CH3Q=He0ZYD7G(7_JMW')=/BW@X*a#353###"&'732654.#"'6gxjooj $.V5,o )PBG2&L"=kQ&E/%;[ /LK-? [&4&#"7633267#".54>0+:1C-K%C?&0c9wX+NJ->YZ>f c9'.91(XF*]&>)P8*>!#*2&#"#".5467&5462654&'"$@-)E[>YY>-JM*@b8# X77p?*NGgIVYCGnO)$4/AR)@N+=,%1;&7C#732654.5472&#"#"&'X j2:N@\]@<4.BdA\\A/LN)b}G2&"+<.[<#E05N'X!$ 54>54#"76323274632>XY>l2C/Kqh=XW=@-H/{:)?#!3 gU8);!(-9K9_2Ed)#3%#"'53674&#"76"&53327dooG]$H1$%(M-# 1%srO;T@h+bN99"Pa+5RՆe:CT%\ ".5463753##"327;F5$wRqq9D . 1T6QrlSF;{X40#5#.5467>54&#"#&54>32326734p,z-oTZ.1/5-a3 :%1H$TL[BB:o.""bS .%&06 , "13HR 8F+%7"327#"&547"&54632&#";)')LJB[gN;t[=Y=+,4* TTXgVJ/;+?MUDP461#5#".547.547654';#"326734p3$85%N9-b-< Z)1).~LG9,o4v ?,A,H)L &!RM'G+N "72654#"'463!53###".>%!QK"$HDSqO5G>7".=4+5323673#56O$%[<%"_rrF7%6+T,/ 5 _C64>32&#"3%2#"'#'&'732654.''7&\ 553b!>9>& 6gfkKCWb"KB  Q,  J( 'YU.LQ~5A"6&   8d"726=3353###"&54>32&#".;osoos'AA"l!77,.9IA/T)7O'sy9S) Lu(".54>32353###"&54354&'2"-&RN3RT,8@,kooi\:1X)$O6+-BNr: A*: s@.]<#4&#"5632353#5##"&543s#%=jppj.$8+bQ, X rj&;&`+'#5#"&546;2654+532"&#"32673+qW\k"`+EX7I|*  .& 3uq/t9x_(;UT@(2:7265&+7!2#;!"&/#5/>lIe3fG(V-%MT\D7k TU$#5#535327#".54632&#"po"6*7E9't)0"53 51-SJW&9a32&#"3654Mhs %!"RAV~ ?+ B2(XVHl#4=[k[=l+QO1 R F_#5#".54672&#32673pVYB[,jY5-'64+Wp,>*GC&P`N/9k*'K1#3"27#"&54>32#.'&+532>74&Kpoh,&1(>P!79f{5$5 fX {%#.%U3!9173267'"&=4&+5353#54:QOUm ,qqV(".`6 T$J&2353###;#"/#532654&+5&0#mppS9 + &F10 6%-1a T1U,*%T3"&547&543.'"32#"'53254&#"327)O+?.-?E]2M`5<1%YYBAM$J3~NOYw0&SP"*4$>AQ0%.B;!9(6[(+#53ggOe #46;#"#"&54323254/&=8*$6e6 :%Mn 0p6g50>$,S3- +$_*#a47W/<#3qq,^4>3'.#"#,JM)~3_ #*FUpKl8SZ)  `N,2#4.#"#&56>@{Hp0 XdC1A5&'*"<#'2#"&'732654#"'6I][KGh<71J7'H'%78YI D$3*"327#"&54>;)7v2=.CaXx$, 6)\-Q*_H%: Q\#+ 46;#"327#"327#"&547&G:dWDU=*'(O$?!*1'(O^XDY9KH7D=*%=+V4/%(r #".'3327=#D&>22`-x)"-[=t#3` O'7#%7uh]Q"G_ #".'3327#3"F&=12a,y) oo.Z=f)#3#3_oo  '7#%7#3 ]P\MLoo:& 2#'32g !Js2#"&54#".'33674&#"#"'532?672#".'7326%3274&+73274&#"'63#".'* %? &5b>-K2*oM4v *#)5#Z-O)H<9$p]*" +R"4H`4a!"db;16b c3'#5'74&#"'4>327537&54S.QT56* e).6!!-#,rOU $U>.E#,O,AtS6<&8F, 8(!6_OݠMd!~c +2&I^J !: I*E%327#"32>7#"&547&54>54&#"#54'"#54632632J8*'(J$4!  '(O_LF**-[G? SH8S'5OQG))`;=* =+F3CN(.$OLU>XP;=XE.E&e"-327#"&54>54&#"#54'"#54632632d#1&4F#*##E80&@7+@*;t$-'*'$317(9g4&'e"3f+J>3267#"327#"&547&'4>54&#"#54'"#54632632W& %8&=I1 !#F61?7+> %@?6)'"'+"0%306(8g3&'8,#0"&5476;54.#"3265_B@X`=?# (78+'4_\EB@B\_&7 P9:JJ:*463227#"=.7"3>=4&hSNg+3,t/B #,.# #ANgI<#"d g (7+v.*.+]&7432654.#"'632327#"&5&XF7c =*=?Q0k ,7H2M^T[Q 'OiU  Z[J +#"'7326=4'732>54.#"'6Zr""d}]lk6OG-GG++51#9 cR="' lH`FL83(HV   P!""&547&/7'32654.#Ml}N`7[4$ M@b%"!&"L5]E_^Gp+"Lj.Ga-.%8?!%;#"&=#"&=4+5323273 "i!44E[P "$* 4nE TqT:T -"#!.47467.54632&'"327'"#'2654. B&.C`v+Y!P %o]o]fq-H@\]@3HA#ADN   Rb  +?-=NL#8*%".54632#"54732>"3674&j+JF)`CSf "6W9O @4+@ 0*#`, +)Q9HJiX=c]>%]%7 6)2&!,-3?h,'"&547%327_BI>0;AI+k5hVY4P1%1Qc)"%#53'"327#"&54632&>q(>5"#0.ns"0'Q[M7O&^toW RK B%2#"&5464&#"326%54&+'3254'"'632'&543226e0<.-0=As"%'!#%*g5A.lukU .3N`B&Y@R:qv7C.*J@,(5?-0=m342'/BKPDE4OCU$"8"JY x1 A@I?<(#"&547332>54.5473vucPPH-?!0::0P 19?2!x{b0310IZ%BG+4N.%#(C, %+!07Z<0%%#"&5467635#"&=3;4&#"3260ke=54X P KbPWHJk\KJchoqc:`N%nQGPJGJQM<F'3%#"&54>7654.#"#>24&#"326Fmi/(6!-%1,PdN'++P\KQreHPmj|~g#;*$ &/',T>blfM@,I0Ka^OH^X<-9#"'.#"#.#"53.546326324&#"326pXP8$$ ()&$$v(V47LfWY7=jWlP>:=E@<@>Ys/XNH+"#[\2"  P0LYNN~T=YZ>=UR<"%#"&547332654&#"#&54632o P QLJEG;DEPy^Zyo ))!Nf]LJ:?M@]e^W<&%#"&54733254ᒐ&#"#4632*+fPPH4D<7#"&546326324&#"264&"326dg{ -@B]oLGg 0CbX{r9)*=:Z5>PTSADPd~zg-<M'YAJ[YF#Fl)2:*-6>AGRBAX`P-*%#654#"#"&=46326324&"26-1P1\43<322VQW$G01F$1&P$3:dABd9-TR11RT-[YVV>n]77]n<%%#"&54733254.#53254'5++gPQG0# \ADMP0!RNOpeI88CHW#32<qr< aFc,\<oA#654&#"#54#"#54#"#&'&'&!"53&54>32632632o/Q0/0,2AXW@BURPQsa^w>AYeLmrZ>TZA@V\P(%#"&54632327#"&#"6324&#"326cgPgT;@ -va?e\PXA?VSD@WasyfCeIP[>z]AZS?EZVP"%#654#"#54&#"#&54326321P1\34P43\1P1X3/d^yb5I55Iby^(LL<.%#"&547332654.#52654.5473{\ePSCAH//(`:8QQ8P(=F=(S+(Zdkb#BMMA(1<0*"'B, '3?/KT.%#"&547332765#"&547332654'5673sij P RK5--8b7;J(B_PV"6/+Qi|k0)!Lh#$b9)8A? M! 0 < H<9%#654&#"#4654'&#"#&5467>7673632632ASD480"%P.29+$ZE{z PNi` -A*J=VWlk_Uo;U"#L  ' F!K47t*ml64#X(,#K2/+$OnP%#"5332654.'3bPDUP`pVD$8+);<2>J#".547332654&'#"&5467.546322'4&#"3264&#"326&@X`51^W@'OPOpuIB4e=Bc9,)3]EBV!&.1'%15+75'%/+<%#"&547332654&#"#54#"#"&547332>32632')o P QLIC -"*P 34;P*%( +Z>LZNQo ))!Nf^K]15."> A47 $>N><7C#".547332654.+532654&#"#"&46326324&#"326A8opDvWT hSXi-$<9/>>0&> zU[~ZeC?3N`I<5#654&#"#4654'&#"#.#"5&546326325I.79/! P%$'9=LCBz|R PLni4sAqh\b6;dQh,c_aO54&53 iPTLJEA^Nd#P#I2')Pxh*01N`]L:_MD@AH1:I4 6 ="-%#"&5463254#"#4&#"56326324&#"26=zgibDX?!P1#A6IO&-]ROPbGJ^a`zldfn=pI^`G/"Z3<OXPo"-.#"#4&#"632#"&546326324&#"26oB#.P!?XDbig{OR]-(MI6~^JGb`aW!0G``Gp=nfdlz2Tb<<3HPOFGPN<w#.'#"&547332=#"&54632537'5&#"32wpn P PMOM;YU;E[PpppBD%5/!I/xm+*)!Pd9@9:E=\GHhO4!"P%#"&537324&#"32^h|P^^~PQ?BVT@?\thwv~XQA@Z<<I%#"/#"&'332654&#"57'&#"&546327654+53232654'7/eCW5a 8t$'~ #L'78DMQ9MTVH+7>-.? 5{G.!E$?>AM_a+Fl&(c*) 4<7#"&547332654춮춮.'&53$!?)EJ(YPU>9S4]a66ad0;JX4PVV/9#"&54>7>4646<54&#"#&546324'326c]+922&'&)C \BF<I2FgBD[HaoiZ%B-&  $48')*AK^k%.98Gm5(AL[0=%#"&547.#"#.#"53&54>326324&#"326lZMg1 -&BR_XVS C^[-YYk#T?ZkcM7,-":6C[00%'7-C!FO:dGEEtXAY`=QR"#"&54733254&#"#&54632dbQl,F,H74>-9FdFKeKcjaP>=5E7I/@A:-4!&.FTUJ2#"&547332>54򂂮&#"#&54632RoFF9*:> '.8.5@FiQFg5.>(0cQ@8==9G&&@0&<(2C)-6;5PUND1J ]V$0%#"&547#"&54632632$4&#"324&#"326Vg^QpJ,A>QeIG^:2QLP9/.13*/ 9@B?D>?9`ziQf(T>HZUF=.{^=D0*9D]lF?Q^'%#>54&#""&546326324&#"326SF&-:7@(nrv_Q=8SQcC=GCCDE>s])t89P429\wu\_t==nS=RXIFYg%#654&"#&54632;F;@xA;F;mVWkpSNv?[\>tPUnWvv+#"&547332>54&+53254Ȕ!*, $L:Us F D<, '((A&C*BC2CUF9NI%cT@55@#654&#"#54&'"#54&#"3#&)53&54>32632632-.F.,(SF'+(-F"&a7M*CRTqN:L!%V#>%TATMUKX+Dbhg4-5)le)<1M1&:Q8bIII) I`'%#"&5463254#"#&546324'&#"32\]Zrm\K5urFiSP]F(&-@>A@x_ruZ\y? R_]PX!!`CB[*%#"'&54632327#"&#"6324&#"326k^F:=&%-A@^u01}OIZCn:w[B^V@d##](%#654&#"#4654.#"#&54632632I2K2:&%.3) $>0[%+.#"&5473325#"&547332654'53273WgSr,F,E9CX4V=;QE($!&,aK<>jp_R@;5E9G) 4;>B9$)("/H@1%#654&#"#54&#"#&54>53632632IXW>Q)%W;SR9B.&53R=@%97)+7:Q<#".547332654#"#54&#"#"&547332>32632Qqm;mL@cEQWK$$<#:%1=&F&#*(!T89#n+[=:/)@CNiSJR4&--+4+;0-$,%$))#@J9.:#"&54733254#52654&#""&54632632%4&#"326xfhA VIz9A//[kjmPf53aDMm76>65C?=;5=5E8H0$5u:N=6%#654&#"#4&'&#"#.#"53.546326324F446.$ <'<6jT%u#VGj]2{[9AfZ(C:dI[=QDF8O"3aq9 eAf 'b1C ;,H[{+$Ok$#"&5473326=#"&5473326753a`So,F,H7D>>BJ\3F39/%C=5E8HWG2ZJPHEO0F% $/%#"&5463254&#"#54&#"56326324&#"32dX\mjZQ4$", 1 1?),C='P>9F;@??@A?Z}v][v:"-5-<22:=F)??T@?]^BC]*%#654&#"#4632#6324&#">KFKA9-S |Gi6#E95/Z6%#"&5467332654&+532654&'&'.5473Z9.05lE9FAAx~Qk=;?B;<(*!F'77'-< P4jr£XJ[eSOC,y??@;9?B?u=7.2518,#:v[]v{\yAS??)B^]]\%/%'#"&54733265#"&5463237'&#"26wYmQl,F,D9H<3Z=TU@J?=5E9GSKKP=?NP|~TonoY6#&78%#"&537324&#"2mZRl@{7ZunRdw`F`M#"/"'5326549'.#"&546327>54+53232654'7H/@&c+8VHM0*5.$P )11J9M#T5}$=,] 33<[.:OqB*@F#=.+)#+I$6779LI< 5!':$H;%#"&547332654&+53254+53264.5473pVdtFCM6L $SREDST 6LL6AYI:'$*/&(-RKfcNM.4;'<5=<. 1( &/(+ 1$&/ <X=E%#"'&'5767632327654'&'53254'&'532654/54#"6XUC_NC12+V-J3@3 P4E7E6/XmT/;8H)1X+"l[6lSy-W@; 6 :+ 6)7@E5D8#>?*z3^EKDl&'567&5462'4&"6lWPQV1?iXXi=3A6`6ff 56jHZZHj63993Z..x!#"&5467327654#"'7#5!632 (rN_$ 'D5NY$!* 4r/=YL'6)/4A1"W4:#"&54732654&+532654.54632'654#"hXL^*!A1>FT<--%X5LL5NF"#A1g+ W(3##"&5476;5#"5476323#3'54#"35#"32OUFN^2/S\\0/MHSOOOdr$FNNE$=5dxGUXMO,(HM(&MEn4H|kaf>u@5;<D /FNs805w3!2#'32654&+5lpplR5'%#".+#!232673264&+)(=X, 5)p]-luE<<7 CKKCVG-:;:-ogHeaF65DxD&##5!7]<yRR,4##"&5332654.#""&4632675#5264&"0Dk@!ZLNNTF$-/S5?O9X~XX? 6u4&6&&6A~=]l9csX~]B-UJ-><9gZoonAf>>f= &"32654&3254&#"#5632+ &632|ttsN1 {=GL򥤄:O1NVӤ5#7#5]]]ii*?2732>54.'!53&54632&#"3##".'{)-A";5,nuz;N%m3=%di5oQ(KJ=c2,ADYaWo)Je)$!AI\&=<";)$$.732>54'7#".532'654&#"=E:CH:%*&A>#Ғ/VSE4-@~0'2G!1HqDBJ6T~L-KeU'D_6 0JS[zN525#"'#37&' pA@3/c^^?iiBI]{74>732?33#"& G&Lqo&U@Ud-#, {_OGf %!#3# Mcxhepx')L72653#"&=3@0]o_^m_87TM_nhY@/@D5@ !!!!!dLRRR "7###"&463233!$"32654i]) L42L )]=8###{{2A]\B2 -3"#32$#7#32>54&#"#4632^^&C@&\C3MTv`o3Xc~`%J5cdV>jwFi9.9"&473327!#"&547332>32!4&#";LO,f %=)7 327&54623267#53##"'7654."bl$<   ?0+/wggw/*2@6/ ehRLMM_bI 6) H   ,.KIAAb11c<_7$ $7_< !# #333ffhdhWPP 7232654.'&54>32#4&#"#".54&%1(BJ 2:"B+U[S,%Y(88(pq AL9&%*?"LI+.6Q*4 !2(]l5BR!D=CZ2` !5]<23#4632#52654&#"]c)MH+p\58S8CK#\q.W32#.#"32654&'#53gKwG/0GyL+_^Wj{m^aHSFF~}At3Ni`12bjO4NNO:A=QIo_(KA'5 !##33#bXԁXc{'c4>32#5#"&+532;4&"+HL*c]SU}%%YKH;W,q\ݖ`R$GZD@SB32654&%4&#"#"'#"&5467'63267#53#32>32"  439++"6 X'>F9>)PW-*b67J C 36(!#4!M\k'2P) -CZ"d*%,%R,.#6D\-hBHf7P>j_pAA=R&/&ZB463233###"&"326&x^^vMt|~qr}}aѰ;'LУ5f !#3!3# ]]w]]L;'##"&'332654&#"'675#5E0Yh}k^pTkENMDMH'V4Auk`~j]bo<,GA 3#32?33#"&=# C&Lqo(S@U8T8 {_OG4J #"&632.#"!26^pm ԌaVԐ}|Fu~!535.=#53533#353¡@@]8HA]F<[sFF09 $326=#5!#'#"&54>32#.#"{$9^>-b   [ ";3)''*9g)h $0=, M+2 Y532#54+#f]]VҢ; !!5!59LfTRR5R !#53>32#5#".'#53#;4&#"2 OZ4c]3YN 3.mKCpGAA>Pq\ݲL32#"'#"&54>7327&546zDUKLJ4_'CI)ClE./`DbDAUqx+$BI3)-=ED]?E?jg;44z{'C2I8$GZ&6K'h*94632632#"&'732654&#""&547&#"72>54`HC<"7> BQ/.WHHb!4@  ԉ11mXZd.18#%51C}z`_`_=^8# #8^=r1!##"&'732653332>32'7>54#"#"'>d#[CZA5(#*d¶c0 3?,  1 2?: EkNB#18hMeY23h!3E  32& ?.#"324&#"#4>32#"'#"&54>7327&54632GN#*e;LJ4_'CI)ClE./`D`H@Tqx+$BI1'H8(P3-"?jg;22z{'C2I8$GZ",=no9$>6&#";#"32654#"#5632#".5467.5432x]A8LBa$O8]ID:2)>[*DE0Ag>(E=81/GPg,IRf0.3 ,+Q @3+=%"3D=?l85„2b 3#"&53!!3265]]veUXdnoFOOTJl*2>32#"&'"&463275332654&#"264&"2Kew +W~XX?2N1_7'Yo{;}#&6&&6l@D̓sjZmonG>t"0H-"26f>>f== 33##!%5=]]=_zR R5U %#!33232654&+Ud]=@RQAcxfR=@P"&533273632#4#"#A:I0,^'A:I0*^'LLNnLLN =G32654&7"&54>7.54632>32#52654&#"'654&#"|13($Uz.<S`Mi.*@78G9B@ 6M\XE ,++:KR(02@3'#<&* ^6HftR0L6"-R3;UV>ZB;SQ"*1<@!*!%J5F!4&'5254&+#!2f?8KC]-luOCw _:cnicdd)@i' 45732>54.546323!53&#"3##".'p)-A".JYYJ.uzPq7m$W3=%di5oQ(KJ=c$=-,-3H*aWXFAAPe)$!AI\&=<";)52#"&5##5!3264&\p"54.54632&#"#".'p)-A".JYYJ.uz;N%m>]m]>5oQ(KJ=c$=-,-3H*aWo)Je =-:9X3&=<";)#C,7>54&#"'654&#"#".54>32632%32654&./4*/_W K,X0Uv2pY;X+>nI@dEXW^VM<.+E` A)-CD*?4T `LsbZP|I21clLHvKglTY3<E"654#"&54>7&54632#"&547'32654%3254[232DRJFY($B'MQQ>?QQM'B$(YFJRDiS<)3.)<>&0/'>RFaiFJ<8+77 J>DNND>J 77+8<JFiaFRONH><6,66,6<>H2"!!4632#54&@0h;o_^m_8TM\R_nhY@/@D5!!h;yR4>32#.#"32673!".0GyL+_^Wj{mZ``!KwG/d2bjO4NNej3Ni`&#"&=&5463233%54#"#3265ifbPC=P]9"],+N~]piXMbW;eWNZ5,?WyXI(72#'2654&#""&547&#"3#53>3262>54Jbl\@&'=2+/wggw/*2@6/ ehRLMM_jSUg\7(84KIAAb11c<_7$ $7_<5C +#!23264&+Cq[]-luOCKKC]qoDxD'4>323#53.#"32654'7#".-EvLqVUOirmC_6FsKwG/d2akO4jnTTDBD,"O?NOq3Ni` 3##5!#3kLM`[gTB'5 3#3%632'>54&#"#^^V'%*/ 3-'$^ :')1E  ( 5x #33 #]]kx,nqPv %5#"32>+"&54>;@RS?+@ ]bb/Ta:j֛ZA>\ 30ddA_4&%'#".4>3277'4&#"32657R2AIvD##DvIA2RH/wRFnV_qq_VnFRw/ySKvzvKSyoys@}}@sy+327#"&'#"&54>32#.#"326=0?,82FVh+?2KP}_qSt3iJSLMTF \Q27ө0ajP5zoFQ2_b<^1Y,8I'7327&5463232>7.54632#"'#"&4654&#">54.#"WO1!dHQUb<28>"BK9 sWFBcD8cwGcO><`{!!.%-   MYuóO@!5sRui:ei243,w^4/;C4;<_7$ #8^= ,#"&632654&#"323%3267#"&/.#j vNw2 -& tIr"";# aV23NF%!;*/F>2/o $-7%2>54&#"#4>32#".532'654&#":];'/# R+1#Q])R_/VSE4-@=E:~0';+E_b4`8MBX-ŋQ~K-KeU'D_6D2G!0JS[zNE"73267.54632#"&'%654&#"8"c7CetnuJId+PLU,/"#8KFIfT"WcSVdY>EecW>;b$" "%2654&#"632#"&54632#4&#"'QP\B;b V^6j~~xsjXBI7TFEU@4^bH:}dmwa:P B'777'324&#"#4>32#"'#"&54>7327&547' A4434+_'UKLJ4_'CI)ClE./`DbDAUqx+$BI3)MhQQQQ266D]?E?jg;44z{'C2I8$GZ&6K.:T 53527&'765462#"'73254+5254&#"'2*!hp!4@rgT:k=D<>F)3X75#50Qt2ZLNNTF$-/S5%U@.UXE1*J4A gJcsX~]B-UJ-t' h"A24&#"32#54+#4632AER7f]]b`F>E\0VҢ\qkVO*277'.#"326=#5!#'#"&54>lKnCRH/wRW`Qr}$9^54&+Uva(=Z-eF?}/1>D?^r$;;p/T+49E2{ %!FI\T&2{}&7J2{% !TT2{v'[l727-{Tr2'[[92'[['[[92'~o)92'[92 P{TNN&m2'[[>2{&[52{'[52{&[72{'[72{v&[82{v'[82@&[92@&9[2@N'['9[2@N'[U&9[23'['[n92@'>[2@&>[2@N&['[F>2@N'['[>2/'[q'[>2#"'#"&=326=326=3B.3""3.B6"0"6"0"6.B''B.""""23*@&[J2'!5VU4.2#;.23#28;2* "&53265ggF>X>IggI,>>,2* 462#4&"2ggF>X>*IggI,>>,2 2#5264IggI,>>, ggF>X>2 2#"&46264&#"GhhGIggI*??*,>> hhgg>X>>X>2'SS2'~~23462"2"0""0U0""0"2^5!2,;;2^ 33##5#53;yy;xxy;xx;2^##5#5^y;x;;2_'TH(52{'T^(72'T92'T(>2{ 3 {\[_FF&62{}&eJ2{3 # 2_&62{v'[je2 5 2r1Z2'[[g2&g'[[[[2&g~o)2'[g2!  r1mmZ2'[[l2{&[c2{'[c2{&[e2{'[e2{v'[kp2{v'[jq2&[g2@'[g2N&[&g[[2@N&g'[[[2@&[l2'[l2@N&x[F2N&y[2'[lk'[kl2T-T"5vw2m3#2;;2m4&#"#3632#! &66 4&86#0(m+0!2"&53265TJvvJS}}S2}'J623462#4&"2TvvJS}}SJ2v'[2!2#!5!264&#!2AAbbT2v'[2v'[m'[2'~d)2'[2)"&63!!"3!AbbA.T2v&[92`&[2^'[2^'[2^&[2^v'['[2^v'[ 2&[2b&[2v'[2bv'[2b'[2&[2bv&['[2v'[&[2a'&[[a2m#"&46;#"3EaaE,55,FuvFOlN2%'~2%'~2%'~2%&~2#"&54632#4&#";bebR^FE_`DlARPxS :nB45J2}&J2532654&#"#4632#lD`_EF^RbebARJ54Bn: SxPR2v'[2#"32653#"&5463lD`_EF^RbebRJ54Bn: SxPR20v'[2x'['[!232#"&5332654&+bebR^FE_`DlRPxS :nB45J2)v'[ 2&[~2'[$2&[2&[2v'[h2v'[2&[~2'[#2v'[2v'[2&[2'[2v'[2v'[2&[&[a2l732#"&533264&++R83H8# #//#+Lp6:7(#42l7#"326=3#"&543+#//# #8H38RL84#(7:6p2'~%2'~%2'~%2Y'~24632#4&#"#2ebR^FE_RPxS :nB422}&J2#54&#"#4632R_EF^Rbe224Bn: Sx2v'[275332653#"&2R_EF^Rbe224Bn: Sx20v'[2x'['[!2%#"&53326=3ebR^FE_RPxS :nB422.v'[2.&[2'[#2&[2.'[2v'[g'[2.v'[&[2/&[2'[#2Av'[&[2v'[&[#2&[2/'[2v'['[2?v'[&[2&[&[a2l7#"&5332657R83H8# #/6:7(#2l73326=3#"&28/# #8H38R#(7:2#!5Rf&R2}&J2!!#2fRRx2v'[2)5!3RR20v'[2x'['[!233!2RxR2.v'[2&[2'[#2&[2'[x2v&[2v'[x&[2&[2'[#20v'[&[2v'['[#2&[2'[x2v'[&[2.v'['[2&[&[a2m(332;mm:2p "&463"3IggI,>>,pggF>X>2m(#533(;m:32m4>54#"#63232%56%B4r2@3>3m(A+&/7{:.&F).2234>54&#"#632!2)AOPA)P< Xe~3NYN3\;cE<89L,9It\4fKK8;P2332654&#!5!2#"&5RJ54Bn:~SxPRblD`_EF^Rbeb2&J2#"&5463!!"326=RPxS~:nB45JbbebR^FE_`Dl2&[2754632#!5!2654&#"RPxSj:nB45JbebR^FE_`Dl2&[d2&'[[2%#54&#"3!!"&54632RJ54Bn:jSxPRlD`_EF^Rbeb2'[22&[V2 '[dV2 &[Et2'[E2 '[2'[2'['[&2lq#54&";!"&5463284#(7:6p+#//# #8H38R2m26=3"&=hHdH6ggڽ2HH2IggI2lq5432#!532654&"p6:7(#4+R83H8# #//#+2!#532654&#!5!2224Bn:~SxR_EF^Rbe2& J23"&5463!!";PxS~:nB42ebR^FE_R2& [22#!5!2654&+5PxSj:nB42ebR^FE_R2'[2 2'['[ 23#"3!!"&546224Bn:jSxR_EF^Rbe2'[22'[V2 '[dV2 & t[V22& [V2 '[d22'[2&[V 2 & [dV2'[22 '[22 &t[V2&[V2 &[V&t[22'[V&[2lq";!"&546#(7:q8/# #8H38Rm %7'%$L65bUW_442>  ->0\ mt[«Yq2!".=3;rAo[5TUhE3rThUT5[oAkXJEh<2v'[0%2>v'['[%22#54&+Ao[5TUh43,J?A=  +(.971"*%1:8;2m 3#"#54631;+:1\D%%QGCA2m "&=3;;m6]QmndX]62W &+532#"&5432DA7FZ44,|4%#Ȓ9C5@)*@' 2W #"&546353#"32654.pu,44ZF7AD%%@*)@5C9O@ 2)F462"462"2#54&+5$$$$(D\1:+;$$$$ACGQ%%2F&?l2F&?26F&?26F&?2 %326=3#"&54&#"#54632E42GR~MLE42GR~ML;mT6NzP;mT6NzP2 %4632#54&#"#"&=3326vLM~RG24ELM~RG24EPzN6Tm;PzN6Tm2 v&E[2$+%&5463232654&'52#"&/.#"XEn >,-?@/WloSVa H%1P %*TT wTT~=+GNPTSur|K^|64PX- 2$~'[AG2$+%>54&#"#"&5463326?>32:*% P1%H aVSolW/@?-,> nEXT -XP46|^K|ruSTPNG+=~TTw 2$~&I[A2'D[2&D[2'E[2&E[2v&['E[2v'[&E[2&[G2'[G2~&[H2~'[H2&[I2'[I2~&[J2~'[J2k *2>54&#"#"&546332?>32  4 0 ?86JG9*)? I-:V :4##R=0QJMS6743/DS77[O233#!kgTiC2}&\J22!#39TiC2v&\[2!!3#kLTCi2v'[<^2Jw'['[^23##3!gTLC2v'[a2m'Z[2n&Z[2m'\[2n&\[2mv&['\[2nv'[&\[2m&[^2n'[^2}v'[i2nv'[<j2m&[a2n'[a2mv'[m2~v'[n2m&[&[aa2mB#7#533zH;mȥj2m '#7#'V@5l_ai4>mm2n 33##5#53;;ڕ;;2n ''7'7p********2 26=!!"&=EhI}xE_`DRbb2 3#"&=!5!265QzPR}IhEbbRD`_E2 54&"!5!5462EhImA}xE_`DRbb2 k'Jz2 #54632!!54&#"QzPR}AmI44EbbRD`_E2 d&z[2532+#3264bbRD`_EQzPR}IhE2v'[|273264&+332+2E_`DRbbQEhI}x2#";##"&46;E_`DRbbEhImA}x2v'[2%#"&546;3#"3bbRD`_EQQzPR}AmI44E2v'[w&[2v'['[2m%#";#5#"&46;%F2A@3F:HWYFT(F"lFpK2m8 7'5572#L65bUW_442> -5 5%20 \0mt[«Yq2 &5462+264&"\dddFF3F33F&3.Fddd6F33F32}&J 2!#32"&547264&"^Fddd 3F33F6dddF5,F33F32v&[235'"&4632%"32654&20dddF/1$22$#34^LFddd3#$22$"42N&[02! 632"&="32654&,1/Fddd0"43#$22dddF(4"$22$#32N&[02N'['[C2N'[0&[C2y%7632#"&'6264&"#22# /Z<΄2F2*2K2"&5462#"'265264&"dddF-)&3F33FJvv FdddyS}}SF33F32326=#"&462"&5$4&"22T)-Fddd.3F33FJS}}SydddFvvF33F32!#4&"632"&5462264&"T)-Fddd3F33FS}}SydddF vvF33F323462"&463254&"$4&"22dddF-)3F33FvvFdddyS}}SJF33F32v'[2v'[235!264&+"&46;2#"2642^S}}S!dddFyyF33F3TҰ)-Fddd3F33F2v&[2)"&6;2"&547#"3!"264yyFddd!S}}S^F33F32dddF-)Ұ23F33F2v&[92Pv'['[2v'[&[x2mh264&"#";#"&546;2"&54B)33)>-,?. . n19,+:,4?-,??,2m'/7326=3#"5!"&546323&54632&264&"264&"W85)`->>-,?J>-,? . . . .819,+:,4?-,??,,??,6. . . . 2T'4&#"3254&#"#432#"&54632#oNRk<5oTs2I+L1VuspTNoTKLKy&S,$D=%emo42T'#4632#".54632#4&#"32654&#"TpsuV1L+I2sTo532#"&53oNRk<5oTs2I+L1VuspTNoTKLKy&S,$D=%emo2f!5!332654&#!5!2#"&5dgRJ54Bn:~SxPRRlD`_EF^Rbeb2f5!#"&5463!!"326=2dRPxS~:nB45JRRbebR^FE_`Dl2f5!54632#!5!2654&#"2dgRPxSj:nB45JRRvbebR^FE_`Dl2f5!#54&#"3!!"&546322dRJ54Bn:jSxPRRRvlD`_EF^Rbeb2m!5!#54&";!"&54632n84#(7:6p8+#//# #8H38R2'5!";#"&5463 |BZ:nB422PxSzt^FE_Reb2&[327!2654&+532#|:nB422PxSzt@^FE_Reb2'[ E2%#"&546;#"3!5 SxP224Bn:beR_EF^t2&[(D2m#"&5463";5)697:A>25B6A:L2L333##5#535!kgzzTxxTxxTw2L !3##5#53539xxTzziwTxxTC2Lv&[z2L!!5#53533##kLxxTzzwTxxT2Lv'[<2L!##5#53533#!gzzTxxLCTxxTw2Lv'[j2mR#7#5#53533#3HpkCC6DDmi6==632 3632#"'#6264&"2T1=PrrP=1TT@\@@\#ss##\@@\@2#"'#3632tMP:TT8NSrSn8u76v2746323#5#"&2tMP:TT7OSrSn8&76v2%#"'#3632rSN8TT:PMtOv67u8n2463253##"&2rSO7TT:PMtOv67&8n2 !!!!#2ffRRR2 )5!5!5!3fRRRC2 33!!!2RfRR2 #!5!5!5Rff&CRR22>=3"&=##$ , RHHNRڽ MccMkx2354."#546233 , RHHNR MccMk&2!#335462#54."$RNHHR , xkMccM 23##"&=32>5RNHHR , &kMccM 2  ##37ѡTss{2 333#'2ѡTssm&2 !#'#33^ssT{2 373##2^ssT&2D46233#"&54&"2gg1-TTU]6L6 IggIFo&q&66&2D#4&"+3265462DT6L6]UTT-1gg &66&qzoFIgg2D326546;#""&2T6L6]UTT-1gg&66&q&oFIgg2D"&54&##32265Dgg1-TTU]6L6IggIFozژq&66&2{ !3 373H^\[_tt&2{ !# #'z^_tt&m2  57'72r;ZX2'~)2&[2 ! %5rŔmmZ쉻X2{ 3 3!!^\[_90F&cT2{ 3# #'!7!^_90&cT2 5 57%2rPT+ZZ012& L2'[ 2 % 'rPTZZmmZ0:1d2{ 33{\[_R&2{3 ##2_R&2 5%!5!%2rvvZR2!#5#5353!%5 5;HH;vrDmmRmmZZ2 #5462"7#64'3%5 5 gz#.##. vr.RB2++2).ZZ2! !!rvvmmZR2{  3333#'#{\[_RSRSS&  2{ 3 ####353'2_RSRSS&  ^2 5 575#535372rRZZeR1TRTT237!#5#535335'5 575.;..;ArTRTTmmRmmeZZe2  64'5'5 5777%462"&'#5 rRzzz.. -C,xqZZqIRHH)**))B.R2 % 3#'5#75rRZZmmZeR1TRTT2 2657 &5TbbCTi2 %4&"46 T.TCbbTi2 7!264&#!'!2#!CbbTiTT2 '~) 2 '[ 2 !"3!!"&63!bbCTiT.2 % &5-2652mmmicbb2 !%46  4&" .cbb2 3!2# !264&#!2cbbmmm2 '~) 2 '[ 2 !"&63!"3! icbb.2 26= &53!5TT2юbb i2 54&"46 #5!T.T bbi2 %3264&+%!2#!53#;bbiTTT22 '~h) 2 '[x 2 #";!"&63!#3bb iT.T2^535332y;x;;2%#"'#"&533265332653hIO77OIhT7&%7T7%&7TJh>>hJ(&66&(&66&(24632632#4&#"#4&#"#2hIO77OIhT7&%7T7%&7T(Jh>>hJ(&66&(&66&2 2#!5!2654&#!5!2654&#!5Gk##kGD$88$D$88$DڀL*__*LTO)(OTO()OT2(2654&#!5!2#!5!2654&#!#5#5353$88$DGk##kGD$88$;zz;P()OTL*__*LTO)(PmmRmm2 0462"'#5!2654&#!5!2#!5!2654&+64'#.##.6 g$88$DGk##kGD$88$ T2++2)k.RP()OTL*__*LTO)(P.2 3"&5467.5463!!"3!!"3!Gk##kGD$88$D$88$L*__*LTO)(OTO()OT23265332657#"'#"&57&%7T7%&7ThIO77OIh,&66&,&66&TJh>>hJ(2%4&#"#4&#"4632632L7&%7T7%&7ThIO77OIhT&66&,&66&,T(Jh>>hJ2 7!2654&#!5!2654&#!'!2#!h$88$h$88$TGk##kGDTO)(OTO()OTL*__*L2(!2#!7!2654&+#5#732654&#!3532Gk##kGDTh$88$;ţ$88$;ڀL*__*LTO)(OllTO()Ol2!0462"72654&#!3#!2654&+64'!2#!#.##.$88$` `h$88$n Gk##kGDT2++2)kP()O.O)(P.DL*__*L2 !"3!!"3!!"&5467.5463!L$88$h$88$hTDGk##kGO)(OTO()OTL*__*L2"'32653265%#"'#"&553?7&%7T7%&7 hIO77OIh TĄj&66&&66&%Jh>>hJ(2"4&#"4&#"74632632%#7&%7T7%&7hIO77OIhT&66&&66&j%(Jh>>hJط2$2#!#53!2654&#'32654&#!Gk##kGD*$88$$88$ڀL*__*LCTCiO)(OTO()O2 ,!2654&+#52#!#5332654&#!353N*$88$<;wGk##kGD<$88$֊);CO)(OllL*__*LCTCO()Ol22462"732654&#!!2654&+642#!#53`#.##.d$88$։ *$88$ Gk##kGDT2++2)kP()O.O)(P.VL*__*LCTC2$3"&5467.5463!3#7!"3#"3!Gk##kG$88$$88$*L*__*LTO)(OTO()O2m3732EE22222'[~ 822%#"'#"&53326=3326=3hIO77OIhT7&%7T7%&7TJh>>hJ(&66&&66&24632632#4&#"#54&#"#2hIO77OIhT7&%7T7%&7T(Jh>>hJ(&66&&66&2 2+532654&+532654&#!5Gk##kG$88$$88$DڀL*__*LTO)(OTO()OT2$%#332654&#!5!2+532654&+j;;$88$DGk##kG$88$,lO()OTL*__*LTO)(O2& >[2 3"&5467.546;#";#"3!Gk##kG$88$$88$L*__*LTO)(OTO()OT2 ,"&54632#".5332>54&#"3Ylq\i/N[AypCT%:MH%=\=([Ey>3jYW^bpM{}S4$N`x>a;&2>\W32#4.#"3254&#.Ylq\i/N[AypCT%:MH%=\=([Ey>3pYW^bpM{}S4$N`x>a;&2>\W54.+532#"&54632T3)kkN5Y6'&;a>`N$5KsCpb^WY3>yE[.CPA%HM:%TCpyA&\dR5i\ql2+#"32>54.+532#"&546;53#1AikN5Y6'&;a>`N$5KsCpa]A;;myE[.CPA%HM:%TCpyA&\dR5i\qk2-462"'"32>54.+532#"&5463("0""0XkkN5Y6'&;a>`N$5KsCpb^z0""0"yE[.CPA%HM:%TCpyA&\dR5i\q2+33254&#";#".54>32#"&T3)kkN5Y6'&;a>`N$5KsCpb^WY3>yE[.CPA%HM:%TCpyA&\dR5i\ql2U&32654&#"#4632#"&54?!5!"- uOGp1" TUA=frun#(!-7aZ0*6&2Q[cQRZ\Ta2U&%5!!#"&54632#4&#"32654.#nurf=AUT "1pGOu -aT\ZRQc[Q2&6*0Za7-!(2U&>54&#"32653#"&54632!!5"- uOGp1" TUA=fru)(!-7aZ0*6&2Q[cQRZ\Ta2 #&#37632#"&5463"32654&#"[aT\ZRQc[Q2&6*0Za7-!(Y#nurf=AUT "1pGOu -2 #)#37632#"&546753#532654&#"[aT\ZRQcE?;;06*0Za7-!(Y#nurf=8Q LS 41pGOu -2 #.%32654&#"#37632#"&5467>32#"6*0Za7-!(aT\ZRQc84 '"00"*)1pGOu -"#nurf=2M0D02 #)4&#"3"&54632#".##2326O/)/&2Q[\PQyW>rTKHTAkI=54E(5IIn0" TUA>esx\\A#3RbcR3r2 #&%3##"&54632#52654&#"32>aT\ZRQc[Q2&6*0Za7-!(Yurf=AUT "1pGOu -2U%32654&#"#4632#"&546?'5UG/pTKl1" TUA=fu|GY,a5E=2:?-*6&2Q[cQOqfXSnA a2U%35>54&#"32653#"&54632%2YG/pTKl1" TUA=fu|GY,a5E=2:?-*6&2Q[cQOqfXSnA a2 #%337>32#"&5463"32654&#"2a AnSXfqOQc[Q2&6*-?:2=E5,YG|uf=AUT "1lKTp/G2 #)337>32#"&546753#532654&#"2a AnSXfqOQcMF;;#6*-?:2=E5,YG|uf=;SJV 1lKTp/G2 #+337>32#"&547632#"'32654&#"2a AnSXfqOQcP."00"$6*-?:2=E5,YG|uf=l&'0D0(1lKTp/G2 #%#'#"&54632#52654&#"3267 a AnSXfqOQc[Q2&6*-?:2=E5#,YG|uf=AUT "1lKTp/GY2 n32=3#"&547!5!  J:Tk`zn*L||u}|N8T2 m!>54&#"#54632!$!  J:Tk`z+Kzzv|{O7T273632+53254&#"2T8N||v{{L*z`kT:J  !2& XLr2& X[\l2%.#";#"&546323*L{{v||N8T!  J:Tk`z2 7#3!32=3#"&547#m;;!  J:Tk`z,m*L{{v||N82 m%3#5!>54&#"#546323;;S!  J:Tk`zm*L{{v||N82 #632+53254&#"#5!^k8N||v{{L*m,z`kT:J  !;2 & ^Lr2 & ^[l2 5!#.#";#"&546325,m*L{{v||N8;;S!  J:Tk`z2/#53#"&5467#532654&[Ԁ:EҕE:7HeeHT!rDggDq"TX8D``D8W2/#%#53.54623#5>54&"Ԁ:EҕE:7HeeHT!rDggDq"TX8D``D8W2U#53>32#"&'#533264&#"T!rDggDq"TX8D``D8W)Ԁ:EҕE:7HeeH2U& dL82U'[ d2U%3#5#"&463253#.#"326ȍT!rDggDq"TX8D``D8WԀ:EҕE:7HeeH2!5!53#5#"&5467#532654&[;;:EҕE:7HeeHkm!rDggDq"TX8D``D8W2$!%!#33.54623#5>54&";;:EҕE:7HeeHk,m!rDggDq"TX8D``D8W2!##5!#>32#"&'#533264&#"*k,m!rDggDq"TX8D``D8W);;:EҕE:7HeeH2& jL2'[^ j2!.#"32673#5#"&46325#5!#W8D``D8XT"qDggDr!m,k)7HeeH7:EҕE:;;2 $#"'#"&53326533265353#5`IG77GI`T/&$0T0$&/;;Jh>>hJq'55''55'qll2 #$74632632#4&#"#4&#"##3`IG77GI`T/&$0T0$&/;;TJh>>hJq'55''55'l,l2$!2#!5!2654&#!5!2654&#!5#5!#Jh>>hJq'55''55'l,lN`IG77GI`T/&$0T0$&/;;2(!2#!5!2654&+#332654&#!5#5!#Jh>>hJq'55';;'55'l,lN`IG77GI`T/&$0m,k0$&/;;2,462"!2#!5!2654&+532654&#!5#5!#"0""0Jh>>hJq'55''55'l,l0""0"a`IG77GI`T/&$0T0$&/;;2$5#5!#!"3!!"3!!"&547&5463l,l'55''55'qJh>>hJNQ;;/&$0T0$&/T`IG77GI`2 463""&54426n\B++B\zz"C_T- xx -T_CUyy2 #52654623"&54&"\B++B\zzC_T- xx -T_CUyy2 !"&533!2#!"#463!264&C_T- xx -T_CUyy<\B++B\zz2 '~) v2 '[ v2 %!2#4&#!"&463!2653#!"TC_T- xx -T_CUyy\B++B\zz2my!3!#2G672'y 333##5#75#535766M Fn6FFr6!!""2 & t^2 & u "R2 & vM22 '~) ~2 '[ ~2 & yN2 *%#"'#"&544326533265463"_HE77EH_+B\.%$.T.$%.\B+Kg>>gK -T_Cz'55'q'55'C_T- 2 *46326323"&54&#"#4&#"#5265|_HE77EH_+B\.%$.T.$%.\B+(Kg>>gKy -T_C'55'q'55'zC_T- 2*2#!"#463!2654&#!5!2654&#!"&533Kg>>gK -T_C'55''55'C_T- _HE77EH_+B\.%$.T.$%.\B+2.2#!"#463!2654&+#332654&#!"&533Kg>>gK -T_C'55';;'55'C_T- _HE77EH_+B\.%$.l,l.$%.\B+2& [2*7"&547&5463!2653#!"3!!"3!2#4&#Kg>>gK -T_C'55''55'C_T- J_HE77EH_+B\.%$.T.$%.\B+2  )2%#"'#"&54435335463"#3265#3265_HE77EH_+B\T\B+T.$%..%$.Kg>>gK -T_CccC_T- j'55''55'2  )246326323"&=##5##5265734&#"34&#"|_HE77EH_+B\T\B+T.$%..%$.(Kg>>gKy -T_CccC_T- j'55''55'2 )22#!"#46;5#535#"&53332654&#32654&#Kg>>gK -T_CccC_T- j'55''55'_HE77EH_+B\T\B+T.$%..%$.2 :32654&+#5'353326544#!"#46;5#535#"&533='55'1;AA;1'55'Kg>>gK -T_CccC_T- C.%$.llll.$%.T_HE77EH_+B\T\B+2 A462"764'72654&+32654&#2#!"#46;5#535#"&533P#.##.d '55' '55'Kg>>gK -T_CccC_T- T2++2)..$%..%$.M_HE77EH_+B\T\B+2 )27"&547&5463!2653+3#32#4&#'5#"375#"3Kg>>gK -T_CccC_T- j'55''55'J_HE77EH_+B\T\B+T.$%..%$.2#*4!#5&'35.546753#&'5>54.";X'1:-$[M;OTXIVGcV;&+&f-5  U )4:&BTVUSGF %QFWN)"o,  2 6#3265#32653#"'#"&54435#5!#35463"u.%$..$%.T_HE77EH_+B\ZZ\B+'55''55'Kg>>gK -T_CcTTcC_T- 2 634&#"34&#"#46326323"&=#3!535##5265ɥ.%$..$%.T_HE77EH_+B\ZZ\B+ '55''55'Kg>>gKy -T_CcTTcC_T- 2632654&#'326544#!"#46;5##335#"&533='55''55'Kg>>gK -T_CcTTcC_T- C.%$..$%.T_HE77EH_+B\ZZ\B+2$1>7#335#"&533!2#!"#46;5#332654&+#5'35332654&#TTcC_T- Kg>>gK -T_Cc'55'1;AA;1'55'Z\B+_HE77EH_+B\.%$.llll.$%.2$,E#335#"&533!2#!"#46;56462"764'72654&+32654&#TTcC_T- Kg>>gK -T_Cg#.##.d '55' '55'CZZ\B+_HE77EH_+B\2++2)..$%..%$.265#"35#"3"&547&5463!2653+353#5#32#4&#'55''55'Kg>>gK -T_CcTTcC_T- .%$..$%.T_HE77EH_+B\ZZ\B+2 .%3265463"#"'#"&5443265#5!#.$%.\B+_HE77EH_+B\.%$.ZZ'55'C_T- yKg>>gK -T_Cz'55'TT2 .4&#"#526546326323"&54&#"3!53u.$%.\B+_HE77EH_+B\.%$.ZZ('55'zC_T- Kg>>gKy -T_C'55',TT2.#3!2654&#!"&533!2#!"#463!2654&#TTd'55'C_T- Kg>>gK -T_C'55'CZZ.$%.\B+_HE77EH_+B\.%$.26#335332654&#!"&533!2#!"#463!2654&+#5TTz;'55'C_T- Kg>>gK -T_C'55';CZZll.$%.\B+_HE77EH_+B\.%$.ll24>462"764'32654&#!"&533!2#!"#463!2654&#!#33#.##.d '55'C_T- Kg>>gK -T_C'55'TT= T2++2)0.$%.\B+_HE77EH_+B\.%$.ZZ02.53#5!"3!2#4&#!"&547&5463!2653#!"3HTT'55'C_T- Kg>>gK -T_C'55'ZZ.$%.\B+_HE77EH_+B\.%$.2&'32653265%#"'#"&55#5!#?7&%7T7%&7 hIO77OIh ZZĄj&66&&66&%Jh>>hJ(cTT2&4&#"4&#"74632632%3!537&%7T7%&7hIO77OIhZZ&66&&66&j%(Jh>>hJطcTT2(#33!2#!3!2654&#'32654&#!TTgGk##kGDa*$88$$88$CZZCL*__*LCO)(OTO()O2 #0#5#!2654&#!#33!2#!%2654&#!353;)*$88$TTgGk##kGD$88$֊);CllO)(OZZCL*__*LCTO()Oll26462"764'32654&#!!2654&#!#33!2#!`#.##.d $88$։ *$88$TTgGk##kGDT2++2)0O()O0O)(OZZCL*__*LC2(53#5#!"&5467.5463!#7!"3#"3!LTTgDGk##kGa$88$$88$*ZZL*__*LO)(OTO()O2%"./#'.#523%+?n2(,Dn/TT! mT" +2@ ''7'7 hh gg hh g gg hh gg h29'2'2'2v'2's2v's2'2v'2j&[2&[22v& [z2&[2v'[&[<2&[22v'[2&[j2^5!5!2,,c;;~;;2^53533#3##5#5352x3$$$2ThUT5[oA2$$$KkXJEh<2  4632"&264&"'2#54&+$$$2Ao[5TUh2$$$K"0""00""0"bbRD`_Ef0""0"2532+#3264&#462"bbRD`_E"0""0QzPR}IhE0""0"2v"462"532+#3264&#&462"2"0""0bbRD`_E"0""0U0""0"VQzPR}IhE0""0"2|#";##"&46;462"|E_`DRbb"0""0EhImA}x{0""0"2m #?mp2m54&"#5462\HdH6ggm2HH2IggI2l753264&#"#4632#+#//# #8H38R84#(7:6p2l7#4&#"#463278/# #8H38Rj#(7:2m(3##2;:2lq#"&5463!#"26=p6:7(#4!+R83H8# #//#+2m #5326=3#h6B&46VFm70K>VP2mf(4'52#"&/&#"3&5463232680:<*,2 .%  ~E-%9 L2B<;@)0>0')2p>G-+?#2m  /&462"d]ww"0""0mp6Ή0""0"2m  /$462"2]ww "0""0mp6Ή0""0"2_ 462#"&3264&"2$4#$ 2$$$K2f462"2"0""00""0"2462"'3#"&=!5!2652"0""0QzPR}IhE0""0"bbbRD`_E!v462"3264&+332+!"0""0E_`DRbb$0""0"OEhI}x2 v462"#"&546;3#"3"0""0AbbRD`_E$0""0"OQzPR}AmIhE2X'"&5462#"'265264&"462"dddF-)&3F33F+"0""0Jvv FdddyS}}SF33F30""0"2(')"&6;2"&547#"3!"264462"yyFddd!S}}S^F33F3T"0""02dddF-)Ұ23F33F0""0"2X3#"&53265462"V;;^T"0""0-JvvJS}}S{0""0"2v 462"5!!2#!5!264&#!:"0""0,AAbb$0""0"N::T2v5!!"&63!!"3!462",AbbA"0""0P::.T0""0"2 #"&54632#4&#"3462"dbebR^FE_`D("0""0mARPxS :nB45J 0""0"2N 462"#"32653#"&54633"0""0D`_EF^Rbebd0""0"mJ54Bn: SxPRA2'/4&#"3254&#"#432#"&54632#462"oNRk<5oTs2I+L1VuspT"0""0NoTKLKy&S,$D=%emo40""0"2ev'/3#"&54632#"5332654#"326462"TpsuV1L+I2sTo5>hJ(&66&(&66&({0""0"2v (2#!5!2654&#!5!2654&#!5$462"Gk##kGD$88$D$88$D"0""0ڀL*__*LTO)(OTO()OTJ0""0"2v (3"&5467.5463!!"3!!"3!462"Gk##kGD$88$D$88$"0""0L*__*LTO)(OTO()OT$0""0"2X (3"&5467.5463!!"3!!"3!462"Gk##kGD$88$D$88$D"0""0L*__*LTO)(OTO()OTU0""0"2Uv.462"32654&#"#4632#"&54?!5! "0""0/"- uOGp1" TUA=frun#$0""0"(!-7aZ0*6&2Q[cQRZ\Ta2#&.%3##"&54632#52654&#"32>$462"aT\ZRQc[Q2&6*0Za7-!(c"0""0Yurf=AUT "1pGOu -0""0"2m 3#53#53AAAs22 22l v&#"#3632# #88'8(/# 2m1t46;#";2+532654&+"2,  m** mB+2/B-2 #z$*#zlDOop#y%6 #y EO\o#y%6E #yEO~o#q%6g #qE0*#v*#vYs#y'"y^ GY\#y'E#yWGY~#q'g#qYGY1"+32654#"'7#!2+632#"'32654&+ ! . '= &)B9/N"lppl%U1# +2")5#5632#"'732654#"'7.54632"32654&JA+ &)B9/N ! . 'gt{fl5DTTEBPP'E,*1# +2"(%V~}Qp[\on[^oY.#f'#fZGZe#q~"(C(p#C^Ze#q"(v(l#vZ.e#f((#frHZ\e#|((E#|tHZ1e#x("xrZCs#y)s#yI,g#q*&"qrJSq#y+F#y KS\#y+F\#yoKSo#j+F^#jK1&32654#"'7#3!3#!#632#"' ! . ']w]] &)B9/N%U;'L1# +2"1.4&#"#632#"'732654#"'7#3>32#C)@N &)B9/N ! . 'S%K8LYSk70aP1# +2"(%U0'MBtS=#x+F=#xpK\0#|,\ #|LB#j",v1"j"vO#v.:#vVNO\#y.:\#yqNO~#q.:~#qsNP\#y/:\#yOP\g#q 0\[#q 1P~#q/~#qOP.#f/.#fOK#v(0F#v%PKs#y0F#y PK\#y0F\#yPLp#y1F"y{ QL\#y1F\#ypQL~#q1F~#qrQL.#f1F.#fsQ&#|"2j'$#j&#|"2j$X#jt&#q"2C$m"ik"RCh&#q"2v$m"ik"Rv[i#v36& #vS[is#y36& #y S]s#y5EA"y U]\#y5;\A#yU]\g#q T;\P"i" U]~#q5~A#qU0m#y6""yQ V0Em#y6"E#yLV0m#v"6y"^#yM0m#g"6y"T#yP#0Emx#y \"E"yQ  ]Qp#y7@"ytW\Q#y7E#yW~Q#q7gC#qW.Q#f7I#fWUF#j8AF #jnXUE#|8AE #|hXU#f8A #fcXU#|"8vAp#v+U#q|"8jA?"jos-n#|9 "|VY\#y9 \ #yHY#C@:#CZ#v9:#vZo#j8:#jZm#y4:#y Z\#y6:\ #yZp#y;"yP [p#j;"jT[ p#y<&"yR \G#f="fT]\G#y=\ #yM]~G#q=~ #qO]F~#qqKB"jvW#zZ&"zR\*#^)D#yA\#y$*E#ymD##$*#D#f"$vm*c#v6##f"$C "C)#f"$*1##f"$|*c#|r\#f *E"fp #x"$v*|#v#x"$C*#Ce#x"$@%*##x"$|:*q#|r\|#x *E"xn Z\e#y((E#yoHZe#+((#HZe#|(("|vHZ#f"(v~(h#v;#e#f"(C"CZ}#f"((:#Ze#f"(| (a#||Z\e#f (E"ft S#o,N#j_\#y,9\#yL&E#y2$E#yjR&#c2$#R&#f"2v$v#vI&#f"2C"C&#f"22$/#&#f"2|"$`#|p&E#f $E"fl &#jb$5"j}c&#kb$5"kVc&#gb$5#c&t#|b$5"|~c&E#yb$E5w#yjcUE#y8AE #yAXU#88A#XU#jqAo#jrU#kqAo"kmrU#'qAo# rUq#|qAo#|rUEX#yqAEo#yAr #C<&"C0\ \#y<& #y\ #2<&#\ u#|<&"|W\0G" w>0G" s>0G" >>0G" F>0G" K>0G" >>0Gk" q>0Gk" q>" " & K" V" " T" W" '" VB'" `B'" ,B'" 4B'" 8B'" ,B# #?# x#.N" #<" #-" #9" #J" r DJ" tDJ" =DJ" ODJ" |DJ" [DJk" fDJk" xD# }%2" ؁%8c" %e" %a" %d" %T" %U" %," F8" F&" F&" F!" F(" Fk" Fk" F# }',# '+" '" '" '" '6V" 't2Z" 'p$" gL$" rL$" :L$" BL$" GL$" :L# {-# ~-" -" -S" -m]" -w2" `R2" hR2" 1R2" :R2" @R2" 4R2j" bR2k" bR# x2P" 2" 20YW" 2$# V$# V$# V$# V$# V$# V$j# V$j# V# 6# {6" 6" 6>" 6ee" 6TT" 6{TV" 6{0G# >0G" P>'# B'" =BJ# DJ" bD" F8" F$# L$" VL2# R2" XR$# V$# V09G"j 09G"j 09G"j 09G"j 09G"j 09G"j 09Gk"j 09Gk"j /# | -# z #  #  F#  M#  4T#  ,W# y 8" 8" 8" 8" 8" 8" 8k" 9k" #  #  A#  F#  <#  ?#  T# / U# 2 $9# D$9# E$9# F$9# G$9# H$9# I$9j# J$9j# K#  L#  M[#  NY#  O# S P&# s QT# d RV# g S0G"xn>0G"qo>09G"j T09G"j>09G"j U0G"|r>09G"j #xg#q" ̽" ׳1# ~tA 72>73#"=3} !1JL %*Oc2( tAB lG| yG# j8" X:"D9" YJ"|mD8" " #" ב#mV" %0" ״%S\# %RA#  BAh" E 9Gj'  "xF"qF*" F6" F"|F*l& F*#x' g#i'-" 'k$" א'bDAs#  QA" ] 9Gl'  2"x^R2"q^R2" >R2" >R28" ~N28# N2"|aR2k"R \ #x2 g#q2+" ̺25# w2# }/I"li  #'#5!#5A:aSES ISSSS"li  PC $9# `$9#V$9# a$#|V$9# "" ̟-<"" ט-<" ̧6F" ׈6$4# 6RP#vvAJ.8#58HH.8 18 18!518HH8!58HH8 K ?kA#5473]]00e{& P@3565#@]]00e{&RAh73565#A]]00he{&R@ #&=00]hM&{e0+'  1.'  /,h'  1.'  &O ###5353XXRUR&O#3##5#53#5353XXRRR3R2, 2#"&4{hIJ34IJ52IIhF,F|}Whs'h'htth''hWKy  $.9C2#"&46"2654&%3#2#"&46"2654&%2#"&46"2654&?WX=>XX=%56J55(BuB?WX=>XX=%55J65B?WX=>XX=%55J65X@=XY|X<5%&55%&5F +X@=XY|X<5J65%&5XX=%55J65B?WX=>XX=%55J65?WX=>XX=%56J55(BuB?WX=>XX=%55J65X@=XY|X<5J65%&5X "5>32&KY9UV9YY@9229@Y#73#'#D>>{y> 6' I' J 5[A!5[ANZ3#>2>y,!7;#"&=47.=46;#"%.49R.$94.%",,"V'AE>x&FE>EA'F; E <,!74&'5>=4&+532+53265m",,"%.49$.R94.%VE< E ;F'AE>EF&x>EA'M)#","M',"|&". #>7!5QJbFaJgcT{W0O ####532 mb@R@9^^LJJ@J 7".54>3!#33B`--`B 99RJDi>=hF@@V J %!53#5!2#3+99 B`--`RRJ@V@Ci?>iD(W" I?m 3##"=?hhi<#i hh4&&x;X %27#"&'5"5>32&KY9VU9YY9UV9YgY@9229@YY@9229@Y(W&  I<H' 'yy%3#"&/&#"#>32326+H>/-##5+O854$0 #91  .14 ;4X "5>32&KY9UV9YsY@9229@Y$a'&546276327632#"/#"/#"&54?&'#"&54?&'#"&463267'&546326q "." m! * $$ * "m "# m  * $$* m $$* !m "." m"* $$* "m "." n  <D'y'yy 1G& ' '  e<&yO'yO'yyg<'y'y'yw'yzyT<'y y<9 'yV'y&yy<)  'y& 3'yH'yaHya< 'y'yy<&y'y'yyD4 #""3265*LIZ/-inU[*bL)Q #533##=м.CC>; 5dF#632#".'332654&#"#7.&.BRXE%98I/56.1#4"4wO?CU, G4./7(G$4>32#&#"632#"7"32654&!.)5D8?14&C=OTA*45'(308W1 ;4@SN/L;>Q1()64(+1G #>7#5G$=QR,hm4H*#"&547.54632&"2654"32654NVCDUN#M<=LJH++H+$V44*,4"H:IJ:G"'1@?2&$%%.%&..&%F$#"&'33265#"&54632"32654&F!.)5E9?14)?>NTA'30(*558V1 ;4@SN/K<>Q.5(+23')7,Mh ##5#5353L.... S!5S.. lS & G G)o! 3#.546$YY$199 @MLN! IK-=yQ)+D >E8{8Ft*Bu8Q @*F A*G B8G C*H D*F EM FV T7 GV To Heol IKLl II(+]D:)+PH:)+LR:) 84[8)+N:)&A $*0#7&'#7.54>?373#&'>73'&''O 7+#7<8&9];7,# 7BTk=C Uv .t"OiKU@C[|6Y/_fO8((<\;~- e_L;I~ 9)4>32#&#"3>32673#". (>fA!RZi?8M$C( TTS q@f<(d2bjO4܊fO_;3UJgg3Ni`,3###535#53!!3SOOOOB77B7R793#3#632327#"&#"'67#53&'#53.54632#&#"&}Y;?!h(+*9J+#580e|nQ"e5>,X@M ,> 7(62Z'B8.%BW>6!!791![{ 1W9FFr&*3>32>?3#4&'#5#54&#"#7FM"K4a,#D0<7>58T'#T73.3HTY'1 J/*P*$W[ I;wi+5*27P9K GG#&)##'##5#535#533333##5##3'##3'3]N----ZO33O32fO::@@7FF]FF]]]]lUu[N3264&+#327&'332654&/.54632#.#"#"'#"&5#+#!2353CKKCVX5E7C$-4Y?YYX&0'1S6PDucz2> 14CiK]-jt8LZ>3 +(DOjc]x'V525#'+.14!#'##'#53'#53333333#3#'7#!7#;'##%#3'W97W5iZF78X3KL>;HG6CF1_nn\_2NNKW.eRSdcRUd?>,'wqI?))J??$k0".'#73&547#73>32.#"!!!#3267>g@/S7O?/@l@c^3K,DsExD)E9G-AQ=;;9QA/CI&d_;;\k"Y+##533333###]77]F%xngH**HgQ#5575575#5!#7]<F]_F_f_F_RR]F]f"8' 3654&#"2654''#>7.54632#b5( (Ghq;'jC%ZK:P[DG}R\F(%3."tssPu@. (! K[J8ecaM]'!&-327!%#3#+##535#535!2)&+!654'Q%42<fF]4444-Ql]3%LM10 0=F0D0}@=+[DZ "+3#.'>=#53#'#5.54>7_Blu2:xGPOxk 52J4 @;V@7T2 +<753#.'>73'b;(6N0;lh_8CL=`oy;YP~hd"=H;;*64`sG9DanT>   5!!### <ij]RRRR.!67 Z^O S$Mpb .!&'7! O_Y S bpM6m37NY3< .#"&'67>326D=0'zc$ 4GMi27<* / $zl #+=; >32&'7'67.#"3MG4 $cz'0=D6i3;=+# lz$ / *<72"y !&'67 OAbbAO $ bM^^Mb "y {wm $2654&"7#"&5462:-*@+X;7!.'>7*$!#=##=#!$* *$!#>##>#!$,+.*#??#*-,,,,-*#??#*.+rnT !>< sGJHC #dL*m ?PZH~#!5HHP~H)_'y|'y|yB|0!#5!#GHGgpB" !&'6702y#dd#y2781#ww#1JO###J>GO>GONN  v uG {I@0G {{637''7'7)E F-!()!-F EI':==:')34Af3#"#>32327#"5#"&5467>?654&32672#654&#"3273#".546::N< ZK8E.  7    07N]I.,@43"0Q8E='"#7,%P!= #A)&8 yC<>-$/8   1,0L   #2!R>,  $#1<+*1[2-,s)>0=Ay"#>32327#"5#"&5467>?654&32673#4632#454&#"#"&547332>54.1'.N< ZK8E.  7    07N]I.,@43"0Q::]J5A<" )3=7/(aP?G@(&,    58+C<>-$/8   1,0L   #2! ;Q/&  ( &!AM1, %0!4>32#.#"32673!".00GyL+_^W6+/:Z``!KwG/Md2bjO4NNej3Ni`Q"r&,3%,4>753#.'>733!5.4%G1JC> K K CI1G%/45n$GI3PP QC& Ob AC3GGO XFCW0(:L3#2#654&#"3273#".5462#"&54>2>54&#"::8E='"#7,%P!= #A)&8 y?L /Q2=K /P#8,(#7,>,  $#1<+*1[2-,sK<AL=)K<@L=)"0<+,1#0<*+20((?3#2#654&#"3273#".546#7#"&5473326?3::8E='"#7,%P!= #A)&8 y.7 ,  $#1<+*1[2-,s):*% # F:0mR+$#533.#"#632# 33267#KRR x^W^_+LyG0/GwK!``Zg>sNN4Ojb21`iN3!je~(o"r),!t}326?623>3232676;2#"&54?#"#"&54632#"'32767#.5476?#"&54654#"+"54632%"67654#!'\&jaG14tIc9#U( <^$:9L@<.yAP9K0#!$ ?4d+H\  VJ3.)%4$ i'!&j3bP0@73%Տ@6FI?fE3K8 FD60TkY[)& @">YkyW963 kUpD+-Oi27&#"7>32&'654&#"327#"&54>73254&#"#"'63232654&54>7>58A � *-0Me4(,6 # +#$! &8   Q`K N (2$ %D0R  +' W,!  -"p)EG,,6 0#"0(4 $  [n?A1M+ S+$!7)!8.Y 33###3#33俶7HH-H;'L6mm3>32#654&#"#T9/T8?HPTI/&Dd=T,$C;W &/`RVN>32#654&#"#?3U/T8?HPTI/&Dd=TzVWT H,$C;W &/`R?HR3:M>74632"'326?&547>7632>54'4;2#"&>K(DO4_'M*3&B';"#W?Z4dAJ#+?GaZ4% { !?.l#)# !J8)_DMW4>767327>32327#"'#"5463267#"&"327&">54&7B 46:EID]IAN>{W 61@6K'*@#VCw0gX@d) [{'D,D>%5V`">,- E.4!*3Ћ740XZB 91&q$3%2b,P;%c #  &qXz1$-327#"&54>7'7>324&#">  /)=@k/5 4"o #63P !#!E?Xt)a) BN;02'B )9LS.A#- K9vl?Q)!632#"'####5353353"32654&]9ihx|hk;KXTDDTXSBPPBDUS2VZCnn29999oooY^pL #53##`wXiX}'O</R.f133@8j&3X.4j-6%#"'332>54&#"#"&547&536324'326*L2m2!'8 1B ?,0(,0/+(((2sJU9!.ZR3.<'BD%IX$-M/$7\30CG.iWDjT*z>X8YG![i +#!2%3264&+3iq[-lugCKKCJJ]qoDxD&!('#"&632/7&#"327%654&/iVižg?.1;@54@85WO2:W3VҪq58*S9YN|w 2>54'%32676;2#"&54?65##"&54632#"'326?46367&#"3267>=4>32#"&547>3272"#"&#"6dDnnp*"0>+ "K]/ " OQ4""J>U;P/#!# >3;=[<{j@7BF%?&  m1&:8=vJ2 $ m y@'U%E/(+0i(GD2#qHnX'$<" wU f,wZO4 615 K:D()*H)/?NV/  !H23273#".5>54&#.54632#"&5332>54&#"[),#  P42(9#!&%O[aMLW*L9-YU$&&/ NF)M+.EP70 16'Ev`4' )?PuYGD, ?FrbK.J!> ]&*4&+326#&=4&+#!2#*LCJE +q:>Pks6H H6<`&0* ")8.IAC?e^>RS]0%.#7.+#!273#.'4&+326_ ]Pks6=!%\^" Hq:R8:GQJ#H6<]~$(037#&=4&+'7#!77#;2654&'7#_d6= +q:>y8",\LJED<88E8YLdX>R&0* ")8.IAC?^S'32#"&'"654&#"3"&54>3232732>54.#"#4>7.'6732654.9D0  1Z9FQ7S[06%*3& !8 .'++' 6!!  +J.S="1)! ;QJg %@j!E-! (2D8(.\=Bl 4Q-*A%!* .!<&%/ S7 / 4D7'n6  2[G %-%F O."mx74632"'3267>7&#"32>=472#"&547>327632#"&54632#"32>54'#"'#"&>54'632"327&"/"!$( B6;=IRL?yo96K:uA `5,7<" yVgm8'xQL5Any m5#7.54632677'"&464'327632"#".##"&">54&5467&)!!/2 LTs^V*!es!  &L'D1>A   V %P?$85BQJH CT!$N% 5 1aE 60!v$'* -$+-?#+A  Y   Aqk^1  +.?Q0(H %#"&5463232'>54&#"D._p;Iqes 0ZlCV# %S+lL=v=j(H$&R9-Zc74>7&54>2#"&54632#"32654#"632#"'32>54#"#"5>32#"&4#"326-9WGazRe3&Y* !D-#S|+X<%3$?%3R.N=1Y6 a'N _1##>zJ216o+X P ]+J>TP JND3/.@:9^6 =n+/ "2OF< )S}74632#"'32>7+"5467>763267>32#"'"547#"&4>3232#"&#"32654&5463#"&>5&#")0#!$ +bV:@z R *&,3?&  $'#C!t 7(/gF`~TB? C@GW5A\{ p#4-;@"@;_K&%!=J;!.&A5,2 537!.n(>aM'M4MU!5{>"Gz#E !))ln7;>7676332>7>?6332676;2#"&547>7#"=>7#"&5463632#"'B: *Y:s IV<k "*1 v-/G-o  Gl'+-?IvF-47B@Ca"Z[4#"+g $ E3###3#/%2#654#"#"&547332654&/.54>::XXH+VII /C"?n:=KY'+{$,-V~ t'( N8. # < '8-!JA 2.?-" # 5" #7,!} ) E[#u# m{[#u"t mE_# A _# A"t m_# A"u m_# A" @ mE`# B `# B" A mEa# D a# D"u ma# D" A ma" C# D mE"{ md,d#,,d#,,#,,d#9,9]#,9s#,#,9#,#,#,9d#;,;]#,;s#,#,;P/0&Y'K0BLBt#LLBR#L#LLB#YL  Y #LY h#L#LY F#L#L#LYB#[L [#L[h#L#L[DOFGFP  !&'672y#dd#y281#ww#1-; !#'67&'P81#ww#12y#dd#y21 75!&'7'6712y#dd#y281#ww#1-;x 367&'781#ww#1x2y#dd#y2!!.'7'>7!.'>7A82%6/^66^/6%28AA82%6/^66^/6%28DCF @6a00a6@ FCDDCF @6a00a6@ FC!%'>7.'>7.'7DCF @6a00a6@ FCDDCF @6a00a6@ FC]A82%6/^66^/6%28ABA82%6/^66^/6%28%#654'327+(t1 X"qS@(Sq"X 1 &@ "+ &@03!&'7'67!#082y#dd#y281#ww#12#!&'67!254&# NTVI2y#dd#y2}i36G;DD1#ww#1Q#+-"3!&'7'67!"&54663i}2y#dd#y2IVT3+#Q1#ww#1DD;GZu3!'7!#7[nuTpoT !67r#y28v#1 %!&'!2y#1#v8b\ \@#_G 8@  - 8\  Hr@ D!  r@ X& V !!!.'>7QQHM)B\[[\B)M~5PP5b!QaVVaQ!b'7'>7'#'#H!QaVVaQ!b5PP5m)B\[[\B)MHQQ-'7!5!7'!5!'7)MHQQM)B\[[\!b5PP5b!QaVVa,<z.'7373!QaVVaQ!b5PP5)B\[[\B)MQQH!'7'7!.'>7!7'!8L(PXRRXP(LL(PXRRXP(xTToT}_"`^KK^`"__"`^KK^`"VVV'7!'>7'7.'77'7H!QaVVaQ!bb!QaVVaQ!bPPPP)B\[[\B)M(M)B\[[\B)M0QQQQ /6%#"#".'#".#"#52>32>3232>;&'7'6`w:<=92), (#7A>9;<9D= (#4g2y#dd#ygNc bP>I>"854Oc bPcc#1#ww# 73#3#KhxcMx'N+%2#"&54632.#"#>"32654&#@G3!ez_i:QL.> X k]@JP=>RR3R^hc~O83WbSGCW[DBSZT !5!5!5!!5!cdLRR'R# 32654 &#"3#"'#7&54632JVtWqnFi\טfMJrYטh*FJuIqjg[NsfY 33 ! &d)'h !#7! d3Y'q7h!".546;#!!;~Es\3m-IP4!FyRFF;Q)F7!33#3#;#"'#7&54677#?t7}B76Y^E7%u.p }AGFFF[tM b-74>;#"!!;#"'&-&5B1*6. l -9%qJ$7W1 6 B/62C 6j27g )Ig7&3#7#537#537&'#5323&>7t7%2ZqD7[q><2DP?4 >/H@'tNQxF"<32&#"32>7#".'#"&54>32.#"32>d+)H!+.#  ! i)   &6$M,#g7"9|1Jd2>32#"&#.'#"&54>32>7.#"2654&#"  !"!'<N9 5*+(*9N;3-1    )11-1)       !0*5Y& Y5*0 = ! ^dH! !0Ee3VSZsS BLd " E Ed V" E# E E_  6%654&/7#"'732=.546754632&#";P+%U$- 'N8%<>! ^(: BEdH! !0>H W&<  >/&/ zJYpEe3V ,6+9?A7.2")Q8?J,23 ' A8?::F/;1+#,!%! J@3 7#7'&#"#>3273326=3#"/E/571*q@590'*NFK!%:E*$"t PJ(m & 2 JE( &7#7#537'&#"#>3273326=3#"'!!E;tT7/571J9@H590#(AiF$NFK,e !%:EvF2$&  J2,#7#537#5!733#3!"#>32326=3#"/&E!=C3273326=3#"'3!!!E?c5@N/571`)@6 59024= FfF2NFK8Pk!%:E bFfFIp& J JnI,:7#7&#"#>327'&#"#>3273326=3#"'326=3#"/E\ /5715I/571]>@L 5905>590'*=,NFK_/NFK7o!%:E `$!%:E'35$"#>7>2#.'.267>73"&'.'3C[Z# Z[Z "Z[" [ZZ # +5  5+  +5  5+ 2o &  u2%!#7#537#53733#6K6*3K3*FFfFwwFf(Y & 2& 2% 2(  %!#7#537#537#5!733#3# .K. (K( FmmFKFKF__FKFK- %!55% v8CC3EOO- %!55-5v8CCxEOO- %5% %5% vvEOOEOO- ^ ?367>73#.'.#"##7#>76?&'.'3323lKE+" %?&U5 "Z-FKC1# (D&Z8 #Z- +5\5+  +5Z5+ - 75%737'#?-&@K0hJ0K;/E|r,O;ZOhsApA-3#75?'57YK@]OjCCCCBj>-!!#7#5375?'5737TSK.KD>GKR$yElCCCC=OKfOtAT1.".#"'6323275% $YLF%()|!YPG(v.//P/0bEOO.".#"'6323275-5$YLF%()|!YPG(v.//P/0bEOO-752$7&$-|mTqgkxIfEs\OL@DJOZu- gC( [' 2: g( \' 2: h-752?367&'#7&?-MK651\;!3R3`vGKRo "EK#)OTP<4OU7)-33"#756?.'567&PK[qeCK+3.Z8#2Q2^u$ $*EMe!'OQS<3OS7]7X!".546;#;~Fr\3'IX9DwRFB\2F7X mIX733#;#"'#7&546;7Xo-0BP? 0F99FbJ44FBFF߄6."3!!#7#537#53&+532654K@G^K0Oc=nyOK/F::F.F(F-tV( !&'67%3 &=33265/!RCffCR!pHHfdK&!fQaaQf!ƃba4,%!!!!,MGPH v|B:' 2= vB:' 2; wE7 v=-@E7 z|+ 33##5#53$264&"6  i66A66^M+ !5!$264&"6  ~/6^M+ 77''76  264&"''''╫'&&'+  '264&"6  H&`>&`JM+ 2#"&546$264&"6  &$$&%4$%(9MLq"!!#"F"F"D" !l!#!5!F}&\F%!5!3FFFF\L"!!#xF"F"L$$ !!!!#nnF$FpF8>>-5%>H}>>7->H+~>!>' 2\ >%>' 2` !##3XXM = =%#".5332653M\^MRYaRL``LXS W. 2#"&546&$$&%4$%(@m 373'7@))j(ii(6||L}MM}> ?'%%5H%%ݚok Bk( #367&'!!#7#537ȏ7K53.\:$4S3axV5KYxbnc5 !u!'OSP=4OV7F77F(ELx( $33"!!#7#5375>?.'567&mK`h]UkvK#B=<75I)9V6f~#$EDF77F&.O06C>7OZ8YB:33#!!!!#7#537#!!K3:^']%K%,P'wsشfGHNFJJFNVcB:%!#7#537#53!5!733!3:~$K$+O.}y-K-4.FFHHF\GcHZZ\c' # 32654 &#"#"''7&54632JVtWq$[\טfa%aYטh*FJuIq#[g[b%bfYb!##bFFmHe##5eF'F_|333_FmFIf)533fFF4 4632&#"dH! !1ZsS BLd6;&#"'7325;%<>! ^&Ee3V;XI %"5>32&KY9UV9YY@9229@Y;XF ::#:;((:03#;;(((8T #57>7T_{RJ(R:b7(8z 3#(RR (JT #.'&53T.:R(JR{7b,8X X8X  ,KX! YX8MN#3#LLX8 #L X M 3# 5L78,O X8,  7 ,  X4;$#;U$?/1%/8)/)3/6#/2+3/7,/;6#G/()/7,;=/4/1(G/()/2 75/$0/&.=/%"/(/`np/6h%weo/+v7edkq//y)ddp/7d9xdap/)d)wfo/&r5fbxq/6t6jdo/6r,dO/(//,'1Y/';/&&Y/';/&&Y/';/&& Y/';/&&?/$/.1%G/1/67<Q/(3/7,%?/&0/$ 1lwh/(o0tlD/64/8 %[/(=/61&a}p/)w6i`}m/6h*p_}p/5w6gb}s/6j8{bnn/6q3bY/';/(%/  !"32654&'632#"'#57537BPPBDUS9ihx|hk;K??SBoooY^p~VZC2@2zN3@%53!53G0HHppk/1FM / #!3##52672#"&546"32654&%54&#"#546322#"&546"32654'.sG/3)(0=T@BW=Z) Wҕ՘bTl+r4 QK.=.-97- ?ST@9P<Ւӗ!l\|c'-#2>M#67>32#"&54?32654&+532327654&#"72#"&546"32654'.66@&=PDQTC32"32654&2#"&546"32654'.3)19 L;LS>%C!M+en)67*&//ҕ՘bTl+r ^Q DSAE\'9nb<(1=.3C<15?EՒӗ!l\|c'-# %!#>7#72#"&546"32654'.8SYDmMҕ՘bTl+rH*yib\Ւӗ!l\|c'-#"-9H"&54767&54632'"32654&"2654&2#"&546"32654'.UUS/HN<;I)Q&..%&1.+)46P41&ҕ՘bTl+rnX72#"&546"32654'.-'=9"4*'>T.*R),-,K4IH4>4IH42&g2 !!!5%!5!%5!!52DPD6PPPPPFFpFF2j@2 #'+/37;?C%3535#3535%5##3#3#373535#35353535#3535!!XFFFFFFF*FFFFFFFFPPPPPPPPPPPPPPPPDFFFPPPPFFPPPPPFFFFFPPPPFFFPPPP2 % 5#'5'35!!n4XFaG2D25~IX̌q硡Xᚚv2m2@2  $(+.26:>ADHLPTW!#7'#77'73'7'3'7''7'7'7#777'/3'7''7''7'7/7'Dgw<^;;z=c@?@jF@FX55{=b??xiFEF#F@F@F@FFF;;y=c@@@$@F@E@E(@@@>>D2<;;=@?@iF@F55>@@#FEF#F@F@F@FFFL;;$<@@@#@F@E?E@@@M>>f35'3#PSS/d !!dX d !5%!!X/A&r@C&s@l`!!+` l`!%!!"+0k0 #Z)Z#Z% !33xo %'!sxEe/b^ x^ @^ y^ @"x3 0{@^ /x^@@] /7-3Ռ#Zx}#Zy}x.{^  >^  4> xs /{@^  x s ] /}$ gg gg$% gg?gg$ %'7 gg͎gg' 2#"&4>"3264%2#"&546_d`x|ّԕPa`=컵Ք՗ # > ttT88'"3264%2#"&546 x|ّԕ컵Ք՗' %-39?IOU[agm3#654'&'7'&'7'&'7&#"5632"'52767767767%#&5473&'&'&''67'67'67=< 8 q*  KQ+ 8 << : s-KO-: y KQ*"5 8k;;8 q,  JP. 8 d 8 p-' #(,2#"&54667&67&'%64ԕwZZ!!W!=Ք՗j] Z B & Xn?BD=' *"32654&'2#"&4>"3264%2#"&546IqnILklG_d`x|ّԕprnJJp:a`=컵Ք՗' 2#"&546ԕՔ՗'%2654&#4632#"&vyՔ՗.x|ّԕ' '@'5@' #"&54632%"3265!Ք՗uv|Uԕ֚z{y'"!#"&54632v/hՔ՗xԕ'"&546.՗ K^ !"32654^hII43JXIhII252"2#".5467"32>54.%!!JNLOLOSa^]^^]]ADRLLQTJJ\^`^]^]^D2[ !#4&#"#2!462ʋcr˒(vt2a ']#463"c<֙x]7U5@g 'o@'] "#4632#.w<֙<wԓy'g R!RDDRR@RRRj@2, "32654&2#"&4<,,+~hIJ34I- +* !]J52IIh2!%!!,vDX2D2 27!%!!a[vD.\2D2 2 !3!%!!c/vDXX2D#Z #5 !hw"hh3#Z%!33#Z7! >e3>j "32654.'2#"54$㖛f`ck22 !!!!!!2D2X2DE\E2@2 22@' 2#"&546!32654&'ԕ~j  q|oՔ՗8 j.pys '@' '5@jO%!572hO2@f@jO!%!!M2jO!!O|X !%!!4pH$p|X !!p pjO B?_ 654> 54.''#"547&'#"&5475#"&547&'&/.54>75&'67.5467.'>7'>54&'GSRL#q 08"5"( ;N$V&[&-2F >3'  x))[&V# ;,.'6:s(2". U_^T%  T`acMUTH)xnABp`L_ P #-(86C# =**rY-{/r2)T"5 ] ]#  04 T*1r0{,4G=:J-?xIWP!LJ "   LMpB@n~YT"O$%3!56767#"&54>7#"I(,C$U,T 4R5K,FJHGKF-K5R4P* 8vPJ5-J=BkCBjB>J.5J#:.'.5463267632">54&#"#".'&\Qm<+U>U22F=W6E1='5?&DJF.84?/ 9KOY/?VK,6"8T;8pW/F4%KFR 7(/hX[c/398 /!*J"0 &'67>7.(8F!JE;;8-0>P<6jj1?0#8!5>7#"&54632&5462>32#" ".F--F/! O&@\S;""']]'  ;S\@n%%9%./$:#,"(_CA\ 82?ZZ?28 \AC_"OC%3.'32654.'3263!56767#"&54>7#"=!28"L2.@,740 ??;%@.K((,C$U,T 4R5K,FJHGKF-K5R?('t?.!7&//O09Z40='.?J4P* 8vPJ5-J=BkCBjB>J.5J#>32.'.54632\Y6=W5QTMQm<+U>w0JFT;6phiBKOY/?V"0 &'6(8F!J0>P<#/h%3.32654&#"654&#".#"32>!5>7#"&54632&5462>32#"!28"c`%G<$vLB44A2.+8!?D(.%  ".F--F/! O&@\S;""']]'  ;S\@n?('H538 l6&DF$e#A37G )(H%9%./$:#,"(_CA\ 82?ZZ?28 \AC_7 %#"&46323B,-BB-3"S%..J.-7"&46323#54'&BZBB-6 T&\%..J.-7mC88m7#"&4632#"&4632"F0/FF/:#F/0EE0:"ks&--L-'&--L-b75%#"&46325%#"&4632wF/0EE0:"F0/FF/:#=='&--L-ځ&--L-Bp #"577>324&#"326p;W_&(  FNW%'30;b,C|R0  VO7&'32.'>?/&54654'2>2%2367>7632327#"&'&/.'&547#"&'>7&'>;&5477654'4?&54?&472>54'#"&'#"32?632#"'%>7.#"327'&547.'&'6324654.'.#"3263232654&"#"'.'&'&'&'32?6=#"'>7>7&54672>7327"&+"327>32&#"54#"+654.'676794OgB/4PB6*'#C  / Fs3IJI    +b] i ),/KImL W!+a>E,' Y3q2VU U (%#   ([]DZ A'$04[,  ).P() -2>c`* f8''j)6 -M01$"" 8 9+ "# vpn #AYI[ o#,&g!!jV0D9'0*A   H ! E"   !   :## H% G " # O0:/B4G/93NF W  >FH    *{/ ) N !!" %5T Em*a &x18_ Mx'+26LL 8< de ePd<ddH !# #7H@__[$lH )33'__[ll$H )! 7'@𼌌ll0462"$462"#3\+>++>+>++>PPy>++>++>++>+0!# #3UUUc033#0UUUc(H !!!!!PPPH 35!5!5!5!5!H@PPP(6#264&"462"264&"462"!5!"."".qPpPPp"."".qPpPPp,m.""."qpPPpP.""."qpPPpPLPHr3#3###53#533蘘Z蘘ZPPDP8PH ! ! !iVKJWH462"462"462"t+>++>+>++>++>++>M>++>+>++>+>++>+H ! rKxW80 66  >4&8[Rd|lU080iT/8/jt>|fQm[=|dH462"462"462"462"H+>++>++>++>++>++>++>++>>++>+>++>+>++>+>++>+0p3# #`__$<| 3#333#!PPP`DlDH3#35373#`_P_`ll#4@462"462"462"462"X+>++>++>++>+>++>++>++>>++>+>++>+W>++>+>++>+H !!!PVWP6"462"$462"$462"*+>++>+>++>+>++>M>++>++>++>++>++>+:  %5-5%m8=UrKVrrVK0p)5333pPPx<< "3"&632644#<||ޞ||ޞP++>GPP>++>+?g &5467%!5!32654.'XO<mKIf^]g&?4_`v{aI~,PP6]B@L=I,D'6F264&"462"264&"462""."".qPpPPp"."".qPpPPpo.""."qpPPpP.""."qpPPpP80264&"6  <6<704632'#"&"327'7654&7⛟UL8Lh}zcS9>j⛇hL8LUȮy>9OgzH 3###3!8PPPlD<462"462"462"<+>++>++>++>++>++>M>++>+>++>+>++>+8H'$462"462"462"462"462"+>++>++>++>+>++>+>++>++>++>!>++>+>++>+>++>+>++>+>++>+80462"$264&"6  j+>++><M>++>+6<7046327#"&2654''7&#"7ݠhL8LUݟ}~>9SckUL8LhݍzgO9>y8'/7%462#"&54632"&547&#"327&6264&"264&"PpP6SX)%UW9PpP$ y~ $O"."".""."".8PP8,F'ᛠ%H.8PP84)~y)K.""."z.""."6r 3##5333#PvvPvvDPDPH 3# #3(`__`$ll8B264&"264&"462#"&54632"&547&#"3533##5#327&"."".""."".qPpP6SX)%UW9PpP$ oaPvvPas $Q.""."B.""."98PP8,F'ᛠ%H.8PP84)pvvPvvm)H!3533##5#!!PPPvvPvvP0 !!*(uH7!3!3PPP(< '%<w..BB6#5.5353>53#4&'#5#46wPjPiPPiPjP a@r q@` `@q r@aH3#3#'P_`6+.5353>53!!#4&'#5#4675!5!wPjPiP$܋PiPjP& a@r q@` 8P8 `@q r@a 8PHr353!3PP .575HTHHTHFt%1264&"264&"%2"&546!2"&546'#5!#3!53m%34H44%34H44rp<3%$34$#43%$34$#4<<<<Ft&19A#5!#3#3#3!535#53#532"&546!2"&546264&"264&"?r%34H44%34H44q<<<<<<<<<<q<3%$34$#43%$34$#4MF^2%#"'#7&54>7&'#"5325332?373327^5R.($E8A":!D<a?VC4B!;7>謬DA,=3,` .F!3!535#5&5#5!#32>5+Xo@C6*# :LS H<<2<73#3!535#535#5&'LS ӄ@v9&*# O,bD<&<<<;> <<<<<<<$F %-5=46323>32#"&'#"&5467##"&264&"264&"264&"FR:2L L2:RR:2L 00>RtR>00 L2:/B//B/B//B/B//BtR>00>RtR>0 L2:RR:2L 20>B//B//B//B/B//B/F\5=EM462"$462"#"&'##"&46323.54623>324&"2$4&"24&"2((((R:2L L2:RR:2L X0>RtR>0X L2:/B//B1/B//B./B//BR&&&&{tR>00>RtR>02 L2:RR:2L 0>B//B//B//B/B//B/F326=33!53#"&5467I4D<}})u4Mlw_?`):e>謬F2533#3#3##"'#"5325L<,E E<٬<<< >>謬F:D#"'#"'#"54675#"'#"532532536532>53323265)9= =H%DOOe,E E<<>H9 C^t?[>>謬o#2992##29=A,-6eMFJT$462"462"#"'#"'#"54675#"'#"532532536532>53323265(((()9= =H%DOOe,E E<<>H9 C^t?[>>謬o#2992##29=A,-6eMF!#"'#"53253253353#5#,E E<<>謬]CEFR1"#43232>53#"&%#"'#"53253253353#5#>謬]CEF 573#573#N<<<<F'F'F3##"&546;!5#"32655Q>>>N=Q< (!6-E9j9E-l-E9j9E-6#J_3333`J+1"]k{{k]""]k{{k]"( FJ09B%#"'"'#"&53327&546227&546232>5654&"654&"Q>>>N=Q< (!6-E9j9E-l-E9j9E-6#2(2(J_3333`J+1"]k{{k]""]k{{k]"( JzIaaIzJJzIaaIzF>#$462"462"462"#3!!!!&&&&&&<<yy((((((D<<<F>$462"462"462"#3!!&&&&&&<<y((((d((D<F [>54&">54&"#5##333327.546227.5462;333#5##5#"'"'+*4*n<<<<<< 6-7n7-l-7n7-6 <<<<<< N>>>>N )6)FAAF)6))6)FAAF)6Dž"#(A)f]]f)A(#""#(A)f]]f)A(#"'3333F4=2654.7253#.#""&54>7&#"#3>326! *.* !)k<<n.6-' DrD '-6.n<<k)N>> ,(DCCD(,9+';S"2)(d__d()2"S;v+933F3333#####5 <<<<NN<OO<F +32654&#"326=33!53#"&54632#"!4"8FI4D<}})u4MlyK+9H1>c' t):e1``'L~:CG6&&&&n*[D?`*o>;‹ZzzZ/\yMKIOF@IR462"$462"#"'"'#"&53327&546227&546232>5654&"654&"H((((EQ>>>N=Q< (!6-E9j9E-l-E9j9E-6#2(2(&&&&0J_3333`J+1"]k{{k]""]k{{k]"( JzIaaIzJJzIaaIzF#"'#"53253253353#5#3!53,E E<<>謬]CE<<F/$462"462"'#"'#"53253253353#5#3!53?&&&&,E E<<<<<}}&&&&d>>謬CE<<F'#"'#"53253253353#5#3!535##533,E E<<>謬]CE<>QtAHHAAHHA,G+/8SG[5*+6gD+*>>QtAHHAAHHA= F Y%32654&#"#"'#"&532653265363232653#"'&''767&=#"'#"&532653265:.2"",E ED?<R<R< ?+1H)" =%">QtAHHAAHHA,G+(4J59>G[5*+6gD+*>>QtAHHAAHHAF% BٷFr GQ&#"32767632#"'#.547&'#>54'#"&5463232654&#""7 "d\CC\3`);H1)0 !00H4( ;QQ;,,H8+# 0)1H;)`." 7=^+ 3:EE:~J1+G8D-paU ]Yv0SF$HH$FS0t`R !`Zs-D8G+1J +F"*264&"462353#5#"&5467.264&"EfEEfii2<<3ii.&'-532>53#"'#"&532>532>53#"'#" -:- < -:- 53#"'#"'2>532>53#"'#526=#"&5332>5463"7%- 7&'#"5325332?3327#"&((x((":!D<謬DA,=9X.+?|FB462"462"462"'4>7&'#"5325332?3327#"&T((x((x((":!D<謬DA,=9X.+?|F\3632#"&=#"32654&F53#"&462"532653#"'3"&54654'&'#"&532653654&5463"64=8r83#264&"462353#5#"&5467.264&"$462"462"<32#"&54>73264&#"=H<( h9dtU|=VR/iFVOK5`8Gd6Q9KUuKYwCY:NafQsF#+>32#"&54>73264&#"462"=H<( h9dtU|=VR/iFVOK5`c((8Gd6Q9KUuKYwCY:NafQs/((F"*2<53#5##"54635#"'#"532532536462"462"'"3265<>謬oJ((((K.-6eMF %"&63"#"&63"ӕ~ӕ~<<.<<.<F+73264&#"5632#"'%&#"327#"&46325O;SUllUS;BLii+'F;SUllUS;BLii+'& )H) ?MxM z )H) ?MxM zF33##"&546;#"3265<53#"'#"&53327&5462'654&"!( >N=Q< (!6-E9j9E-V2(1+J`33`J+1"]k{{k]"OJzIaaIzF09462"462"2>53#"'#"&53327&5462'654&"-&&&&^!( >N=Q< (!6-E9j9E-V2(((((1+J`33`J+1"]k{{k]"OJzIaaIzF1$326533253#"'##"'##"&53325hT"27&'#"5325332?3327#"&":!D<謬DA,=9X.+?|F,!4&#"##"'#"53253253632#4&#"632,4<,E E<<<#+4S>,4",4S 66  >>謬hT> 66 WT>F#$462"$462"##"'#"532532536((((Z<,E E<<<(((( >>謬F1,5>"&546323>3"32#".5##526733254&#'4&#"3dt\Q(:38 Vkf#,thAdt\Q(:38 Wkf#F3@qOK3@qOKOqK]qEeLP-3212bej)u+=HODCQQCDOH=+u)jebǵDEJ?T0-u'&'&-&'&'u-0T?JEDFp-2#"&'732654!#3!52654&#"'>322bej)u+=HODCQ<<QCDOH=+u)jebǵDEJ?T0-u'&'&x[y&'&'u-0T?JEDF!2>532>53#"'##"&5 -:- < -:- 53#"'3!53d+FTY< -/8<8/- 532>=3##"'#"&5$462"462" -:- < -:- <<-D]22]TY &&&&.RD))DR..RD))DR.'/;yyY((2&&F6%-53#2>532>=3##"'#"&5$462"462"<< -:- < -:- <<-D]22]TY &&&&'.RD))DR..RD))DR.'/;yyY((2&&F3#3#3!!!!F<<<<yy'<<F@%"#43232>53#"&3#3#3!!!!(!//!:RR:0磣</B/32"&6264&"6264&"\\\"Y}Y:cc:Y}Y<\\\^\\\N[[[!92~:00:~29Y\\\[[[Fj08@$462"$462"264&"47.4632>32"&6264&"6264&" (( (( \\\"Y}Y:cc:Y}Y<\\\^\\\(((([[[!92~:00:~29Y\\\[[[F 1<G73265"4&#"2>#"'#"&547&546326324.#32634&#""UoKj5<"UoKj5#"'#"&547&546326324.#32634&#"((0((>(((("UoKj5<"UoKj532%3&"3&#"@<@)R1<;1%:FE{}k/Q<==<#/E/9sssF7273>7&'#"5325332?3327#"&547&'#"'<A :!D<謬DA,=9X.+?|Q"%5>F !#"&54733273%u8Ml\PpI4F<]Bpknm):GF4/264&"!5>54&#"#46323>32#"&^^^*i66J.75<\LKi;,T |Q[[T|^^^S!K'46$;@!*;LY^%A.6 <OkpFb /5%#264&264&"32+"&5467.546"3r=&5432654.#" I <+-dB!!H) 1%$1<H."d I +!E#o!!G=> ]Yv0M;: !`Zs-!!oD!+ F #5!#3!53r<<<<F 2533!#"'#"53255#L<,E E<٬ >>謬cFv353#5#3!53##53<<}}<<CE<<BFFv%$462"462"'353#5#3!53##53((((9<<}}<<&&&&CE<<BFFv##53!353#5#3!535##533k<<%<<}}<<XBFCE<32'>54&">32'>54&#"462"1?oxATyQ*+"HVv|qMY K3ZOk5TyQ*+"HV;ek&&{+0F"eN8H *6)4C)J;O C+?%eN8H *6)4Cp]1((F46323>32#"&'##"FI3+C C+3II3+C C+3:fI5))5IfI5))5F+  3#篮 kmF1462"$462"% 3#&&X(( Z&&&&.mF  !5!!53'!x<<}ڂ'bF)2>532>53#"'3##5#535#"&5 -:- < -:- 532>53#"'3#3##5#535#535#"&5 -:- < -:- 75#"'#"532532537#53265OO%N.1,E E<<>謬;";&"-6eMFR9D"#43232>53#"&#"54>75#"'#"532532537#53265>謬;";&"-6eMF733#5!#353#5#3!53#F<}6}<<}}5}<<}}<<}F5 <"3265$"2654'##"54635#"'#"5325325332#"&54Jh((,6..UOOe,E E<<<0<<30< K.-6eM*(*)*F^tAe>>謬oI64GI7!F33!53!333&

=&5432&#"!&=#"', I<+-dB!!."d* I f_Z1%$1D!+ o!!G=> !`Zs-!!o +!E?F ,7&#"654.#"353#5!6=&5432327632!&=#"' I  I6b<<`d<+-d#<1%$1 +!ED!+ CE!!oo!!emmFe%##535!5!3##<32#"&'##"q&&&&I3+C C+3II3+C C+3((:((fI5))5IfI5))5F<(08B2#5264&+#"54635#"'#"532532536462"462"'"3265:RR:!//!OOe,E E<<<*((((Jh((,6HRtR>謬oJ((((K.-6eMF+$462"462"462"462"7#5!#3!53d((((((((r((.((((.((<<<<F5$462"$462"5##"&532>532>53#"'3((,((2+FTY< -:- < -:- 532>53#"'3462"$462"`+FTY< -:- < -:- 532>53#"'3`+FTY< -:- < -:- 54&"327&5462327&&)532>53#"'3$+FTY< -:- < -:- 532>53#"'3`+FTY< -:- < -:- '/$264&"462"264&"462"$264&"462"^;R;;Rw^^^;R;;Rw^^^&;R;;Rw^^^R;;R;^^^bR;;R;^^^R;;R;^^^F(0264&""&5467.546232#5264&#264&"EfEEf3ii.&'-ii2:RR:!//!EfEEfS~JJ~J:PSrrS5XX5SrrSQ8RtR#2::42:p0F)3;C3253253#"'3!53#".54632#"'3254&#"462"$462" 37{<#2::42:p0?((((F;EO267#"&54632#"'3!53#".54632#"'325374#"326%3254&#"/73 5;824<&:%T'}}'T%:&<428;5 37{<444480 :24::2&;*0*<<R*0*;&2::42: 08.00F\;264&"264&"#"&463235463"3>32#"&'##526=/B//B/B//B L2:RR:2L UR:!/[ L2:RR:2L [R:!/B//B//B//B/n0>RtR>0:RRtR>0:R32#"&'##526=##526=/B//B/B//B L2:RR:2L R:!// L2:RR:2L /R:!/^R:!/B//B//B//B/n0>RtR>0:RRtR>0:RIa53#"&3!2#5264&#!#32#"&'##526=##526=##"&4632/B//B/B//B L2:RR:2L /R:!/^R:!/# L2:RR:2L B//B//B//B/20>RtR>0:RRtR>0F?GO5463"35463"3>32#"&'##526=##526=##"&4632$264&"264&"R:!/aR:!// L2:RR:2L /R:!/aR:!// L2:RR:2L p/B//B/B//B:RRtR>0:RRtR>0B//B//B//B/F4&#" 632'6/!"(;23#"&5467&'!#26'3."EfEEf4 ee 44 G&.ii.&G (BZBAXA~JJ~J23#3#"&'#5367&'!#26'3."#26'3."F4 ee 44 GF 44 ee 44 FG (BZBAXAAXABXB75#"'#"5325325373265OO%N.1,E E<<>謬;;&"-6eMF$462"462"7!!!!!!((((vvN((j((=<F1<462"$462"%#"54>75#"'#"5325325373265>((0((OO%N.1,E E<<>謬;;&"-6eMF!264&"&"32"&5467.54632EfEEf fEE3Jjii.&'-iKI3~JJ~J,#J~JtRSrrS5XX5Sr6FR7"#43232>53#"&264&"&"32"&5467.5463232"&547&#"632"&547&#"3!53<15"0"&JKA5"0"&AKp . *!//!>1 *!//!<<<F?462"$462">32"&547&#"632"&547&#"3!53((((p<15"0"&JKA5"0"&AKpf&&&&= . *!//!>1 *!//!<<<F7>32"&547&#"632"&547&#"3#3!535#53<15"0"&JKA5"0"&AK . *!//!>1 *!//!<<<<<<<F7?G%3#3!535#53>32"&547&#"632"&547&#"$462"$462"<15"0"&JKA5"0"&AKb&&&&<<<<<< . *!//!>1 *!//!<&&&&F#,5$462"$462"73!53322654.2654.k&&&&ªp54.#">32'>54.#"6!2'>54.#"<:00+&D, }XE500+&D, <200+&D, }W';*((-=%4';*((>/W';*((-Fe:7?G462"$462"#3326533265332753#5#"'#"'#"'462"$462" ((((<<<98=8898=89&<<"]1-dc-1]((((((((& eLLeeLLe% ____((((F3<'264&"462"462"462"$462"&\~\\~{{{,&&.((N((_~\\~\{{{E&&&&&&FN5!5!264&"462"F*{{{ؘ<7!63232>7#"&#"#"547>54&#"#4632!654&#"#463263232>7#"&#"#"Gv%3$<"    &3f qE=C4*=NeZC4*=NeA(W"    &3f f@!Q2T   N 88 e,;VJ!:myT):<';VJ!:myT"Ul>x   N 88Fk&.7@43267#"&547'#"&54737&3265732654'654#"hu7Br%J" (u7Br&"Yu7B<\(pv=V=QV=3\MXE0 < DK\MWG0!\MnQq$B533273#5#"'#"'#"'#"264&"FВ/= = &<<"KNMK!"ShVoooВhIh"*484**48 6666MbIoooF7'7%3!53#5!#%'7'9>44?}}}6};?44>lll<<a<7&543267#"&54732654'654#"32654'654#"'h(u7Br&H'(u7Br%%i(u7Br%J" (u7BrV=QV=nV=QV=w' DK\MWG0 < BL\MXE0& BL\MXE0 < DK\MWG.5;m)A;5;m)A;5;m)A;5;m)A;FGPYbk462"$462"%#"&547'567&5432>7&543267#"&54732654'654#"32654'654#"0(((('h(u7Br&H'(u7Br%%i(u7Br%J" (u7BrV=QV=nV=QV=X((&&:' DK\MWG0 < BL\MXE0& BL\MXE0 < DK\MWG.5;m)A;5;m)A;5;m)A;5;m)A;Fz92654'"&=.547&#"#4632632#4.#"26=3=:=X\6EpE4D^'-!( 32#"&'$264&"264&"[ L2:RR:2L L2:RR:2L ;/B//B/B//BO0>RtR>00>RtR>0?B//B//B//B/F\'/7$462"6462"'#"&46323>32#"&'$264&"264&"5&&&& L2:RR:2L L2:RR:2L ;/B//B/B//B&&&&0>RtR>00>RtR>0?B//B//B//B/F\,'/7462"462"'#"&46323>32#"&'$264&"264&"&&&&c L2:RR:2L L2:RR:2L ;/B//B/B//B((((0>RtR>00>RtR>0?B//B//B//B/F '%%X =Hf{GH{RF$462"462"'%%Y&&(( =H׬((v(({GH{RF/7>32'>54&">32'>54&#"1?oxATyQ*+"HVv|qMY K3ZOk5TyQ*+"HV;ek{+0F"eN8H *6)4C)J;O C+?%eN8H *6)4Cp]F/7632'>54&">32'>54&#"<<2353#5#353#5#"&'##3%#26'3."#26'3."4 FG 5<<5 ee 4<<4 GF 4<<4 ee 4<<\BZBAXAAXABXB[/2ZddH]]HddZ2/[ddH^^Hd3::o188A199m2::F=CIOU462"$462"367&'##533>2353#5#353#5#"&'##53%#26'3."#26'3."((((3\ FG ]<<] ee \<<\ GF \<<\ ee \<<BZBAXAAXABXBZ&&&&[[/2ZPdH]]HdPZ2/[PdH^^Hd3::o188A199m2::F #3#3#A<<<<<YYXFR!"#43232>53#"&#3#3#5332653#"'#&'#"&5332653<-&<2?<55332653#"'#&'#"&5332653((&&<-&<2?<55332653#"'#&'#"&5332653((&& ((&&<-&<2?<55332653#"'6462"$462"462"$462"0]wSZ<532#!12bej)u+=HODCQQCDOH=+u)jeb2YO=DEJ?T0-u'&'&-&'&'u-0T?JED<<FA$462"462"462"%2#"&'732654&#2654&#"'>32#!&&&&u&&2bej)u+=HODCQQCDOH=+u)jeb2((((d((=DEJ?T0-u'&'&-&'&'u-0T?JED<<F=462"462"'2#"&'732654&#2654&#"'>32#!53#5&&&&2bej)u+=HODCQQCDOH=+u)jeb2 <<&&&&=DEJ?T0-u'&'&-&'&'u-0T?JED<F-2#"&'732654&#2654&#"'>32#!3#12bej)u+=HODCQQCDOH=+u)jeb2<<O=DEJ?T0-u'&'&-&'&'u-0T?JED<F%%3!53#"&532>532>53#"'+FTY< -:- < -:- 532>53#"'&&&&+FTY< -:- < -:- 532>53#"'YR6&&&&R+FTY< -:- < -:- 532>53#"'3#+FTY< -:- < -:- 532>53#"'353#5<<+FTY< -:- < -:- 532>53#"'!!#"&535#35#'35335 -:- < -:- 32#".543264&#"J9:!D<謬:&IWuKYw7[8W%)afQtFX!75!5!F<<,<<F|3# #IEzyE|,F`753'773#''7FC4CC4CC4CC4C>d(L"(*!//!5QO7Qk>>d(LF_327&5462#"&/4&"327&5462#"&/5#".54623275462327&5462#"dN&"0"51+,{{N&"0"51+,?35"0"&2@ؘ@2&"0"53B!//!* *W{{WB!//!* *" *!//!)tllt)!//!* F&33265332653>53#"'#"&53&5>53&'553#"'>謬U, $F0%654&"333#5##5#"'#"&53327&5462;`2(<<<<<<"N>>N=Q< (!6-E9j9E-6"JzIaaIzS'JJJ33`J+1"]k{{k]"F $462"462"462"!!!&&&&&&&bb&&&&&&<aF 573#%3#'5N<<<<F'E'FF@2'%632"&547&#"632"&547&#"3!53Q5"0"&JKA5"0"&AKpT:hG7 *!//!>oD1 *!//!<<<F#5!#!2#5264&#!3!53r:RR:!//!<>謬{F !!##PS<'F%#32>%!#"&'#"'3!5332>5d~( 4F*) '*'p<( `P38m>謬]<F74&"2264&#"'632#"'3"&547&=.5462"/B//B7//7@AkVIWa?T1+J`##0>RtR>08B//B/=:=X\**^TCDQ^'-!( 3232>7#"&#"#"5.5462Q&&&&RtRRtXH"    &3f IavvaI((((tRRtR    N 88H qJSuuSJq F$264&"%2#524#524##"&4632tRRtRR>>< qJRvvRJqRtRRtXE Eqn325654&#"#F<)u4Mlw_I4D<ـFc3232632"&5467#RtRRtXH"&   &3f IauuaItRRtRT  N 88 qJRvvRJq F/462"$462"264&"7"&5467.53265N(((( EfEEf-'&.ii.&'-54'#"&546326/B//BA@7//,0>RtR>0##`J+1T?aWIVzB//B/\X=:=* L2:RR:2L *IC@L=Q< (!-'^QDCT^*F15=654.#"3276327#.=#"'#'76=&5432&#"Ё I<+-dhH) 1%$1<Hhd* I D!+ o!!}Ӏ3 ]Yv0M;:3}!!o +!EF!%26=3"&=.5462264&",!@!32&54>32#53D!$MNHMP$MwR9?!H!57UB$,8& PP9 $ DYHH-3O@*,   +?_;>/%2 JLbm#%7'7&54637#'75H?1c>LK&II@7:/mke6$;B; AB&eLm!$"3###53%7'7&5463753=I$1MGM31c>LK&I0mke -).;7HH7#;Cf>&/173:>2>7#"&5467Y6   CL_bN\LD  :hj3!eb)4&#".547323267#".546&<(*2,2MS1 O_?/ C/G#^"5  !$ " +=;/QBx  :8QFY5QM'%  5&&_. $'2'32654&##5.+'676753VG E`\)H.2'[A!M)?E)#RDP(#$*GN70C33S{7253#5.#"327#".54>1G33 c$7B &"1(H$C%c.5L0 5$,@ '(f#2#".'732654&#"&546{ #-Uc#k3='I] <r4#53533##5./>54#"327"&54>32=>?&&3>>3?[C"/># .!-% !/ -;P3DD3,HB , :(&,*!.#'3##5.'&/753632&=654G4;E  491,$r^.31*1vZKW(=>E_/**"$*GD(#<%253#5&#".546V.H$33=_%&+><21_P *1(#CAB%m%'673#5.# ) JIg43Q+0   54'#"&543367B5oXO34P- (0" 0!'!C   LE;H_b@L3 4"668& #90.#"#"&5473263253#C PYM.9S 6 2833( W6-( -32 *ia%'63253#54&#".#"#"&54632 33**!" .9E%0 (  $*(-7,*@."*2654&##5.#'>75#5!#2#"'7%25"h <"3$4M(/RLJB5'$1@ wB'*&/5('33T.*'; -q7sl)33##5./>4'#"&5473267>;C3<<3 w/4D 5#'+!-$4CU>3+]2-.6-  &- 6 .,253#5&#">32'7>54&#"#"&546(G.33Iq%E!-8)A&2 J&U-.'$8, $&%8K!9 1!'2[>.'275#'47#5!##5./7#"&M   V 9P3[6 6 4(L     33./@:&p.,547#5!2#"'3267#".573254'#"&72654#y6:1, +":#_5_>%34b6U)2*4'(+"ZMT3,#2940_yHjv1 :jI,=8%E.72654'&'"&'#5!#he97GW2Jb+  @33"558h.73265&'.5#5!#3}1rrF #c2!?$~<]'m33 9OP`.1C.#53>54.##"&=#5!!3232'".'32654'CB * $W3:J C$6> !5  m0;  3+R3< +733 -B ./467#5!#3"'327#".'732654'#"&l'f 3 $7=G:'>) 3PE($"("3J 33G* _57T<-0+!,m!675#5!33##5.'#5.#7Ps(3>>3 3l4] ] D 3 /T?3u?"Sc, >%:<O2#"&'#4.#&5473263254&#"27#"546327>"3254#"532654,!I"?3 4?54'#53&5472654'lF~c :'7G}v4'* 9*13.Q32*9*2=*S-8' 3?6+2%M7<=2Q#Q.$"&=#5!#267#"332654'$8!$^C*3 !:"-?6:!33}6K&4d82R<-G'!3.7"&5#5!#32654#"'632r2ER (I  & n5#33 |& (0 C %;2.#"37#".54>#> f$8C .- ;;-*Qk.5M/>.,6& mm-33##5./654&#"327#"&54632:+33::3 $ 3 !=-,;*8R?34# 7F 5!$05 W3$"753##5./7.54632&u  Vm:32L8B*"  d(6&4/! 053=252-"( ? #!z.'5#5!#6?#&54C+G1*k`CV? 3 *339 (&+/).1.&#"&546325!5!#<6!(E5<^3 v)2+M3K.K9#>3253##5&''654&#"72767&#"KK;\PƓ2 (+8"?!J_T3:  ( 5 & /.*#"'73254&##5./67&'#5!!532')3l*<&I8/. ,2$%=0g21%11@ /!%033(* #<7.675#5!##5.#"75WmI/3 q(0!Q $KxD!33?c : "..24632&#"32>7#".'32654'#"&'5!u '    %4O:-K0* 3h=(1%<)4 1  (>?P#3SC1 jv:-@:33. *4'#32%#5!##".#"327#"&54674\# U:ea,)#G7 $*4,753309)5))30#(L.!##5./67.+35b-3h)!=,V=.#DZ4.36/;)8 <*>A5QM'%  5&&.%25!5!##5&#".#"&54>326 O3&)+#$$$3 4"*-%Z33>?$7( "E$)/).0263253##5.'27#".'#"'72654&#''/ #!7j3# % 8- #/IK[334 0 +#,1!&30".!327'654.'"47#5!##".x/b1a4rk7 $%Y( MK$33-;X ,J.%#".'#"&'6732654'#5!!3254':(4 "<6 " $78$=",6(3!'@* 33$H8 ([\.%)&/32>54&#"&54632%5!) (z/,"2 5%"H!'.W([6'37 ("1 ,X332}.(2"&=#5!#>7#".'32654'4632"" 3 !/!-*0L) 3e7,=$Z$+7~33{CC#$9" 8NbC u@#!K$:.'7"&5#5!#32654#"'6324632#"&n -@Y0A+a_ 7 !33"g+ '$*@EM. #74632#"&&+5!##5&/675O /CA \532{ +/0P>(( B336Q )-m(E(5QM'%  5&&7.7.'7'675#5!##5.#"75D++DWmI/3 q(/!R $L9 0 6"D!33?c ; ",'233##5./32654'#"&4732>54'r=Y3ll33P]+ 4]?? 9/( 0GC=W3&-!?2 JJ3Z0H..'5!4.##5&/7'7632#"'7326Bpk/&3>(y&)djF4Lf< 33#nAW6P!4#>#=>G83, ;X.7.#'675#5!##5 /AT&yg4,~X:<*6g&+33: N6NFo.4&#"3"&54632#53## R! *5'72BC3[? (&/733tY.'463&'7#53##54&#""&5& 3>wC3]+ ' )(3 .>3 333oZ((.47#5!#'>54&N2;ACZ+3xK833G@EE0 )! .>B2'65.#"#4&#".547326324#"327#"&54>326%5!M*9!#)/3=TFRD )7=&Q7-%y# +SaU7uG' &(09+'{9F0/#)( Z63,/"2)/# L3D06`*.33.(L1#B%''5NJ Q4A.#"#"&547326325./>54'"&547326733##wCQLZ/EF<4686q4)$1( ,(74#"#"&54632533##yCM /E#.DG 6 28V^!F O",&"'*& N  WC9M.3CC3' --$1 *;) *<7#)"5"% $%1^5HM3.+=.##"&547326325./7#"&547#5!##27#p#l1D.*" KN &Hc<3&   34O:0% L3 2>] (R8,33 $ a0 +.-5&#"#".547326325#&'#5!!#2654.'o+( t "+.*" ^KL=7(3(Lg9 ;(.' P1e33=() "%+? 8 7<.KW.#"#".547326325򂂮./#"&=#5!!32&'#2654'#"'}C J0E#.136E$"1'-4TEQ.##"&547326325#"&54>7>54'!53&5473##2654'r   g 2C.)# 33=EV2 Q6& 00ƯE 3r7F<"G/P90% L3):ZD N= 3\H8J",O63o.BA!"x@TV>NAqY.>.#"#"&547326325#"&'332654'#"&=#5!!3267#DPLZ.9S 4 56*3m3b[;RR%(F^K3r' M@'. *32 +[3'&?.33S2lF&.:.#"#".547326325#"&5#5!!>54#"'632#p#y "+.)# ?D 3 .q,.3' :&.' (&3 M/733q \&"!<5>./!!4632#4&#"#"&54732632&#"327"&5͘6 3.*>L, Vx'&w  >%.39!-]B4]I/A*3$"m&$ -VU.GK.#"#".547326325#".'332654&#"&54>32#5!B Q/D#.DG 7 83%.":,'3  $2BLF57 + 8&Kc33Mvu' ,.), '<) *l0'F+R'*7;X/:\IFa!#'" '}>33B.#"#"&547326325&'&/67654&#"327#"&546233##xCL LZ.EG<46PB`,p'@7) ? (V~Q]$iJ4QQ4<( M@*+ .;) +BW68;"*$3- 2DJ=7Q(ZX3.+3".5472632&547'#5!!67&'" '0!1-Hh'#* d.e,-.(5*f@*/:!3:gV2"33!S|^P "55:.#"#"&547326325./7&54632&#"753##yENYM.EF 6 46FkMq_<+( $yR3yUwW2) W6), .<) +EFL)[E/(8 DJ3325!5!##{B OYM.DG 7 65"5- "!4>)b3|P3m' W6), *<) */:'' , '&D2;(f33c.H7326325.#"&54>323>54'!5!##5."#".5472*"  9; ))&)H2 $k>j3 k "*.'3 -" !2% ,,>33-&54  ;'0%   s1<.#"#"&547326324&''654&#"'>32533##267&#"CPLZ.EG 6 462$%.# n6F3VV3 # S34 ?2( M@!4 -<) +) W"&!$ $1PE3 ,+R)!.>.#"#"&547326325./67&'#5!!2#"'73254&##xCL LZ.EG<199b($fp3@&69tMh," K94F' M@$1 *;) *IA] %M#6@33)9&1,&bNIg2 1 #3=.,3726325.#"'675!5!##5.##".547  8"z@4#:/ #*.) 2" H,6`-+335 ;'+* (&3{I`!!.8<"32>7#.##".547326325#"&54632&%5! 9 !1- -!49;$1C1g7aD0;3%: 3 !]#"J5214B "U>0!+$+_33hA.FJ.#"#"&547326325#".'32654'#"&54632&#"3267#5!A MLZ-EG 8 38 %9bC:"9%T,P"5R3O)4! K%3!%33MXc' M@*+ .;) +w1Jm_:Q?3Z7?#00):2G #!9 D833=.18.#"#"&5473263254&#""&547.'5#5!##5|C H YM.EF :56`#/*U.C AML93?59.( W6+* *;) +5+P,`)%""33B73N.'5.#"#"&547326325./67.+5!##zENLZ.EG 8 46)9-9Ye n>4a/:R#-2) M@!4 .<) +;CR 89CG33bN/- /jM.#"#"&547326324&#"327#"'#"'732654&#"563263533##ENYM.EF 2 56P%!, <" 4#I5N3QQ32) W6), .<) +Ms/6 +:')01=8k_  $W3.*.5.##".547326325./67&'#5!##5&'o#g #*.)" BdE*g-~IF3LI;0{V2 ;'+* (&3 /]G:%R#A33b{(?+7q\.6?.#"#"&54732632.#"#"'73267.'5#5!##6325CPLZ.EF 7 29 :(20A,- B_853@5"'2n' M@*+ ';) * N4?4j" EK7"33a84E.FN726325.'#"&'6732654'#5!##5."#"#".5473254'}   2 "<8 " $7W33   k "*.*8$= '3!'@* 33"<4 ;'0% &'3w$H8%P.:>.#"#"&547326325&/32>54&#"&54632'#5!zDNYM.EG<64_:&9 VB0d/7kY&4y{' W6'. .<) *Uy79!(# )7#$*9,JZ9o2Zq!!33.)-4.#"#".547326325.#"'675!5!##'~CP/D#-DG<19:!~oA39V0 %& ,-&- /;( *%-6b++33@>e X,q.)18.##"&547326325.#"'675!5!##'.'7l$;.2D.)#: !~A3X_:,n6(Z%O:1$ (&3 o-6b++33v&Q . I7QHY*.M`e.#"#"&547326325.#.'474.#"#474'#5!#2#"'73254&#"#25".5#7#3FL LZ-EG 8 38Q*  R+&69'j$044"  ,4 38a?  S3 2( M@%0 .<) +h5F 1#2  Q 33Y.!1&/ &,*4\D8 ,0)U.D%'675765254.#675!5!##"'#7254&'#5./7&',EX.4"kP*-D !4XlGq' %+!O3)"M."M.(,"m ?   [X5-33. Q>" 5$,3#3Y%( )0" V67723##"'73>54'32654#"'632#"&547./675#5!4&#"#"547326c' 'Fy52\,0!K  Y8H%%P #'Ks93@<(2 DoW1 A/DRkAY)<3!7!H, F^iH8#Q*&%4[54'32654#"'632#67AC K55 .

 H&;$6 .*>,C"<6) 1&,)#.$&<4G:)1050*0! 33.LT2#".'732654&#"&5475.#'>75#5!#2#"'73>5654&#6'25"7!0 "D-7a:#32M*.;!"+ 4Q( PT'; 6&$ 5$1@ u!0'*2"B_OGP85)9+/5*33.'*';)#oq7O.SW%.#'67.'32654'#72654&#"&54632632#"'7654&#"#5!w#) JR, 1.(!   *433b= >!)0%,*@,G"A4) 1&,)#.$&<4G:)1050*0!&;:#33.Ya2#5.#'67.'732654&#"&5475./>75#5!#2#"'732654&#6'25"A K !0 3S+!"+.Q132M*.;!"+!/U( PT'; 6&$ 5$1@ u !0';2,0#! FVGGP85)9,05*33.'*';)#oq8F._c.#"#"&547326325#".'32654'#72654&#"&54632632#"'7654&#"#5!EOLZ.EG 8 46371('   *445ʅ) M@!4 .<) + .*>,C"<6) 1&,)#.$&<4G:)1050*0!&;33.NV7&54675./>75#5!#2#"'732654&#632#5&#"#"&5473263254#"'25"&+$*W( PT'; 6&$ 5$33)1@)L--" 6 '-7z1@ uw ?25*33.'*'; )#6&*#F2$6 / *"(<q8.ah.#"#"&547326325#".'732654&#"&5475.'"#'675#5!#2#"'73>54&#632#25"zCOLZ.EG<643;5^=%32M*-<%+1I(B'< 6&$ 5$ &34=<v' M@$1 *;) *&>[SGP86(4+-54633.'*';)#&>&iq7zT.7>&#".546325.#"'675!5!##"'72>54&+#0?^&+>,RH@zw9F0/")( \22)7! *3&#Cc-/6`*.33.(L1#B%'7L JX/M.8@65654&##2#"'74635.#'>75!5!##4&#""&'25"^*/5Y1[%'&$-/W(PEpV3B#''P1@ u3 "8@2T=8;$!"15&33@ )&q8T.EL726325.#"'675!5!##"'72>54&##4&#.#"#"&546x4& (@zw9F0/")( Z72,'(#-74'#5!##"'73254##5.#.'474.#"725.'#7#3c6 'j1"#4"  3Q*  R+&3)?!? FDS3 n 33Y)1&/ &E|5F 1#2 V$878L=0)x.HUZ&#"&546325.#.'474&#"#4>74'#5!##"'73254.##25'.5#7#3Gg6)KE#3O-Q*  RJ'6'j1"#4"  ,(3BV? +">S3 ?7/G'6:9[5F 15? !33Y)1&/ &  Um@8U)0). )-d25.'#2654&#"2#632#"/'#3'.'#5!##5.#"327#"&547.'#"&5463.'#"&54 '/"/<3M >) OS#C  fR<kI3m+  "4!&&+'!A)'*?  i=9)!* 5 < 338'^ /)0 -)#!&.!;A675!5!##5.#"#"&'732>7&'2##"'732>54'6!S395%,#5*!/ NS> BG # V._a]9idR'33l0#)3@ ,R.A/<'  <Z' K4&#"632%4>32533##54&#"632'>54#"#"&5467654#"#"&Zx=UBSB,>*y>3RR3S>`D . %' O$U:)E!?;|eL 5+1!rY3gPlh"% $)+&9G   .A25!!67&547'5&#">32'>54&"#"54>yPG.}e,7d-Iq09"*+ #& ! J8-.'3!wCq8Nmx72r<6& $'! $   -D+18{GQ4632533#632&5474&#"#5&#'675.#">32'654&"&25\GaJ1@23Cw2K"0!?)+ (  '$fJ 9,g-* 6PnM3UQUbCdE) Am@H< -03-1 (!  #"U' ) :%8{MW].#'67&#'675.#">32'654&"&54632533#632&5474&#"#5'=2SU*!9M7X2K"0!?)+ (  '$fJ 9\GaJ1@23  Jx2,f-0:.5@H< -03-1 (!  #"U' ) =6PnM3OWqFCdE)    -+9+{L2533##54#.#"#"&546326325&#"32632'7674#"#"&5469M.3CC2#- .%$ -07#)"!E( 6)%1^+{=2533##5&#""&54632&#"32632'7674#"#"&5469M.3CC3=_%"/>,.H$V^!F O",E**8+ N  W15HM3< *2(#C2107#)"!E( 6)%1^+o{:@4&#"3"&547&#"32632'7674#"#"&54632533##'32#R!  *&V^!F O",E**8+ N  WC9M.3C3l72l[? (&"=7#)"!E( 6)%1^5HM3t7l.927#'47#5!##5&#".546325./7##"&& !&')3fO=3=_%&+>,.H$JN&H $ #a0 133qP *2'#C21$AZ (R8.>DR#"'#'654&##5. #'76?4#""&5467&'#5!#572>747474+' ;Wi$:/3  ~#k%3'4 *nHU cK:d c 2'i ~x50%"*2  3FU&$!334F&Y.[am.#"#"&54732?6325. #'76?4#"".546?&'#5!##"'#'654&##572565474+];2QH* ?A  )5  ~$k&3" * -fU(:Xi$173hK;d c 22(W6%0 ,<)+!  3F U&  33%i ~x60$0{5H%.js47#5!25!##"'73254&+##5.#&/4>?54&#""&=47#"'3267#".573254'#"&72654#725'.5#7#36='3 3#  ,4 3Q*  H7$ %7#_5_>%34b6U)2)5'(- 8`B+">R3ZMT33Y 1&/ &,|5F     -;  #2940_yHjv1 ;iI,=9% ^B8U)0).KT.#"#"&'#".573276754'#"&547#5!25!##"'73254&+##2654#"&=47#"'326732?6325.#&/4>?54&#"725'.5#7#3A 6?W 5_>%35b5*2)45F'3 3#  ,4 3~())$y$ ":$_,C?38Q*  H748`B+">R3"'81Hjv1 54'"'#"'3267#".573254'#"&72654#63f *(<44Ld3TT387p4(0 %7#_5_>%34b6U)2)5'(- ZMT3A4?JJ>`V3R-1C*5)!#2940_yHjv1 ;iI*=9%1_g7>54&#"327#"&5467&'#5!##"'#67.4&54>7#"&'732654')58"%(2 k#@64 ( =MHk-4    $O3="-}$B">' 3!+22 %N!@%2kyHY+    R=.'; -+ .:CP47#5!25!##5./7##"'#"'3267#".573254'#"&72654#27#6B<4MNG%7#_5_>%34b6U)2)5'(- P!M82ZMT33A>] (R@#2940_yHjv1 ;iI,=9%/ $%4a.3<Il.#"#"&'#".57;67654'#"&547#5!25!##2654#27##"'3267326325./7##"D)EW5_>%34b6  .)2)56=<4'(- P!M82*%7#_HD? 2 46MNG2'?6Hjv1 ;i& !,=9+MT33)%/ $%4a#2940_U, +0>] (R."O2654#7567.'#"'326'47#5!##54&#"#"'#".573254'#"&('(+"?4<+/"O $. >":#684`#$%T3%#O5_>%34b6U)2)5%963M$3 !#0 9MT33 .!O,B%Hjv1 ;iI,=9nP.1:V47#5!##54&#""&54>3&'#".573254'#"&72654#7#"'3267y6hC3]+ '+(+5_>%34b6U)2)5'(+",*":#_ ZMT33s;Z(* / Hjv1 ;iI,=9%9 * #2940_7(  .!*07265&'&5#5!!2+&'52654/&/#2qoJAb!;'9(G"`&$,\4 i7k}=]$3&0m33=,'+J 3C8 $ 3H +.6AIO.+532654./#"'#&5#5!!;26722654'#"'3265&'5#_AmS."C`z;GVx _1u?AbD=#) NC1 1rrFe2]&S)$7='4[2@]Eg&0m33@ZK&z$~<]< /H +.GRZ`.#'67&+532654./#"'#&5#5!!;2672&'#2654'#"'3265&'5#G  JP, AYlhGVx _1u?AbD=#) N_AmS."!/4C1 1rrFe   > *0C0<4[2@]Eg&0m33@D2]&S)$$$K&z$~<]< /H7+.Q\dj.#"#".547326325&+532654./#"'#&5#5!!;2672&'#2654'#"'3265&'5#xC L55 .32>32#"&'#4&'#&'%4&#"32654&#"5326if0C GQN5+'W@5o "#!)=b*7%B3"- ,0g*^3=c}o H$5a/W2% $-b+ @33@?%  S x$ )!`QE]A' 4&7-gBO-yQ xE67"+3%.,5.#"&54632+&'#5!!#2>54.': +="6(K=+4K/+n+c=2-* 4#bDLg*9#7 /G =7<09N33=&(Yi $.#u%2654'"#".57"'327467#5!!327327.547#".'327#"'#".'&'73254'#"&( #~4 $7/%:)>! @l7J 22 7$:@C1cF 8!;& 3/L$&B!  1#4`M8>.K 33GQ =Gf%=#`O (!+(Z2Bm#-!@z)2K..M"67$.)%'2654'"#".57"'327467#5!!327327#5.#'67&547#".'327#"'#".'&'73254'#"&,J( #~4 $7/%:)>! @l7. 3Q,!K\2 7$:@C1cF 8!;& 3/L$&B# !  1#4`M8>.K 33GQ =,J73 *0K.`O (!+(Z2Bm#-!@z)2K..M"6G{.OU%47"';2?#5&##5654/67.5332654'#".5467#5!!3276 @.29 , +201#<9S(1$;# ,#2S;?C.9Up+' 0, S) r+".9L7  e38+CMg9%*) ,,*7*33!3# )T.1FO2#"&'#".'&'732654'#"5467!5!!63254#"';2654&#"&'32696)'P S,BQ0L-%)(7 =/e5}2M<^06@%1'B/D61K65k@$2N7> h @#/~&U33)P`CY5:232537632%32654'#"'732654&#"iF$cF.=",M("PG !"3)32#"'24&#"3254'#"'7;26&'32qz/34+44(#ve A1,?66c- @!<^H&4.1.&0"((.l2&?M5E/Q*  )|- *O!@r''' E4 *A!= ,B)2+5;HB=!7)-"?9LK6m( (6C)$ !0^v327#"./32>4'#"5?>5'.'#"&54632>32&'&=467+"'3254'#"'7332654#"> +!- & :]/2 h; !# ,I!  $8&P;+*8#>"#! H$%" X,#<-* .*)[yV/) N*0Pj9]s *B_>7  9 '.1X'(+%%. /^6?(W)++C.  - #8!1- $o%7'72654'#"&5432654&#"2#"'#74'#5&/67&#"#"&5473724&+"&54632>L7U.h*}1 )9d_80:)/4%!*449?[0 ! DT$,A?iP .m &;.))&E! K*+BM #< $ t\A-7/>+5 H/F\9,/oS&C8M; Bw. '6:>f$ 'G%#"'732654'#".=#5!>74#"&5473723!32654#"'632P#?*\.Lq!?6.)y[5G!* a3D/#;T *"))/&-( (*! 3 5 <1) $ / E3 37q9-$!B2H;'d.#"#"&54732?6325#"'732654'#".=#5!>74#"&5473723!32654#"'632#g>2RF* 7I $ .3(:\/KrD6.(\5G"* `4B0#;T (!")$3(V6)+ '30*#%* *" 3 5 <1+ $ / G1 37q9-$!B2L*.o 'I4&#"3".54632!32654#"'632#"&5#5!654'"&5473723##Q"    5'73;d&!"-:c4,'d[[3G!*a6AB3[@ (!/76D$"1'6vS?'O3 2 ?/) . F63t5n{FQ%"&=!5!632533##&#"2632'7674#"#"'#"&'332654'7&547#326!#C9M.4BB4T`"DL #,F)+8+ T 0E:qu30M0?32>54''#"=#5!#3267#"&'#".'732>54m !730)%&7 #R^1~ S-6aEH_1W/2MI</5 0 $ȇ975e;"2)%:(7#6333T]4BzH^0IJj !0)"o.=4&#"3"&54632!32?67#"&'332654'#"&=#5!##6Q"  *5&824 K(R6m3b[;RR%(C3[@ (&07-2l7?+[3'&?.33t ='L#".54>3253>74#"&5473723!32654&#"'632#"&=4&#"37 % :&#? [8E!*4/@b  "'KC/(h[B , 5#,6&.,c 5 ;1)  w 3I %"nO=(s_M h'm.#"#".54732?6325#"&=4&#"37#".54>3253>74#"&5473723!32654&#"'632#D6/E#.EF56;5/(h[B  % :&#? [8E!*4/@b  "'K(3c',.*+ *;)+>=(s_M- 5#,6&.,c 5 ;1)  w 3I %"n8F $U4A".546324.5473##"&54747654&#"3732654'y &E3lB99<8ͫwW6ca*#)Z[c2$ET"* 8%327 %N?i4#6K&/ %&)*.$ "/(" ='(5h49K #`v\ &# XKA>$ /E% 1 )FDZ,!.=46325!!32?67#".'332>54'#"&=&#"&7*.5L4 K .U6RL33 >GV80I$S%#40!(%7;n3-2l->2$Gd_&aM/ *<2#*@3;[h2f.#"#"&54732?6325#"32654'#"&54632654'.+"27+"&54>327#vC  L[.DG( 46%3i4#6K&/ %&)*.$ "/(" ='(5 %3c'LA(- .;)+ #`v\ &# XKA>$ /E% 1 )FDZ*417.IZ%"&=&#"&546325!!32?67#5.#"#".547.'332>54'#"'32?632(4/!(8)02L4 K3C K55 1A3 :FT?/I$SM4FmQ 325!!>54#"'632i 32ZB  &"2(2F` 36% ,./7#6cL0 5",@ H$3q 6X2&"!<Y?r;327+"&54>32533##5.#"37#"&54>3254&#"r, )F>)3& 3__3f[B L!1)1GY$-H>3^+1:('c3w!icM/=B+@ H$+w4?o{=%2#53##54&#"3"&5474&#"327+"&54>32533#;3C3Q" *$Y$-H, )F>)3& 3,,4633t6@ (&"3+w4<>3^+1:('c3J.04#"./732654''254&"&54>32%5! cM 71=71832%5!*"Mm 33R,!(v^7186u) &.X*Y?31 ZD.++  5 %09&h;$ #1U.;?.#"&546325#"&5/332654&#"&54>32#5!p +="6(KE#3M.83XYBLF57 + 8&Kc-(3ov*9#7 /I'69:V\IFa!#'" '}];c33E.J"&54632!5!##54&#"3".5463&'#"732654& +0& )1"@B \H2} #(/  ].  ,!.,  !1 7%f*336!U )+ #<*D%8o.>4&#"3"&54632!5!##&54>32#"&5/332654&#"6Q"  *5&82C3 + 8&KceT83XYBLF57[@ (&0733t'" '}]\|V\IFa!GU.JN!25#"&5/332654&#"&54>32#4&#.#"##"&5463265!["#83XYBLF57 + 8&KcA2,'(#-9+ &*/6-$F133{@2533##&#"2?632'7674#"&547'#5!#6746:M-3BB3T`!F  # "-F)*8' !) /d-n\\xZ17FM3`7#  )" F( 6' NkWG LcdY233{4[.:27#'47#>7&&547'#5!##5./7##"'&  3f2t2d-n <3KN $ a0 1DEPLcdY233A>] (RMrWG-..?6?'&547'&547'#5!!>7)'!#2")3!#)2>32TZ,k(N!* oZ8`Pc,H#"E (37>7#5&'R,!EZ  2F?2TZ,k(   (")3 % J+0G//1,H   + (37>7'&#"#&'7 8!#2  2hn,k( ')-X")3#F"W!- *Q 02,H !6- (332632'."33 FG! 4F0If.A;s3@32632&=474754'#567  JQ,!!2! 4F0If.A;."33s%   >!)0+wD}*33'I6* '0 <54'#"&5463367&547'5#5!!>?6765qVN43O- (0" 0!&"%   3%#)J &"y?H8G`b?M2 4"75& 0#)#33"!.M%25#"&54632"2?&547'5#5!!>?67#5&#""5473265!'$2&'4##< 3%#)J &"y*38 2:;K -1&%# 8)#33"! 5;,<*7/.j.#"#".54732?6325#"&'332>54'#"&543367&547'5#5!!>?>?>767#B K55 .3!"&547.=47##5!##5&#"27d%  2- *y%"V$3 ./MQ3U0  80  #z0 $sH5= MK0G33Ap  ' -Y507"753##5&#""&546325./7&54632&#yR3=`#.>-TFGkMp^),*' yU-J1& DJ3^P% )3'#CcLGK)[F=8Y4&+#3"&475./7&54632&#"75!###632R!  *GkMp^),*' #yC3yU-J1&v 72[? (&88GK)[F=8 DJ3t:L654'!53&/&5473##"&54>?654&#"7#"&5432654&'*!F 47! ΰT$4>./U9(-a <- .:8&.MC%!%30%16D<)%4*P23sv5S. G4- *%.$/^.$1kFsM20 /7T3;OA8   '+3k33H^HtBq}I 5Pb?,\F!a".: 0%"(Ee.[.#"#".547326325#".'732654'#".'&";27#"&546325!5!!327#C  /E#.EF :2965FsM20 /7T3;OA8   '+!:~QL3',.%0 &;) *+Bq}I 5Pb?,\F!a".: 0%"(3k33H^-,^.,%"&=&#"&46325!5!!>54#"'62Q,'<6!(9(5< 35%*>.@&vR3L33q 6Y1&"!=Y/.I4'!5!##".'732654&#"&5467.#"&546323>$u>t,+$N71[@43d/&$5I4$)/% )* )-0=!& >33-&3_"142JYG c}@1(7% "&!5  !253..W%.#'67.'732654&#"&5467.#"&54>323>54'!5!##!  JP*!"*6a?(4\=&$7G4$)/% () ))&)H2 $z>y*>3!  >"(0# MdZ! O#A0(7% "&"4  !2% ,,>33-&2 >5S,.L"&5467.#"&54>323>54'!5!##5&#"#"&5473>3254`C$=(+ ))&)H2 $k>j32: O 9J,8'#<(AH %"$P !2% ,,>33-&0'3*V02: >(11I).i#5.#"#"&54732?6325#".'732654&#"&5467.#"&54>323>54'!5!#"* >3C   LZ.EG  55)33-&2a).F%#&/532654#"'63. #"&54>323>54'!5!# %@(}"w66 `SaG1    ))&)H2 $k>j( +#A#m3<)H]     !2% ,,>33-&= 9y.$&547'5&#"&46325!5!!67d.<6!(9(5<e,.Lcb[23vR3L33!PUYy.5;"#5&#"&46325!5!!632#7. /6754&5'g3<6!(9(5<MN12 -cRTM>}*vR3L33o   +R#E :&.A3".547;26;2&547'5&#"&46325!5!!67&'#"# &/!24 i , d.<6!(9(5<e,./'5P@*81!3:b[23vR3L33!PUY .06#5&/67.#""&546325!5!#632&5455(3=e&=q9"4D$7?".W46#2D&2N;b!35:%&4)0")55*z33f;8lWq>;&!K.6<E.#'67&/67.#""&546325!5!#632&547#5'O+!?J3G&=q9"4D$7?".W46#2 5(3~D&22' J#'0?*635:%&4)0")55*z33f;8lWq>7&!    Y.HN&#"#"&547326325&/67.#""&546325!5!#632&547#=Y22#;]* ?A * '6=e&=q9"3C$"1?".We9 2 5(32LD&1<DL$3 8=* +b!35:%&4)5)55*z33f;8lSu>7eޱ6&p.1.#"&546325.#"&54>325!5!##W +>"5(LD$.J7"5- "!4#4,d1P3G*8#6/J'60C.9'& , '$E/F"e33uov.3:4&+#3"&475.#"&54>325!5!###632R!  *"4'"!4#4,b3C3v 72[? (&8.:& , '&D/F"f33t97.7D%4>3&'".#"&546323>54'!5!##54/&#""&#6'$;E )-07+' $C3)" '*ڏ.' ,, !25#>33o)((U0#,;  ..7"#"'727&#"+"&5463267.'5#5!##.'6325J $": <+,6"K (&0&T1IX`$J01O#,ESEU3:x "&'o)*&"33)<{0!B3'GR#"=74754&''65&#"'>3253>74#"&5473723!>54#"'6267&#"29d4T3 $% .# o5F[5E!*39 9f+<-/@)9 D $E9Z Y"&!? $1P 5 <0)  w 3 J&"!<4+E/" ,K~=I#"&/732654&#"&5475&''654&#"'>32533#2767&#"!0yAJ5$3 E:=)5I1,3e #K;\P0(+8"?=>;J^{XVF* A0!C &%NR  ( $!J_P3 & /y 5?727&#"63%&'4&#"'67654&#"'632533##5&''27&#"6)'kA+E!&/03W!) L&)PwZW5OQ1-&m9)4;Bt2H% *)E '4 ~K3.'`9G os*5:4&#"3"&5474&''654&#"'>32533##267&#"2#R!  *(2$%.# n6F3B42 # S34 ?R82j[? (&# W"&!$ $1PE3tI ,+R)!e7  s0>>32533##5&#".5463254/.''654&#"72767&#&#" n6F3VV3=_%%,>,OK $%.% # 3#2$ ?1PE3O *0($Cd. "&!$ 9 ,+2&(!  sFT7463263254/.''654&#"'>32533##4'5&#.#"""""&2767&#&#"YC/5% ( $%.# n6F3VV2,'(#-< # 3#2$ ?!$F1. "&!$ $1PE3 = &*/6 ,+2&(!.1;D>3267.'5#5!##.#"#"&'7267&/'654&#"72767&"76325Z.jZLUJ05 (2%$( ,%#' ")V[ X(E{)+&"33 EIe '$*& 5!+`E!#W23!52732#"'73254&#32654#"'632#"&=./67&'#5!654'"5473;{"="w/. ,2%=0')*G *n71l*<&I8Anf-% =,3(* #(69vK Zr8 #1? 8- -407X>0A_ %M#6@33*8&1,'aNaP'% .I'675!5!!327&5467+".527#"&'&'332654'#"/.', 26S _%+19/"#  -@%s$ 20:+.-7'%& .J-* 33., /hx+e s(4  31SZPEl."2("2.'5675#5!!67&54?67'&'Q\[p@f,. 4W59'}h<%2;33!4aQNI Qi "Z7I9&Z0.8>74732632&54?67'&/675#5!!67&#"#"&5S2)0x!0  4W59'}!@f,.)9q%>J\[ ,A,9B "Z7I9&Z082;33!4aQNI -[<%=.+2%&546?#5&/67./675!5!#636750_I-);"Y1Ky0%*"(T` $-E^w72+[uo);T&? *e#+33+W#T`l L d.+.+'675!5!##5&#".5462*H''%9!~A3=_%&+=ZH%$X,X.6b++33iP*1($B21.7=%#"&'332>54'#"&547.+"'675!5!##36?6'55oXN44O- (0" 0!&"9p(\g%,  S]D46326325.+'675!5!##4&#.#"""##"J(3' *9!~A2- 1%$.H''%0,.6b++33<)  +60$X,hA.SW%6325#".'32654'#"&54632&#"3267#4&#.#"""&546325!' (9bC:"9%T,P"5R3O)4! K%3!%(#2,'(".;B05XE1Jm_:Q?3Z7?#00):2G #!9 &R:* &*/7,$F33. P4'#32'4'#5!##".'732654&#"&5467.#";27##"&5467# 6: &f %#hQ1[A43'=[06H-#1)0! +%!4,("&T!-33543VD\1JXI V^A@1!>!&. #3+'8O[33##5./67654&#"32727+"'#".#";27""#"&546754'#5!6324'#32\%jI4QQ4";,C&,n'B7) ? D#G7 *& e)7?Q# '*:8P)YX3I% 58<"*$3J))3+'!-3JI4,#).1=&547'5#".#";27""#"&546754'#5!!674'#32nd.#G7 *& ee,.# '*:Lcb[2[))3+'!-33!Q~UY4,#).2>#5'.#"&546325"&#";7"&546754'#5!#4'#3224L"3 #QA"+c   && oM4#(&:B E1?7 /G%:B|X,#'4335*5* #, .-47254&#"#"&547.'5#5!##5&#""&5465.H$`#B2U)D>NB93=_%#.E-=79.!21C+P,],&!"33P *3(+;82Nbs Z2767&#&#"4'#32'4'#5!>32533##4/.''654.#"#".#";27""#"&54670)3#2$ >`# '*: e N$F3VV3 $%!/,#G7 *& (82&(84,#)T!-33 'E3:. "%" "9))3+'bs^kw.#"#"&54732?6324/.''654.#"#".#";27""#"&546754'#5!>32533##2767&#&#"4'#32C  LZ.EF 46 $%!/,#G7 *& e N$F3VV30)3#2$ >`# '*:2(M@%0 .<)+). "%" "9))3+'!-33 'E3(82&(84,#)`shv%263254/.''654.#"#".#";27""#"&546754'#5!>32533##4'5.#.#"""""&5462767&#&#"4'#32 2) ($%!/,#G7 *& e L#F3UU2- .$#.;DM % 3#1% !?\# '*:1. "' "9))3+'!-33&E3 % -3/7,%E--+2&(!54,#). J4'#32%467&'#5!!32732#"'73254&##5./7.#";"&5/ ;*d Z< ^r#%/"(M83'5!ll *" #33 O6GM^O7-&-=ZC;V -VK ,.Zg.#"#"&54732?6325./7.#";"&5467&'#5!!32732#"'73254&##4'#232F6LZ-EG  38'5!ln **d Z< ^r#%/"(M83V *';2(M@%0 .<)+2;V -VK , #33 O6GM^O7-&-=A'.NZ#"&'332>54'#"&547.#";27""#"&546754'#5!##"'36?6'4'#325pWN43O-'9 0!4*0" *& ea1$ # '*:NF9FbbAL2,$ 7$'/ #3+'!-3309-/ " \4,#).T`%25#"&54632"32?#".#";27""#"&546754'#5!##5&#""5473264'#32!'#7")2# F#G7 *& ea/3: 2 0DR# '*:Y -2%&# A))3+'!-330985;442B4,#)Z.kw&#"#"&547326325#"&'332>54'#"&547.#";27""#"&54674'#5!##"'3>7#4'#32\7<QLZ.FG 929/=N43O-'9    4*0" *& ea1$ !.5(3# '*:q: M@+* +9, +nbAL2,$   $'/ #3+'(-3309-/ 5F9;-A4,#).3?J%463&'#".#";27""#"&54674'#5!##54&#""&74'#327#A5&,#G7 *& eC3Y/ '*# '*:2D(&#))3+'(-33o] ()4,#)09 .>8op.4;%#53##54&#"3"&546754&#"#"&547.'5#5!#!530*C3Q"  *`#B2U)D>NB =79.5533t6@ (&/{C+P,],&!"3382N.JV%25#".#";27""#"&546754'#5!##54&#".#"""&54632674'#32 #G7 *& ea 3 *-#+& .9A).3l# '*:))3+'!-3309*$(  @-7,$G7 4,#)\.;B%254&#"#"&547.'5#5!##4&#.#"##"&5463265;`#B2U)D>NB92- .#%.;K'2( =79.C+P,],&!"337- ,4",07-,=082N(.>JS4'#5!##5.#"#"'73267.'#".#";27""#"&546?#325476325n e?53 :.10A,. 0;1$#G7 *& ˵ '*:D@5"'2!-33N+(Tj# F?9!-/))3+'U#)B4,74Eh(.Yen.#"#"&54732?632.#"#"'73267.'#".#";27""#"&546754'#5!###325476325A 6K\-EG 38 :.10A,. 0;1$#G7 *& e?53y '*:D@5"'2c'LA(- ,;)+N+(Tj# F?9!-/))3+'!-33m#)B4,74E[.;C3&54632635!5!#'654.'#5.+'67.#"&#"I!SK'; !u7J 4 :1-'X= !C" +@%"h8-i,L,=!  a33 (3"6!)& ) )K:0O;"_` ;.L46326325!5!##54&#"632'67&#""&5463254&#&#";"&=29- !"s3ZB!AH"+ '%A & 'bEAH"1@#!  %,a1G5&L33œ+\;+$'  %$1&!=QL  ^1,17,W3723!32654#"'632#"&=4/.#"&#"&546326325!5!>54#"&5473C =f*2N$, ,<!8QI(8#%"y{[5E!* z 3 H&"!8#Y:, . ^ ++N,=1&3h3 8 <0 (  U,u.#"#".54732?6325#"&=4/.#"&#"&546326325!5!>54#"&5473723!32654#"'632#@!L'F:.EG 8356$, ,<!8QI(8#%"y{[5E!*3C =f*233v& D4%0 ,<)*2:, . ^ ++N,=1&3h3 8 <0 (   z 3 H&"!8#LT6.B&54>326325!5!#>7#"&'332>54'#"5&#".#">$$3 4"(/& +"OzCRs9>-<)&:M8&)+#$G( " C #*/)$Y33 :<[rSk$&-#3,$*@i>?$7Y.b.#"#"&54732?6325#"&'332>54'#"5&#".#"&54>326325!5!#>7#@!6K\.9S29+,Rs9>-<)&:M8&)+#$$$3 4"(/& +"O33r&LA%0 *32+Sk$&-#3,$*@i>?$7( " C #*/)%Z33 :<[PF8.P32654&#"&5463254&#".#"3254'7#"&546326325!5!##".'NM8F40)H5 /+3''=. 1-=C0; .d?gQ326325!5!!67ge.&)+#$$$3 4"(/& e,.R]b[2|>?$7( " C #*/)%Z33!P`N.HM25!5!#632&=656=4'#5./75.#"'.#"&54>326: : ."332>7&#"3d/+3''=. 1-=C0; .d?3# Z#' # 3'C@"(D!/&9-%C 1 F33a ' $)E]  ,.Q&'.#"""""&54632632&/#5!!3273#"'73254&##5./67d--*"(:-(69vK ;P$ 5 K:/,: %kB-/6-,=0#%33*8&1,'aN %51 &$ 1 #2>ZFFX %.n.#"#"&54732?6325./6?&'.#"""""&54632632&/#5!!3273#"'73254&##E  LZ.EF46,: %@--*"(:-(69xM ;P$ 5 L:32(M@%0 ,<)+5FX %3B-/6-,=0#%33*8&1,'aN %52&$ 1 #2>o.I%2#53##54&#"3".54754&#"4&#;27#"&46326325!5!#84B3Q#    '@'." 0&;GG;1.+&S )5733t6? (!Y&&3))90QR0+ 33.I%25&#".#"&54>326325!5!##5&5&#".#"##"&546326*&)+#$$$3 4"(/& O3 */",%/8C(.3>?$7( " C #*/)%Z33W) B.9+$F7!.?H%"'.'27#".'#"'72654&#'326325!!'2654/&'\ %# % 8- '/ #!7l/"C  Y{+51-S%G34 0 +#,1 &30/IK[3=#$ 0]3* G.[f4.'#"=&'327#"&'#"'7254&+'632>325!!32632.+732>'2654+"'` _0N)&%  - ):(  W&2     &N.?b>g6Q"9mCP6D3*AYwKG% +11 &' ,A* N3< M81^(/K$Aw2<J(!-]32654&#"&5463254&#"327#"'#"#".'7&'7;>54&#"56;263533#Q&54&#"56;263533##5&#".546324&#"327#" 4#I5N3QQ3=_%&+>,.H$P%!, <X')-1=8k_  $W3P *3&$B21s/6 +oS%4&#"327#"'#"'7;>54&#"56;263533#2#53##54&#"3".546P%!, <" 4#I5N3&&54B3Q#    )5r/6 +:')-1=8k_  $W3:733t6? (!"\726324&#"327#"'#"'7;>54&#"56;263533##4'5&#.#"""&5461) (P%!, <" 4#I5N3QQ2- .$# .;C1s/6 +:')-1=8k_  $W3 > ,4/7,$F.8@327'654.'"47#5!#'2654&+"#5.+'767&x1b1a4JPc])/$  2'[A!)7\$ E%Y( MK$33+## P:Q(#;GN70$0#)#g.JT]"&547#".547#5!!>32'654#"#5&#"#".473263254'326?'654.'"1G$ $4 '' !0#"1230< S'-()T 0, ! a1WI # ,K$33+9) 8-##B2* 218#9+01S1':K -A*3F31B 6 5 +o< .'Fm'<@67'&/&'#5!654'"&5473723!32654#"'632#"&'.'7#')=[3G"*`4B0;c %!".:c4.r=Ũ4tG5/  3 1 ?/). D836C$"1'6vS %6Vgh'$U]f.#"#"&547326325#"&547"&547#5!4#"#"&547326323#32654'7#4&#"6'327CPLZ.DG<46JN*3^B<:NWJ0 zSE5g~M[M8,u#=3!N! !0"c' M@*+ ,;) ++3&:K37@+3F31<  4=:35'N6H"#9+/M +o< .'jm'[_.#"#"&547326325#"&'./67'&/&'#5!654'"&5473723!32654#"'632##B   L[.EF 046*+.r=!)=[3G"*`4B0;c %!".(!32va'LA(- 1 <) + %6V.5/  3 1 ?/). D836C$"1'+b'iT1;G654'#4>7'&5467'#5!&5473##"&7&/62654'  D%,;\P6&+   )>0cGEW;9)<&7F<&:3|1 )&N?3]G#x@TV6\8!q.$:AE%"&'#5./67&'#5!!>32'4&#"'67654&#"32%&'5(.g3AcG*g-~"# ,<+.1'-P$ D20f".H;0{UT]O$@\H:%R#A33%  >-5%13gh3. ( FDKG(>+7qc#ws IN2767&#&#"327'654.'"47#5!632533##4/.''654&#"#".%67#c # 3#2$ ?1b1a4F3VV3 $%-V) $" ,+2&(!B%Y( MK$3 E3:. "&!$ (5 ,y #sGT\ej.#"#"&54732?6324/.''654.#"'#".547#5!632533##2767&#&#"327'654.'"767#F7LZ.EG  38$%-X* $4F3UU30)3#1% ?1b1 '2(M@!4 .<)+). "'  )7 ,K$3E3(82&(E%Y(  .D27'74&#"26"&547#5!!632#"'732654##5&/67'x*\' )> :Ahq)diG4Ke$!  3>(a8^Nl>.=&7533#&(4#>">>E:%0 ;PnAW6=&.KS^.#"#"&547326325&/67'#"&547#5!!632#"'732654&#"#27'74&#"26wCNLZ.EG 8 46=(a8?:Ahq)dgH6Ke$!   2( M@!4 .<) +*V6=&=&7533#&(4#> @>E:%0 ;#/Nl>.:.4A327'654.'"47#5!##54&#""&5463&'#".7#x1b1a4lC3]+ '*5&3: $/%Y( MK$33oZ()!((- ,-<  3+#oR.)04%#53##54&#"3"&54675./67&'#5!#&'5y12C3Q"  *BdE*g-~"!3I;0|UL4433t6@ (&# ]G:%R#A33(?+7qc{}.I3254'"'3'2654&+"#5.+'767&'#"&'6?32654'4'#5!hVG?8&h8$=9)IY]&7  2'[A!)CO?"<8  $?E",l$H8$"5 N7P(&4GM80$:Q3  @*33.T^%&5467&'#"&'6732654'#5!#>32'654&#"#5&#"#".5473263254#"'3254'"'$1#."<8  ))!0#"231;V%7 (+2R 1,%C8$= # !H?3!3.33"5/ 8-#B2* #3*)A"8; /I$H8$v}.RXb.#"#".547326325.+'767&'#"&'6?32654'4'#5!#'2654&+"#3254'"'h:B/.) >>2,2'ZA!)CO?"<8  $)IY]&7  3VG?8&h8$=T' ;(*+ *;) +EN70$:Q3  @*33"5 N7P(&4IE",l$H8$S L254'#233##5./2654'#"&5473274'"&'#"'7327#5!2'!]  z*?>X3ll1:@g,Hd=A-*-  K10 <"1% $ ?2)>'   ,$C=W3&/?')I0&J 3/6::!)n3EdI'KS%2654#"'632#"&'5.#"#"'73267.'5#5!467474#"&5473723!25!6?a*/6a6 +4-!21@,%. B_2[8E!*a3DC '1`$RK&"!<9Z!-?4?4jOCK7%3 4 <1)  / C73n=EoIC$Y4&+3223#32654'7#"&54>7.'#"&'6732654'#5!4#"#"&547326%V9#=CE5g~3HH38+v#7.'#"&'6732654'#5!4#"#"&547326323#32654'7#4&+32EM/E#-EG 8 38@<*3 )/';"<8  #:NVK0 zSE5g~3HH38+v#=33 $V9#>c' ,.(- ,;) +3&!#=*3!A) 3F31B  4=:3#B419#9+/&%M+$H9*.>G#".'732654&#"&5467&'#"&'67326=.5#5!!3254&#&#"dT7&'#"&'6732654'4'#5!##3254&# JO+!%6b?(3-5N*5I41* +"2"<8  #7W&$"43f9#=B  >!)0NdZ ;PA.@1(7!& #,F3!A)33"#2 UK+$H8 ).JT"&5467&'#"&'6?32654'4'#5!##5&#"#".5473>3254'3254'"'`C$;'6"<8  $7W$30< O $7 ,5*#<(8$=EH%"#OH3  @*33"1 53*!/) 2:;&31$H8$h(.bk.#"#".54732?6325#".'732654&#"&5467&'#"&'67326=.5#5!##3254&#uD6/E#-EG (38)1[?43\>&%5I3$)2(5"<8  $7W% #">3[9#=c',.(- ,;)+b 2HZG S"@1(7%! E3 @* 33"!4 UO.$H8 *q&.7A%#&/532>54#"'67&'#"&'6732654'#5!#'3254'"'#J1|"v54!$0>!`G1z/"<8 #7W.&?8$=+3#B#m3,"I] |?3!E# 33"%7#$H8$. ?2654'#%3267#5!##5.#"&546325.'&'+"/P ' 4A-5 , /.;6325.'5#5!##5&#""&546325.#"#"'7326s@5"'2 B_853=_%#.>,-H% :(20A,-84EK7"33O *1)#C21N4?4j" Es Y2767&#&#"'3254'"'3#67632533##4/.''654.#"'#".'#"&'6?32654'4'#5!0)3#2$ >8$=`' B!F3VV3 $%-44 "<8  $7(82&(($H8$)E3:. "'  )(3  @*3sZgqv.#"#"&54732?6324/.''654.#"'#".'#"&'6?32654'4'#5!632533##2767&#&#"'3254'"'3#6E  LZ.EF 46 $%-44 "<8  $7!F3VV30)3#2$ >8$=`' 2(M@%0 .<)+). "'  )(3  @*3E3(82&(($H8$scq{%63254&/&''654&#"'#".'#"&'6?32654'4'#5!632533##4'5.#.#"""&546322767&#&#"'3254'"'3#6 ( %&14 "<8  $7H3UU2-1$# -3&'#".'#"&'6?32654'4'#".  8$=YC4\+ ' )(% / 4 "<8  $".0< 3$H8$33oZ (+" /!(3  @*o.C6325#53##54&#"3".54675.#"#"'73267.'5#5!#s@5"'2313/B3Q#     :(20A,- B_8!84E:433t6? (!, N4?4j" EK7"33.LV726325.'#"&'6?32654'4'#5!##54&#".#"""##"&5463254'"'10!2 "<8  $7W33** $.9B8$=6 '3  @*33"<(  %*(-7,$F$H8$.N6325.'5#5!##4&#.#"""##"&546326325.#"#"'7326s@5"'2 B_852- 1#!-.#""&546325&/32>54&#"&54632&'#5!K '7!&"/>,-H%z_:&: M$B0d/6lY&&3|"8 *2)#C21Tc09!(# $<#$*9+KZ9o2[p!*33I..22'654&#"&/32654&#"&546326%5!0:.;&A|Z&[,CXK&1++ Q?0a 5'v;1$:*.#a!Yr!G9!@3$=4 %4EF*-V33./=B4'#5!#2?&547#5.#&'>354&#"/46725'.5#7#3f%X9 0D3O+$1J5-+B?YG+DR1!#33]*/On=|6E!+;( h9 ;M52*=.O;27#"&'&/32>54&#"&546326326325!5!##4&#"4&#|/'-@ %CY&_:&: M$B0d/'V":0/+&+VN3@'." %*801+"[p!H9!(# $<#$*9+K4''0+ 33Y&&3)J.FJ%25&/32>54&#"&54632&'#4&#.#"##"&5463265!"z_:&: M$B0d/6lY&&2-+"#! .;J(2( Oc09!(# $<#$*9+KZ9o2[p!*7-  B"07,+?033W~P[g2533##&#"32?632'#".'32654&'#"&=#5!#26754>4632#"654#"+1;K-2AB3S^*= # ,&D(#p[=b7'/Z4(-CP4 %  Y&7, "b,!/0HP3 `:  7(>'WjIe[&$[H4 7!5+33" b (< 2i^ 7hT.,39.#'67.+"'675!5!##"'72>54&+#/P+!AU(w2zw9F0/")( \23)L Kf!*0D,4U6`*.33.(L1#B%'7L2JX/W)  +{1.#'675&#"32632'7674#"#"&54632533##yQ,!LeV^!F O",E**8+ N  WC9M.3CC3 *0L2o7#)"!E( 6)%1^5HM3N.,5.#'67./7##"&547#5!##27#OQ,!ES"q=N&HO=3& !&')3  J *0E-0B (R8,33SE $ #a0    U.#,%.#'7&'#5!!#'2>54.'$  JP,! c=2-* 33#bDLgC   >!)033=&(4GYi .EP&'#5.#'67&+532654./#"&=#5!!;26722654'#"'\# J3%80?R/."3O+!B]}GVx _104*= "12 NC1      <(3/)$3#'0F/U4[2@].33 @ZK&7a.M.#'67&547#".'32?#"&'&5332654'#".547#5!!32?#Q,!DY #0C( $*%*`4H3m9"@(1'2KzK9# 3!)0F../]X ! B!-"$,!@Rx+F! c6'$&1/a1337'!>_;HTZ'@7523!32654#"'632#5./7.5#5!654'"&5473G$B>.I/;d&!"-33  !&d[[3G!*a!#/%l32533##dQ,!MdY$-H, )F>)3& 3__3 *0L2c+w4<>3^+1:('c3.U.48%.#'67&'&/332654&#"&54>32#5!`JQ,!-313XYBLF57 + 8&Kc503v  >!)0." V\IFa!#'" '}]Ai33[3833##5.#'67./67654&#"32727+"&54624\%jI4QQ4P,  (V~Q2JL8P)YX3`3 *0@*=@ 8<"*$3- 2DJe&.&,"##5!!632#7. /6754&5'g3HLP12  .cRUK>}* 33p   +R#E 9&-5 .5:5%"75!##5.'#5./7&54632&7675#0{&]#yg3C3GkMp^),*' yU-J1&3gw: O4  2 DJ3X/{ VGK)[F=8Y323>D'@s$k>j3  !$z 94 ))&)H2 #/">33-&5"*!L, !2% ,,.$,767.#"&54>325!5!##5.#7JfA&'"!4#4,b3P3Q,# KEN0?C , '&D/F"f333 *q    s%3.#'6754/.''654&#"'>32533##2767&#&#"P*!Md $%.# n6F3VV3 # 3#2$ ?"(0N0F. "&!$ $1PE3 ,+2&(!. ).5.+'675!5!##5.'#675s.{%]U9!~f4D3H''%3on: M6  1.6b++33X/z $X,JL+YA.=A%4632&#"3267#5.#'67.'32654'#"&'5!  J)4! K%3!%(#2M,!'*:_B*9%T,P"5R3OXG   #00:2G #!9 &R3!)0) LukDQ?3Z733.<7574'#32%#5!##5./67.#"327#"&54674G$Ex# '*:ea3  !'l$c +& #/&4,#)3309;"*#CG3*'(c .BGR.#".547326325./67.#";27"#"&54674'#5!##574'#32^;V%B6+@D7.4  !:Y$c  ,+ "a33kH#H{#  V:g' D5'/ /:, +1!*/7G3*#(-3309B.T"0)4, K 9h.',5.#'6754&#".547.'5#5!##'5x=79.S+!Jg`#B2U=4>NB93hO/82N +0L1C+P,L/&!"33m+}. J%55>754&#".547.'5#5!##5.#"#".54732?6325.#O/=79.0O2`#B2TB. ALG83B!6/E#-DH (47R,+}73N27 C+O,G1$$"335',.%0 .<)*+, .6.#'6754&#"4&#;27#"&46326325!5!##Q+!Md@'." 0&;GG;1.+&S.N35 *0O/QY&&3))90QR0+ 33;76754&#"327#"'#"'732654&#"5263533##5.#IhP%!, <" 4#I5N3QQ3Q,EM1s/6 +:')01=8i_  $W33 *&.*2757#5./67&'#"&'673265#5!!3254'D'@33  !(kK$"<8 " 78$=#/#"<"*$B]3!'O/33$H8".&/7675.#"#"'73267.'5#5!##5.#6325Ih :(20A,- B_853Q,@5"'2EM12N4?4j" EK7"333 *84E. 75%5!##5.#&/32>54&#"&54632675.{%]Hf4H"^Y&_:&: M$B0d/3gnq: M6 133X66 [p!H9!(# $<#$*9+KQ5L+'.26%.#'67&/32>54&#"&54632&'#5!KJR+!EZu_:&: M$B0d/6lY&&3  >!)0F/X,9!(# $<#$*9+KZ9o2[p!*33_#".'#5.#"#"&547326325./>54'"&547326733#3274#"3"54632K+( 4CQLZ.EG 7 4686q4)$1( ,(54'"&547326733#67O/_4CQLZ.EG 7 4686q4)$1( ,(?>54#"&546#0%s/O;5SKI/ &@b8M 6$,E-/()"9$ &[OYY6#80B:[o ;$YkGO *"%Q[%`;+1># 3 .F{`"&'#5.#"#"&54732?632&#"32632'7674#"#"&54632533#3274#"3"54632KT)3CYM.DG28V^!F O",E**8+ N  WC9M.3  6 *>0#$,96G##6'W6), *;)*<7#)"!E( 6)%1^5HM3G,X'$;(<{{W%&=47#5.#"#"&54732?632&#"32632'7674#"#"&54632533#67=/^3CYM.DG28V^!F O",E**8+ N  WC9M.3@7CB _! 6'W6), *;)*<7#)"!E( 6)%1^5HM3#`%+{j"&'#4'5.#.#"""&54632632&#"32632'7674#"#"&54632533#3274#"3"54632KT)2- .$# .;C/1) (V^!F O",E**8+ N  WC9M.3  6 *>0#$,96G##   ,4/7,$F17#)"!E( 6)%1^5HM3G,X'$;(<+{{a%&=47#4'5.#.#"""&54632632&#"32632'7674#"#"&54632533#67=/^2- .$# .;C/1) (V^!F O",E**8+ N  WC9M.3@7CB _!    ,4/7,$F17#)"!E( 6)%1^5HM3#`%.|.#"#"&=7332632'&547#".'32?#"&'&5332654'#".547#5!!32?3274#"3"54632#".'A t"?O2)/!y '$#0C( $*%*`4H3m9"@(1'2K(K9#  )7 *>0#$,A4   --4XA ,F4 2H']X ! B!-"$,!@Rx+F! c6'$&1/a1337'!#3X'$;(<-,Q ,M6A.q.#"#"&=7332632'&547#".'32?#"&'&5332654'#".547#5!!32?67&/&=47A t"?O2)/!y '$#0C( $*%*`4H3m9"@(1'2KLK9#  ,8/ ]--4XA ,F4 2H']X ! B!-"$,!@Rx+F! c6'$&1/a1337'! #`% 1T .KE/! K5A.IM#"/#4#"#"&547372&#"327#"&546323274#"3"546327!5!L*: .\ =K, V'&w !)7.  5* >0#$,#97F/r]I4<*3$#m'$#*8. ) 4X'$;(93.<@%&/&=47#4#"#"&547372&#"327#"&5463267!5!b/ ^.\ =K, V'&w !)7. @7?D>G .KE/! r]I4<*3$#m'$#*8. :L3h#"/#5.#"#"&54732?6325'./67654&#"32727+"&546233#3274#"3"54632K+9 4CLZ.EG (46A1]A,p'@7) ? (V~Q]$iJ4 )6 *  >0#%,94I/5(M@*+ .;)+BF11 8;"*$3- 2DJ=7Q(ZX3 3X'$;(<{[%&=47#5.#"#"&54732?6325'./67654&#"32727+"&546233#67=/^4CLZ.EG (46A1]A,p'@7) ? (V~Q]$iJ4@7CB _! g5(M@*+ .;)+BF11 8;"*$3- 2DJ=7Q(ZX3#`%.B%&#"3262&'#"&4632654#"#".5467#5!!32>F$ $%3@-   (0M9<("/24:3;-,9 !+0&0\LD4WE BA9908?JV!?-:#&K22O&7(/(.T#"./4&5&'#"+".547;26;2&547'#5!!673274#"3"54632A4)  .(5 P '0!1-$ i#* d.e,  6 *>0#%+9,Q1C^P @*/:!3:jS2"33!F+X'$;(<.E%&=47&'#"+".547;26;2&547'#5!!6767S/[ .(5 P '0!1-$ i#* d.%e, +8CB b! 1C^P @*/:!3:gV2"33! #`%5W%"&547326325./7&54632&#"75!#3274#"3"54632"&'#5.#"yyUDUF`.EF 3 :FkMq_<+( $y$  6 *>0#$,KT)3D = $#& $k>j ,-)=52$.6&6 o& )6')H2 >33,'2 (!4/&QE'+H,' " Q !2!+,,.=74632'.#"&54>323>54'!5!#&#"327'#"&;,  :1 ))&)H2 $k>j(#%" 'o"*E(39- !2% ,,>33-&> S'(.+4'!5!#'7".#"&54>323>$k>jd3b:D  ))&)H2 >33-&1,G (J8g,, !2% ,,.G7467&#"&54>323>54'!5!##"73254'#5>54&#"3&80?' ))&)H2 $k>j!D"4ZIj0(Wp2 89,O*51]$? . !2% ,,>33-&($0426BGC) 1(%'4 Q.c#"./&'&'#"+".547;26;2&547'5&#"&46325!5!!673274#"3"5462$K*)  /'5P &/!24 i , d.<6!(9(5<fe, -7 *>0F-96G5AUY @*81!3:b[23vR3L33! 3X'$;(: .T%&=47'&'#"+".547;26;2&547'5&#"&46325!5!!6767/\ /*2 P(. 24l$) d.<6!(9(5<!%e, +8K: d! 4@UY @*81!3:b[23vR3L33!  sXf#"&'#5.#"#"&54732?6324/.''654&#"'>32533#3274#"3"54632%2767&#&#"A4*)3C  LZ.EG 46 $%.# n6F3 )7 * >0#$, # 3#2$ ?9,Q##5(M@!4 -<)+). "&!$ $1PE33X'$;);Q ,+2&(! }sM[%&=47#5.#"#"&54732?6324/.''654&#"'>32533#672767&#&#"?/W3C  LZ.EG 46 $%.# n6F3C- # 3#2$ ?CB _! o5(M@!4 -<)+). "&!$ $1PE3#`%# ,+2&(! scq#"&'#4'5&#.#"""""&5463263254/.''654&#"'>32533#3274#"3"54632%2767&#&#"A4*)2,'(#-0#$, # 3#2$ ?9,Q## = &*/6-$F1. "&!$ $1PE33X'$;);Q ,+2&(! sXf%&=47#4'5&#.#"""""&5463263254/.''654&#"'>32533#672767&#&#"D/\2,'(#-0#$,A4))4#:/ #*.) H''2" H,6`-+333X'$;(;.,Q"!5 ;'+* (&3{$`!k.?H726325.+1'675!5!#67&=47#5.##".547  8"zqA1/Y4#:/ #*.) H''2" H,6`-+33 #`% 1T d! p5 ;'+* (&3{$`!N.Q5#"&'#4&#.#"#"&46326325.#'675!5!#3274#"3"54632g~E0oA4*)2- 1%$ .;J(3' *9!~g  !6 *>0#$,:$e8,Q##<)  +607X>0,.6b++33G-X'$;(<+.GP%&=47#4&#.#"""##"&46326325.+'675!5!#67/]2- 1%$.;J(3' *9!~D"TH''%1T d! <)  +607X>0,.6b++33 #`%$X,.RV#"/#.#"##".547;725#"&54632&#"32?3274#"3"54632%5!A49- -!  9;$1C1*m7aD0;3% 9/:81 )6 *  >0#%+79,Q/#"J5214B "U>0!+$+ 38883X'$;(:33.EI%&=47#.#"##".547;725#"&54632&#"32?675!n/^- -!  9;$1C1*m7aD0;3% 9/:81A6zň1T d! #"J5214B "U>0!+$+ 388E-Y"f33=.PW#"&'#5.#"#"&5473263254&#"#"&547.'5#5!#3274#"3"54632%5L)(*3C YM.EF :56`#/*U)D AML  !6 *>0#$, ?59.97F##;5(W6+* *;) +5+P,],%""33G-X'$;(<73N={.FM%&=47#5.#"#"&5473263254&#"#"&547.'5#5!#675=/[3C YM.EF :56`#/*U)D AML?5T?59.CB _! 5(W6+* *;) +5+P,],%""33#`%f73Nsx"&'#5.#"#"&54732?6324/.''654&#"#".#";27""#"&546754'#5!>32533#3274#"3"54632%2767&#&#"4'#32KT)3E)LZ.EF46 $%!/,#G7 *& eM#F3  -7 *>0#$,3#2$ ?[# '*:96G##5(M@%0 ,<)+). "' $":))3+'!-33 %E3 3X'$;(9O)!2&( 64,#)sl{%&=47#5.#"#"&547326324/.''654&#"#".#";27""#"&546754'#5!>32533#672767&#&#"4'#32/W3F6YM-DH :38 $% /,#G7 *& eK#F3E+3#1% !?Z# '*:1T d! o5)W6%0 ,<) +) * "' $":))3+'!-33 %E3#)!2&(!54,#)O..4@#"&'#5.+"'675!5!#3274#"3"5463254632#"&6A4*)3>$h )7 *>0#$,09twF&9,Q##Y/4^6&333X'$;(<- \'"'.#)5%&=47#5.+"'675!5!#67%54632#"&/X3>$@B/~9twF&CB _! Y/4^6&33#`% \'"y.%#".'732654'#"'732>54&'.#"632'>54##"&54632754&#".#"3254'7#"&546326325!5!# P+@uL.3Sz7%  #;%!, 4B  H"%!/+3''=. 1-=C0; .d?1d'  ):X}r% Ef$ - @I'D/   "C  "IDF(D!/&9-%C 1 F33DaJ[#"'732654+563632>76?67#"&'732654'#"'673264'.'32>7#"&(  `   M%%- )E$M9m%2Q!?"  '1"    1 G,A#=! ~"Y/Tj&/ *,d%P   ( + o#"&'#5.#"#"&547326324&#"327#"'#"'7;>54&#"56;263533#3274#"3"54632A4*)3E  YM.EF 2 56P%!, <" 4#I5N3  !6 *>0#$,9,Q##5)W6), .<) +Ms/6 +:')-1=8k_  $W3G-X'$;(<c%&=47#5.#"#"&547326324&#"327#"'#"'7;>54&#"56;263533#67J/V3E  YM.EF 2 56P%!, <" 4#I5N3C,>G ^! o5)W6), .<) +Ms/6 +:')-1=8k_  $W3x#"&'#4'5&#.#"""&546326324&#"327#"'#"'7;>54&#"56;263533#3274#"3"54632A4*)2- .$# .;C/1) (P%!, <" 4#I5N3  !6 *>0#$,9,Q## > ,4/7,$F1s/6 +:')-1=8k_  $W3G-X'$;(<n%&=47#4'5&#.#"""&546326324&#"327#"'#"'7;>54&#"56;263533#67R/]2- .$# .;C/1) (P%!, <" 4#I5N3A5CB _!  > ,4/7,$F1s/6 +:')-1=8k_  $W3#`%serz#".'#5.#"#"&54732?6324/.''654.#"'#".547#5!632533#3274#"3"54632%2767&#&#"327'654.'"767#K*) 3F7LZ.EG  38$%-X* $4F3  6 *>0#$,0)3#1% ?1b1 '96G5(M@!4 .<)+). "'  )7 ,K$3E3 1'X'$;(0)3#1% ?1b1 'CB b! r5(M@!4 .<)+). "'  )7 ,K$3E3#`%#(82&(E%Y(  . FP73267&#"&'#"&54632765'&5&'#"&'6?32654'4'#5!!3254'"'(# 8-$A71%-7'9X)"<6  $78$=  ""1 7);(&HM'-"$-' "! j3  @*33$H8$1.9C74632'.'#"&'6732654'#5!#&#"327'#"&3254'"':. #7"<8 #Zz%##" 'o:3#8$=&42.'3!E# 33" 4 X6G$H8$6.%/?&'#"&'6?32654'4'#5!#3254'"'͑Y)"<8  $7W)!d8$=j k3  @*33D6,G~8$H8$.MW#".'73254'#5>54&#"3&5467&'#"&'6?32654'4'#5!!3254'"'#."4ZI.PDM0)Wp278-F )2]17/"<8  $78$="/ ,527B%AYD) 1%'4NG >3  @*33$H8$\.Yb#"&'#5.#"#"&54732?632.#"#"'73267.'5#5!#3274#"3"54632%6325A4*)3C  LZ.EF29 :(20A,- B_8 )7 *>0#$,@5"'29,Q##Z6'M@*+ ';)* N4?4j" EK7"333X'$;(<84E\.NW%&=47#5.#"#"&54732?632.#"#"'73267.'5#5!#676325U/[3C  LZ.EF29 :(20A,- B_8c@5"'21T _! 36'M@*+ ';)* N4?4j" EK7"33#`%f84E;su"&'#5.#"#"&54732?6324/.''654.#"'#".'#"&'6?32654'4'#5!632533#3274#"3"54632%2767&#&#"'3254'"'3#6"KT)3E  LZ.EF 46 $%-44 "<8  $7!F3  6 *>0#$,0)3#2$ >8$=`' 96G##5(M@%0 .<)+). "'  )(3  @*3E3G,X'$;(8$=`' >G ^! p5(M@%0 .<)+). "'  )(3  @*3E3 (82&(($H8$6s#"/#4'5.#.#"""&5463263254&/&''654&#"'#".'#"&'6?32654'4'#5!632533#32>74#"3"54632%2767&#&#"'3254'"'3#6K*; 2-1$# -0#$, # 3#2$ !?8$=Z! 96G/ % +5/6-$F1'"),%(3  @*3 E3F,'#'$;(9O ,+2&(!+$H8$  sx%&/&=47#4'5.#.#"""&5463263254&/&''654&#"'#".'#"&'6?32654'4'#5!632533#672767&#&#"'3254'"'3#6/ W2-1$# -0#$,@5"'29,Q##:*  +6"06-+?0N4?4j" EK7"333X'$;(<84E.Zc%&/&=47#4&#.#"""##"&546326325.#"#"'73267.'5#5!#676325X/ ^2- 1#!-54''4&#"3&546327%5!w=3+!*W "h~G29S&+H$+#!&A[G6H&B225GDi&R $j|"F"!0&8%`'2-$02T5FKV!_33!.6:%47""./32>54&#"&54>32632'&5!  o\&D?[>&: L%/,#<(6k :+N@/L$Xs!U?!(# $=4!3 "*W6 39OW ,33N.,08#"&'#5.+'675!5!#3274#"3"54632%'5A4*)39!~g  !6 *>0#$,9V$ %9,Q##Y.6b++33G-X'$;(<7@>e X,&.!%-%&=47#5.+'675!5!#67''/X39!~?B/9V$ %>G _! Y.6b++33#`%@>e X,N.,4=#"&'#5.+'675!5!#3274#"3"54632.'75A4*)39!~g  !6 *>0#$,`:,n6H''%9,Q##Y.6b++33G-X'$;(<&Q . I7Q$X,)."*3%&=47#5.+'675!5!#67.'7/[39!~B?5`:,n6H''%CB _! Y.6b++33#`%o&Q . I7Q$X,.MQ#"&'#5.#"#".54732?6325.+'675!5!#3274#"3"54632%A4*)3C  /D#-DG 19:!~o 6 *>0#$,9V9,Q##4&,-&- /;(*%-6b++33X'$;(<7@.AEM%&=47#5.#"#".54732?6325.+'675!5!#67'A/]3C  /D#-DG 19:!~o6@9V$ %1T d! 4&,-&- /;(*%-6b++33 #`%@>e X,,T.*1'7675!5!##"'72>54&+#5.+"7zw9F0/")( \23@){({`*.33.(L1#B%'7LfX/JX/,w/'733##5./>4'"&5473267ZW$Ld3TT3:3:*4(%1( &'={({R5FaV3R,1-45)*%/DJ+,t{.'72533##&#"32632'7674#"#"&546WQ9M.3CC3V^!F O",E**8+ N  W{({5HM3`7#)"!E( 6)%1^,Q.*'727#'47#5!##5./7##"&3a& !&')3fO=3JN&H{({? $ #a0 133AAZ (R8 ,2 <F'7>32#"'326?#".'732654'#"547&'7&#"325k I4M.',59(#)\H>g@&3"5R0<3% =!o#>(5<$U{({TE)+) W<P-JcM|B @ykAE.0,"4!W>&1 5,. "'7%2>54.'&'#5!!#̦#bDLgc=2-* W{/{({Yi 33=&(0^,.9D'7.+532654./#"&=#5!!;26722654'#"'| %80?R/."C`z;GVx _104*= "12 NC1 {({ <(3/)$7='4[2@].33 @ZK&,.I'7".547#5!!32?&547#".'32?#"&'&5332654'zQ'2KzK9# 0 #0C( $*%*`4H3m9"@({({1/a1337'!>_;HTjN]X ! B!-"$,!@Rx+F! c6'$&,"+'7675!5!33##5'&'#5.+"7qpz63VV364?#q#){({a,+3@X3=$W2;V1,:H^'7#"&'#4&#&+&547;636;24&#""327#".546327>32'4&#"2654&#"53260g*\3B^#H}/69 ":, %n  "#! *5'2*,=35a.W83%'/{({40gBL3E9 C<9*#S x $ $>Ba1>-44'xE67!,3%,'5'723!32654#"'632#"&5#5!654'"&5473|6A/;d&!"-:c4,'d[[3G!*a{({\F636D$"1'6vS?'O3 2 ?/) .,T(4'73##"&54>7>54'#53&5472654'|/2Ư2@fHFV2 ʬP6&(:4I<"G0{({,N73Pr^HZD N=%3]G54#"'62|,'x 35% *>.9d{({?'33q 6Y1&"!=8[?,]('7327+"&54>32533##4&#"@, )F>)3& 3__3Y$-H{({5>3^+1:('c3W+w4,U.'+'7&54>32#"&5/332654&#"%5! + 8&KceT83XYBLF57v{({ '" '}]\|V\IFa!w33,]4'733##5./67654&#"32727+"&5462@\%jI4QQ4";,C&,n'B7)!> (V~Q{({S8P)YX3I% 58<"*$3- 2DJ,-.'7'&547'#5!!67d.Oe,.{({Lcb[2"33!Q~UY-,:5")'7"753##5./7&54632&U#yR3GkMp^),*' yU-J1&{({ DJ3VGK)[F=8Y325!5!##5.#"04#4,b3P3"4'"{({0&D/F"f33.:& ,  ,{s"0'7>32533##4/.''654&#"72767&#&#"] n6F3VV3 $%.% # 3#2$ ?{({O1PE3:. "&!$ 9 ,+2&(!,Y..'73254&##5./67&'#5!!6;2#"'q I<38b($fNr3>(69vK Zr8{({ #1?ZFA_ %M#6@33*8&1,'aNaP'% , .'7.+'675!5!##9!~A3H''%{({.6b++33$X,,A./3'74632&#"3267#".'32654'#"&'5!)4! K%3!%`K9bC:"9%T,P"5R3OX{({$#00):2G #!9 @s1Jm_:Q?3Z733].%'7.'5#5!##54&#"#"&5475@h>NB93`#B2U)D>=79.{({:&!"33C+P,],82N,J.$'767/.+5!##5.'-9Y KU?3)9?Y7;G&4{({9C 233LCR F7""5j, .''7675!5!##5.+"754632#"&G3>09twF&{({^6&33Y/, \'",.3'7;27#"&46326325!5!##4&#"4&#c0&;GG;1.+&S.N3@'." {({)90QR0+ 33Y&&3),<'7#"'7;>54&#"56;263533##4&#"327#"q1 4#I5N3QQ3P%!, <{({')-1=8k_  $W3^s/6 +,D.'7&'#5!##5./6&'5&r-~"F3BdE*I;0|UL{({C#A33@]G:%3(?+7qc{,. ,'76325.'5#5!##5.#"#"'7326q@5"'2 B_853 :(20A,-{({84EK7"33N4?4j" E,.#''72&/32>54&#"&546'5!|6lY&_:&: M$B0d{({Z9o2[p!H9!(# $<#$*9+K33,J.3>'7"&=#5!!>7#".'32>54'4632#"&-%. R(4wY=a9'3.EN#)=#(0 {({6(33X(p"YoIg]$TY2$6-2(+4 ,.!/'7%"&5#5!!>54#"'6324632#".#4Z.M#"(Lj!- {({/733h]d# "mL_-!,9.!-'7&+5!##5&/674632#"&xeU I3C,=,E$Q(/(" !%&{({K]23Lp/8B*5G# "fX '&,#. '7'.+'675!5!##9V$ %9!~A3{({I@>e X,X.6b++33, . %'7%.'77.+'675!5!##`:,n69!~A3H''%{({&Q . I7 .6b++33$X,W.=A'&54632>32'654&#"#5&#"#".5473263254#"753$i.$. +"0$"531;V$6 ()R 3)%Cͫ #3g;- 9+#B2* "2+)A(2  /H33g/2#".'73254'#5>54&#"3&54>5e"4YJ.PDM0(Wp2 88-F )2]$5)g9)327B%AYC) 1%'4N!1 a,"2#".'732654&#"&546s0A\G+N8,3 2L*+>*+3,M9AM*?L?DN64*!.BaR*%&54632#5&#"#"&5473263254#"]*34'2  )M.,#1',7)^6&)# C4$6 3+"(= *%&54632#5&#"#"&5473763254#"f1<31; :I,8'/='$C 2f142*W.2: ;(10I <.#4'!5!##".#"&54>323>$k>j6:D  ))&)H2 >33-&"E,, !2% ,,R&#"&546323#YId6(KE#3M.33e7 /J'69:~'.#'673#bF(B,D<Md33 ) 2L/0/N0!'56753#5.'I"5v0{33  !&, )L!!n?.'25#"&54632"2?#5&#""547326!'# '4##>U$-- ]_sX'. 0',/#Fk'.##"&54732?6323#R    %,1D.*"$ B 33`   O90% %(1  }*"463263253#4&#."##"K'3' (32-+F% .BX=0e7- C",/P#".'732654'""""'732654&'.#"632'>54##"&54632&25F43aF83 T$   #7$%%*+3 < G#&5Y2 !#@:WfXq# -(>IL. ".B  !J)cr23####5.#"#"&547326324&#"327#"'#"'7;>54&#"56;26354&#"&5463.#"B4]XGJJ33E  YM.EF 2 56P%!, <" 4#I5N6?HHiϑf,[TMG )7eE3b5)W6), .<) +Ms/6 +:')-1=8k_  $z"$%HSu$#!.'26=33265#5!##"'67%30" C3X 64YA &)+""*2 FHm33:Q'P w.5%25!32>3247#5!##"'6732>54#"#".M,/'04?;mwXbpYB2 &@28K %'3:P%7(/(D4=?S633]dP$CJV?@ ;.$/726=&#"&46325!5!##"'>73265(<6!(8)5<;X65YB %0"B3\(evR3L33:Q'P*2 GG:.>I%26=4&#"4&#;27#"&46326325!5!##"'7673265M%@'." 0&;GG;1.+&SW 64YB & {0"C2)+Y&&3))90QR0+ 33:Q'P  q*2 GGmEP%26=4&#"327#"'#"'7;>54&#"56;263533##"'673265P%P%!, <" 4#I5N3X 64YB &0"C2)+s/6 +:')-1=8k_  $W3:Q'P q*2 GGm. (,7%5./67'&'#5!##"'?3265&'533265 ,$<!!E7.(X 74YA '(90`G8p3/#C30.,8(33;Q'P ' 3$  \g|+2FHJ.1;H%26=#".'#"&'6?32654'4'#5!##"'>?3254'"'33265Z%4 "<8  $W 65YB G8$=90"C2\+ (3  @*33:Q'P$H8$*4DJ:.-6A%26=.#"#"'73267.'5#5!##"'?632533265U% :(20A,- B_8X 64YA  @5"'230"C3*,N4?4j" EK7"33;Q'P 84Eq*2 GHl^.4#"'&/32>54&#"&54632326=!5!# 65.#Y&_:&: M$B0d/6l8 C3wWt;Q' [p!H9!(# $<#$*9+KZ9D-GH33K.+%>54#"#".547#5!!32>327028K %';m-/'04?  KAhV?@ ;)T622S%7(/(D4+'#I$923#32654'7#"&54>54#!5!4#"#"&547326E5g~2HH28,u#218#9+01S1'$T>CL3F31B 6 5c"274'7&'#"&5463267&#"5 2  "(0M9<("/"& " $-"BA99:Q. $#"./&'73274#"3"5462%5!K*) "  -6 *>0F-$96G 4 3X'$;(:33.7&=47'6753S/c F@m1T d! 3f33.'6325#.'5#5!##5.#"#"'7326!)*03U/+4/"*)6% &9i;(33bA,4,Z 9 a.$6325.'5#5!##5.#"#"'7326`# #$I'o3)!("+ 0/gn+33<699I *Q."632.#"#"'73267.'5#5!#!)/"*)6%&3U/b9RA,4,ZB9;(33 "8e'^x1Ce'#5373264'7#"'&546;5!5!##"_SS);5EC96LxSkEI]Jl$ O)C$3n'1*iR^FI\Ie}II#F%"3267#"&5467'#5'%&'"'>325!5!'&54>32'"!!7'432+M(!'88OCF5(H+b"+SG?*.,8)D $Q|B-&#=8N50C )=ϛ9lM7 SDFH9H5$2 ; % JWH5J* -D_%"3267#"32>7#".547&5467'#5'%&'"'>325!5!'&54>32'"!!7'432+M  "88O#7( &)B=<& +5(H+b"+RH?*.+8)D $Q|B-& =8% <*7#5$%>0C )=ϛ9lM7 SCGH9H5$2 ; % JWH5J* -iL#53&54632'""#"&546;5!5!##"32632.'"'46322674&aaXN4*D!$R*O -s ZG i"0d-'D",,  )NZkM62 ;# F_v4`IIP; ,R?8"-[  8+|KZt#532653#"&53#'7DD%!'/D31E, $K=Ar$2??2 ;UG#532653"&53###53'73CC%-/FbE-4 MHM==Y@r$3?A1%H:L,043###53%7'7&54>212653"&537#533'=R7LMGM31c>L!&!-/FbE-3;CC'e%>BUHH7#*$3?A1%2@>5LJ'+'"#&'#%7'7&54>322653#"&537#53=D $R:- ?1c>O,7+,/D31E-*DCC$ %IY>J7#%1 $2??2&2@&Kk%#532653#"&53&54>32&'"7DD%!'/E21F- $wY 1#*4 #*(@r$2?@1 ZsI, : & #T*e'oE] e *@#53'3.547#5!##"&54654&#"&5475!"327"2USS W*8#P @cs6Ua."MV#C"$!?Q|Z)O R_5<IIp5(DwM-iOMOaEptKO%tQ&37#"32>7#".'47&543254&#".54oF: K?184H&#1! #3;=8( &)D<9%:/\"8p'@43F>7^HH8v#  L2' <*7#+=h=.Ma;"R"0'>aW<D23275#53###"'3274'4632.'#".547&54632'4#"6*SFeFGKNF>&)b  *&2@,61/eAH5=DG:587x%zHHO /O"* )-.4!@*HC1C3;C/59'1&"W8<463!5#53##!"32?2'#"54632327654#""&%!5!+/MMI{@D!'5$a5{w&'; 0!7kEy]HHr63, $X,tB .# rHg%.547#5!5#!5!53###"=[PMBMI)6T8 M84HoHGa4($w.Xg!5!53##5'%&'"'>325#vvNG+b"+,MHG՜9lM7 Se2;463273267#5!##5#"&547'7''67.74#"6dA58GF\S*#: ^(FG"`/6A)P"U&-8.$10880D4J'?9;8,X5EHHm38B6&"UAx7'9 D* ++(xe"+4>3275#53##5'7''67&74#"6w*27GE32&#"4#"6v AMFI   .9'M= B -=*27GEd'-#*,1*9- 1Z"HHC  >*Z.+  9F;7&29/E3EF1"%2++ * e-#53'-32654.'5!5!5!####".5 >>-"C =  GG# :(:i<#D8Y!$%# qHHc(6./XrX e13275#"&'5#5!!632'>54&#"#5'%p;,78jKe)E@(C 8>E!S3   G,!;I"//U@HHA5E "pB.k.'&  7"e153275#"&'5#5!!632'>54&#"#5'%#53p;,78jKe)E@(C vE!S1   G,!",III"//U@HHA5E U.k.(%  7HEe7275#"&'5#5!##5'%5;4;/Ke)GG-")7"/GU@HHだ7&e&.##'7&#"#"'.'732=!5!25!6FG2ZI% A2 4_%11 @_9 G<Cl75M $/Ju&58%X!',(.H24eoe,!632'654&#"#"'.'732=!5!o#)<\8/7X1 ,+ ?- 41%,8M9 G<oqS84X/1K=&',+5(6!N$p',(.He=#"'.'732=!5!!3632'>74&#".547B; /[,11 @_9 G<z$:\B=.!)2IBq158%X!',(.HHmUbk' !,'N$0>Z*e;7267!5!!632'>54&#"#5#"&54?.54672'&#""H8H6ZwC!Q/4FG% q@/.XEJ',06*HHNX<]|/g0 ,tM? A )M+Ab N 4$'<e"L"&54?&'&5732=#5!##5#"'32675#"&54>32&'"32675^pR\; .-MG  O% 5W4%+7C;L"2*6K( Aq?*$!< ","-HHx + 4='A#31D8&8 E4 "?e #7"&'47%.547#5!##5#".547"327!;p%<D?FG=K:0G 8,)Sx'#K)G>)OM? A5&HH # 0%: '#NI]e,0747#5!#32675!5!##5#"&'4?#"&75CaW%;(3f#0]FG py3N5C;GGQW@HHzM !>*:A Ue.7267!5!#632&#"#5#"'&5467672'&#""HF7D")4FGI*?36-DJ')?*HHMX t#-1B$B1 N  #&9*qe<#"&54>32&'"3267#".'&57326=#5!!#3267q7C;L"2*6K2 A?7^@7#; .J65W4&+1D8&8 E3#"<#4QJ0; ","HH224='@$e##"'&54735#5!L!45'9456Hye'"32767#".547.547#5!#; 8+'6J8yM0G 6,*Dd'#K)B'%%TF 0%:- R!&HHIze!5!47#5!#32767#"'&}CaW%;&039$ ~>4'&HC;GGQ*.I{Q"!e-32654/5!5!5!##5#"&'47%5##"'&5c = **GG&p # 9(`Y!0>7 qHH]N?=*(6//@t&e!5!32654.'5!##"'&5, =   #C4`HY!$%# H(6"=3?ze=72654#"#"&546;5!5!##"263227*"'&'"'4632)M#K,p#\@(W%4 &`+Mh!)'S6+3.^HHJ'Qz+N?-oX"Pe,!5!632327#".'#"'&'7232654&#"BG+FU!"#   bG47'2/V(469/3H$IMLF[&9/=,/H (e'463!5!5!###"&'4?5#""'&).8 f(GG]& r!M?1L\>1HfHH,@ ?)_@^O4^s)e@%2>75#53##57#"&'4?#".'3254&#"37".54632)+8=eGG32#ry3bA(*-7)B:-= U9HZ- 8(HH*OM= ?) :.CG x*5-/C&#0=`DAe/46;5!5!##"34'432#'#"&'4?".)B4G)B+ N_ rFl>& -=U^HH+ B001c9>6?) e(92'CgG"&'4?#".547.54632#4.#"6;"3275#53##57p+.,@ 5,3K)-EH  #(6"9.Cs`FG&&I G=)J.% 21T)1GI& ',$DRoHH##OMje ##"&=#5!26=#j#P>H iH&GV s.e7#53!5!463!##"&eek>H i.cH&GV sJe27#"&=#5!#>5*FKf6;;X*U@HH"/1e 7"&=#5!#632&#"#5'3275#Kf)GD/43G.;,78U@HH>L &/""//uXe%!5!0#"&54632&#">7'27'R EgYCJ9!  8+&HDZGEc N},4s)e$#534&'46323##"&546;54#"11 J2#$_ (+0H' 38 *Ga o !2. "qe46;5#5!#3##"'& (\|/0% HHGO7:e327#"&'22654'#5!#=6 <'RI>Q^~)57Cl'/W!)Ud7v`,"6$HH+e 7275#"&'5#5!##5#"&'4?;4;/Jf)GG%r7"/GU@HHN?@)[e43275#"&'5#5!!632'>54&"#5#"&'47%5p;,78jJf)E@(C vE T7.GG r .I"//U@HHA5E U.h1)$>vM6?) "e !5!".54632>7"'67&'"Zʏ4$JN9aJX@%<+)g A 1*+HH:=(Ad2E]F%Gj4!+.+Fbe.:!5!>32#"'#"&5463232>54&#"#.#"32>N'ER ?,\&'^ESXEVO:* #0  G3(SS( H"/aR;:$W\gSJi'L% <:  )@js+be2>#53!5!>32#"'#"&5463232>54&#"#.#"32>#bbN'ER?,\&'^ESXEVO:* "1  G3(SS( CYH"/aR;:$W\gSKh'L% =9  )@js+uXe!5!267#"&54632'&#"3'R (+0EgYCJ')?HW/UZGEc N  #&9e)#%72>7#".5467#5!&#"32>V+wN0,.'N>27+)_nB&' 6DT24'B,) /* HHLL&0  Je 7"&=#53#'327'Kf)u*;,<$ǥU@HH{*"/)e "'&/732=!5!#3#B#D"D1V[9 F<J*0S& 5U',(.HHGQe-3!!3. 54>7.543!5!5!#!"w+zC0E<5#%" 'Q32"&'46?&/"'"#".#&}}]\#9,06 p%?$ */$H 9@;l8/oI D!21 e-%'32##5#"&'4?#"&54632&#"?!5!7+"FG&rEcZB G9!  s*7U]E?>) Kf=Ec M} H2e<4&'463235#53##5#"&'47%5##"&546;54#" J20"eFG  p_ (,1 ' 390HHI   >( ~(a o 2.e $"&'47%46;5#5!####"&%5#r & (\FH_^3@)NN?X HHOnFe+##5#"&'4?#"&54632'&#"327!5!FG&rEcYC G'& :,C;]E??) Jf=Fb M  +6;Hbh 94#">"&'47%.''7&547>325#53##59.5 8q 4u.C$ 11"%:d\MFG&(#'    ?*Q H9c4B3 +7>.HHM@ e8ES2675!5!!632'>54&#"#5#"&547&54672&#"%654.#"6322675"'#G9G9W54vC"P-!5EG1MFg87XD 7+6&370"! 5E7C#G-N2$HHoGP58C):Xm+[,&hi0W9:>*A:[ F.$'/8' h}G$- ,"09yeZ#533254#"#"&546;5!5!##3262632#'2674'"#5#"&54632&#"3275.'7xOO`1o 7"4v%Iyz'<U8&W=%<3HP:.49CG40;J^&" 21200aP41MKV NISd+ ^HHK5?Q R%=*] T,# (  7K'SHH$%eP!/AYW$3X24F# ]&c6:'" 9 nxe48%#"'#"&54735&'73274&#"#"'&546;5!5!#!2632##53G@+'j&:f(0ig J*/41Gki6T :#HOOq 6lU.;6{.#-19+ZHHIsKmxe5H#53%46;5!5!#!2632#5#"'&'47.547&'73254'"#"&"3275#"';xOO=1Gki6 U ;"H|>E4$%/40ih!J-`o<O~e:%4'5%5#"'#"'732654#"'6323275!5!##5#"'732FP("^B_e/`036e-0U%3 O[JPQ5+7-u7"GH HHi+*4D81889e1H"34'432#'#"&547#"&546?&546;5!5!#32?3267'"'A(C#+J`%wDG< ,L6'5B4sG>E-BGS" "7\-B Ex*!B)0o%JhJ0!G1,T=U^HH< %*1 + dS0f&e0747&54;5!5!##"632&#"327432'#"M"FdK( # ##+?K A %;?F('(<\THH ' J)IJ31g#y)}eQ"34'432#'#".547".54632'&#"3>33267'".546;5!5!#O(B+ ')Ta&' %P:A-< > l ;.;^. Xz:A6u<x*!B/0"TY7 0!%H*.@#&;  +L@,eo &A>#327'".546;5!5!# (B+ 13Z&.:r";",A  /'8FXz;A6=KAx*!B00"TXQ_N +# a' "3 1ua%&@>$f7* !>^HHi fe.7"&547&546;5!5!##57##"672'"327FZLMLVGGMe -EFd/)G\#F95O6(^HH=25# HD$;e'3"34'432#'#"&547&546;5!5!#'&#"326)B+ 11S H<;F/B4G"$'$&/%<x+ B00[\3T.A(,=U^HH{N#%.8,e0##"327#"327#".546;5&'&5476;5!5!,x(5]1A).+2]1AVu=uN`9,jIM315,#Y:3'!Z;3N(L/3Q:./D7(&^H9,eA4>;5#".546;5!5!##"327#"32674.'7#"&)'91 ;oFa8,x(5]1A MUUPJ;9 EV2Y%#5 A+L+5P^HH#Y:39*. + +8Y9eP4>;5!5!##"32674.'7#"32674.'7#"&54>;5"".)(;/h!&P5 9$%DUJ;9 CY1Y'91DU:B%5 ^HHX ,%%;!9X + )8YI#5 M Fnke4%#"'#"&54735&'73254&#"#"'&546;5!5!#!2632#G@+'j&:f(0ih J*/41Gki6T :#Hq 6lU.;6{.#-19+ZHHIs9VeT3254#""&546;5!5!##3262#"'323762#"'732654#""&547&'7Y`1o=4v%IVW'<U8&iO7:IM8& 'C)1`1/@=4vaC1B NISd+ ^HHK5>]A5'1( NI4, f-8Y 9VeNW46;5!5!##32?62#"&54632#"&546;5#"&'732654#""&>54#".IVW M8&1Yb@G(1JfL\SB#\R1`1:5=5u*1-XHH   5:'A2? $*.<'/BqR6D'j\  NI#N)#!e7!3##"&'4?35#3.54>7&43!5!5!###!"w(  \#B>Y5+A"DQ7"'4?&#"I,6!5J#9,4)8U=XK  $dAJz(y)=B0S:J5  "U 6 2% > )(&- ?#56#5j5:J;)^H)4s8$:Y-@P? ?_.0ieIR#!"632'654&#"#"&'#"&54>7467.546;5!5!&'26I,6!cL :-0:8U;XI  ^%A#7T  $FW+A? > L5  - #> $6 6"$!#PH! ?!#;)^H /6!:9e.7##5'7&'"'67&#""#"'&/732=!5!!632FG5DZCJ[  A2 "D!1V[9 G<Dl鱉) J N$/0S& 5U',(.HeVT+e=2>7#".5467&'&'&'".546;5!5!##"34'432#" '*E*Q=D7 Xz:B4G)B+GTD6 <*?+:E $ &A>#=U^HH+ B008U;6$  8e#5327#"'&'515%1D!A<HH Eye+!5!47#5!#3267#"&'#"&%#327>cCzW&:&6j |>+B ZK1O\%;&03()HC;GG R`@{R*E@Q*"Qe-5#%##5#"&54?#''&/732=!5!5FGB%t#D4_%1U\9 G<l @ ?) S%*Iu'5U',(.H4Z"#&54>323###53654'.#"\H' / )R=*MMHMOJ#!$f#A,0/FN!HH &/D5":\E$.#"#.54>323###5H8K 0 !H"E/AnIMMHMeDi"!,  32:MIHHI13###53654'.#"#.547632&54632&#"}MMHMPJ# 3J,$2j P7$0 6-A HH &.E.:&<#J&F 67 ;Pj+.#"#&54>32&54632'"3###5W8Ye+7E"I 55[>4[: D $QMHMg0A)>,.!$+? l=E558 ; $F\GG4R6#5357#"'&'76#&54676323###53654'.#"AA>(M P#323###5BBN(M :%#8K 0 !H"E/?nJMMHM"WdxE"3(&YDi"!, 32;MHHH9^%&#"3###53&54>32r!/ SMHMK &@)-%+*((a((-HH//2% '/2h#4'&#3###53&5467632O8FFSMHMKA7,06s^Y:f/O3(HH#(7^':J<f#'&#"3###53&5467632`tޝN=DSMHMK9>DSCfI "I'HH)1X)X5=2327#"'3274'4632.'#"&547&54632'4#"6*TIONF>bc  &2A- 9@UeBD9 '/;597(O >+O"',. /UEIC1C.@+7N&1"  e1!5!463!!"32?2'#"5463232>54#"".Ep&y>D!'a5{w&B 3/ .!0O$HG5 4+j!X+tC )#FL e!5!.547#5!#"B[P)6T8HM84HH4(#x.:e!5!'%&'"'>32vvR+bOJ+SLHt9m&'7 SCO#La(14632732?#"&547'7''6?&74#"6J-8FF\S*!:)D9"_/4D(OEU.7V<9,!1809/C4J'@96<-JCj38B4&%VAy6")9-:,,, +Ke"46327'7''6?.74#"6eA57HF<(ـHU.6U8. 10880D4j Q7^HH #v# L / h>-=''%|-Gm8Kk#53/"#'&54>32QQE #*(:+.)*+I< & #T*;I4* e37".547.543!5!5!#!"632'674''7&#"pw%1:<-@,:=9*@!>?&R9A<:-4;0(@c3;7M <^HH 9!B-WO+::(,"&Kk&54>32&'"Y 1#*4 #*(KsI, : & #T*!.#'#73^{aCҔ Q ###53'#532673#"&53MGN8YY1+5=]BC[<+(HV.*CTVA!, M '7###53[TZpMHM\T\H&547632&'"###53KX($9%0 $SVMHMfpI1 ; % JXIH'?'"#'&54>32###53C32&'"3###53654&#"#&eOJ= I/&4AMMHMOR= / /N)0W,(( ; I>%HH >N+;4 {1#534>32>32&'"3###53654&#"#& QQ%K2J= H.&0AOOGMPR= / /M)J)(0 ,)' ; I B%HH@L+;4|Kt#53#'7``FK=[;|K(t'"#'&54>32#'7(D $R9,-,7)tK=% % IY;H5$2 ;|K*t#53/"#'&547632#'7*QQC!%S9+*%$8%qK=K+ $G\:B=2 ;LL #53#%7'7``Z?1c>[ޚ7#LL='"#&'#%7'7&547632=DD3:+ !?1c>W %%8%$ Io5L7#"2 LL=#53#%7'7&54637"#/=bb?1c>MK&II2 ) :+[њ7#:B: Ca2 U&54>32'"###53'73Y 0$,C!%TMHM==YhsH + ;$H]KH:,!###53%7'7&547632&'"#&'MHMB1d>F '$:$5 $R9*(H7#1 ; % IY5.cLP"%#53'"3###53%7'7&54>7633'PRPI4 LMGM31c>L$%@'Lm CAVHH7#157eI##"327#'#7.547#'"'.'732=!5!#3>;5!5!x)4+?8^/BGb^{`4=.#D/[,13"BZ9 G<UN2,J'-E#S)bӓ&2P1%*Bq15;)Y#',(.HH=5LmHD #53/"3###53'73'&54>32PPD $P%MHM3=+-,8(I, $ HZHH:9I4%1 _g14>325#!5!53##5"&'46?&/"'"#".#&(]\#9,01M}NG p%?$ */$| 9:HGi8/oI D!21  e<'3#5346;5!5!##3>32#".573254#""&WESS31> !& +'+#9=!4lRB$3=Wk'q H6y!BO# `II.3 I 4M'6MQ7.OS@p"#8e '346;5!5!##"327#"'&p Vg68z)5,@7_3DV{jMN!MmpIIL(0I#V*tGGe*'33254'7#"'.5476;5!5!##"M VV(2&96LxSkE102Fl$ !%;#j7'1*iR^F]4B52}II#ce5'346;5!5!##32?2#".573254#""& W{1>cY+R5+#9=!3mQC$3?Wj&q H6y!"!`II;5I 4M'6MQ7.PS@p"e*3'34>;5!5!##"&'4632#"'&>74#" VA+m)2YM>4#5 #:<hSR>&2."7@,hII>.?i%/5A-% )B)93e37267!5!!632'>54&#"#5'7.547672'&#""H8H6ZwC!Q/4FG-5?+-DJ')?*HHNX<]|/h0!+t9TY6@01 N  #&9ze(E%5#"&54>32&'"32675!#'326#".5732=#5!##5'l4A;L"3(6K)<'! 6V4&+n,8bD7" ; .-GG,1,D8(9 E4 ɘ* 4=':H"(?NKB$","-HH'9e##5'%##"'&54735#5!GG,!45'9韓8'456H?e*"327!;'.547#5!##5'%#".548,)Sx,JK),*D?FG-"sO/<)B'#N$&-I! R!&HHc8C 8&:4{e>#53#'#7.'73254#""&546;5!5!##327>32{PPL/1 ^{_-]E91W@oK6t%I{|' *%*6M0E&֓ :GG0.&zLm"8'^HH;2 H]e!%747#5!#327675!5!##5'7#"&!75CaW%;&88-0]FG,603N $ C;GGQ4+8HH{7~A 7te(G"327.54>75!"327654&#"%.547#5!##'#7.54-(1"xW6"C !,6)8"Mt| fK`^{a@Y* .o3+('* pO7a9A@]49HHm (|ZAԓ`LJe$%'-32654/5!5!5!####"'&5 -"C = **GG# :(`8Y!0>7 qHHd(6./?ze.@72674#"#"&546;5!5!##5'7/"'46327675##"3237632)L#K,p#FG-] `+.9N!)'`>E:'%S6+3.^HH鎦9 !z+N?.PX" 0ۥK e0%'-6323275!5!##5#"'#"'&'7232654&#",!hG+FU!$FH 2 bG56'2/V(469/3n9$IMHHIX&9/=,/H 7,e%##"327#'#7&'.5476;5!5!,x"-,@6^0AFb^{`N=4,/?,1/.G!S)aӓ 8]6A8>mH9e*#'#73"3254'7#"'.5476;5!5!#^{`Er%0':275ItQgF000EhǓK?",C!f6'0(hO]E^1A12yHH-Ve<#'#737#".'73254'"#"&546;5!5!##32>72K^{`C'3.2hOA#1W@oB6t%IVW'>,Ӕر(?& 5JO6.&zLm8%^HH% D- e+4#'#734>;5!5!##".547632#"'&>74#" ]{aD?* k(2UM)$1ObPgPQ6(1-Ӕ5@-fHH=,=g 17E5CNJKM#:31Re%'%####"&=#5!26=#-"GG}P325#!5!3XNG+bOJ+,MvМ9m&'7 SH)e36%2>75#53##7'7#".'3254&#"37".5463275)+8=eGG,894cA(*-6*B:-= U9G[-+ 8(HH}8|.DF x*5-/C&#0=aCA e'46;5!5!##"34'432#''%".)B4G)B+2_ ,Xz:-=U^HH+ B00V<9&A>g=%5#"&54>7.54632#54'"#"6;"3275#53##5' d//GM   -3K)-EH&9*5"B$Fo^FH- ?N3   U(2FI&#"P DRoHHs8,e%'-463!5!5!####"&,!Db,GGh8l&HHGV se%%'32##5'%#"&54632&#"?!5!7+"FG-V,EcZB G9!  s*7UX81f=Ga M} H <e+4&'463235#53##5'%5##"&546;54&#" J20"eFH+ ^ '0,380HHl8Ia o .,e$5#46;5#5!##5'%5##"&lɕ (\FH-_S. HH醞9/One-32>75#22654'#5!##5'%5#"&=6 >) ')57FI-!:P^~/W+   +t,"6$HHc8 3ve#"'&''7&'732=!5!#\I6 %18<,9 G<L1A858M8',(.HHe(".54632675!5!##5'75'4?&'"p4$JN9`KX@CD GG-/^ A 1*+H:=(Ac3E]FAxHHc9{Rp)!.+eG4#"6&'&547''6?.54632632'>54&#"'67&'"8.$1mL.=HV-6UA58GF] 57#4 )% 6),Y84(+++(}3PS?+ $)9,*089/D4G )% ; " #+R ' ;&= $i%.327'"&'5#535!##"327#"'&'#>;5p<+>"jJf),w*165G^1ARzgL6' uG,>#-)U@GGA/I.1T)rF2B!|-;ng+2327'"&'5#5%!##"32654'7#"'&'6;5p;,<$jJf) h%0';2DA75JuQiD)' 1`="/)U@HG@",C!258$1)hP[D(@{Vz7$h6#>;5327'"&'5#535!##"327##'#7.'uG, ;,<$jJf),w(4,@4]2A!S) ^{`Pv(!-( b@^O4^s`1HGO-? Ee/574>7&43!5!5!#!"!###"&=.5#2)DQ7&43!5!5!#!"!#5#"&5432&#"32675#.)DQ3m=1/"-P01FT?.le #'".547&=#5'l  (:FM; .j VL   * 5:f,DON Ee1"&'4?!3.54>7&43!5!5!#!"!#5\rBr%53H DQ?5#3.54>7&43!5!5!#!"w(  \  $Bl+52H!DQ5 B^HHNe277'7#"&=#5!#>5. Kf6;;WOTbU@HH"/e:#53%#5346;5!5!##32>32#".573254#""&SSSS31>+=,+":=!4lRB$3?Xj%q H6yOO# `II;5 I 4L(6MQ7.PS@p"e'^]e'^9J#8e'o8 $e.#53'33264'7#"'.5476;5!5!##"USS VV(2&FB96LxSkE102Fl$ O!%;#3n'1*iR^F]4B52}II#ce:#53'346;5!5!##32>72#".57254#""&SS W{2>cY+?,+"9="4lRB$3>Wj'q H6yO!"!`II;5I 4M'6MQ7.NS@p"e.7#53'34>;5!5!##"&'4632#"'&>74#"SS VA+m)2YM>4$6 #9=hSR>&2.2O"7@,hII>.?i%/5A-% (= LLP);31%e'o e'oE e >#53'3#5346;5!5!##32>72#".573254#""&SS W`SS31>+=.+":=!4lRB$3@Xi%q H6yO!BO# `II;5I 3L(6MQ7.PS@p"$i )2327''3%"&'5#535!##"327#"'&'#>;5p<+>"@ WJf),w*165G^1ARzgL6' uG,>#-)Y!ͺU@GGA/I.1T)rF2B!|-;ng /8327''3%"&'5#5%!##"32654'7#"'&'676;5p;,<$@ WJf) h%0';2DA75JuQiD)' .G="/)Z!͹U@HG@",C!258$1)hP[D(@{1zEe39'3%4>7&5463!5!5!#!"!###"&=3.5#2 WD'*7&5463!5!5!#!"!#5 WrBr%53H D'*7&43!5!5!#!"!#5#"&54;#"'"32675#3. WDQ3m=1/"Uw1FT?1e$7#537"&=#5!#632&#"#5'3275#eeKf)GD/43G.;,78.cCU@HH>L &/""//&e%7#53!5!32654.'5!##"&'&5ee, =   # :(Eq0cHY!$%# H(6./ln?L)4>32&#"3!##"'73274&#'7&E!89!m5#73267#"&547.54632&#";#"u2:!8{7pS7#".'#732654+732k 69 j.wv21M* 8!*K9NO,?!eDh1&>!S'%+"'>74.'32>5"#".54632632&#"51k?Y"'    .8&UD(Q:/3l(1 -PF5(@XY'"%._"I1M^*:<(QPE75"&/73>54&#"#"'532>7'&#"7637632&   3*,937#"327.!/:*7B}$pp-H(UD9$UUk)7"&54637654&#"#&54632&#7>7i-C8^)3-_2W?ZPTI%"F2*3q SaQ  G$/3*1A\=EQ4>`B%'7267'.547&547654';#"-R:Df`g~Z!H4|{JGMGoC KAB(5OK  ! )QJ&Gm'463!##".%&#"7>^E; ]J >;$BXL&ARR.5C7'&''7&{>Jb&8\%U%;<|Y%OF L)*- NI_$7= : 7G(#3 gl:6$ %+-F4726=33#'&54>32&'".9lrqoY/STC^7P* It &74354.'.54>323#"&V(#-,%PK"4FA!@uih6L@_<" /9--+C>a;&TB:U++0 HI4&#"56323##"&543x '5"|ih;+5-`>*!V qeU#(:%_%%267#"&=46;265&+732"&#"4w UXq ]!(AW8Dx(  "(\2d8{Y';RT=~  %#574632&#"32>7#"'&Vj)0$31315(]GGQ)h O $G16K$ U JM$"&54>32&#>546;&#"a.0  e2PMY2KU!tFf3 K )<*X^kj$)}]74632&+3267#".nY+, 53(W UU4=,PaM.9i+`= -O/#.'&+'32>7'&'767+.54>32m`5f~638. '1#P 1%(:I;G)%HE*6Tb g5(U S 9'#A (? >2.:'K,%"&5754+73'3267'Nn-T|-%9O,^5R#"'-&23##;#"/#73265.+5%0"j6b- %D/, 5%+NH_R0R.!!(R&632&#";#"&".546776770&:[/$IcW~@$i;nU+/3Cb Q@$GP*DF$%DK=H \q34323#32?672'#"54>7654&#"#&dWtIFB ,?GLE6Fn",>;\#xG.K&)!K%;( 3 ]0}2>%&!#$".547#'!#"327<$DD2 vʐK@L\( +G.i1SSL4 2KV27'75.''4>)C& GM 5= );,>I3#ZKuCq +5&U -+7265'.54323265'.5467%&'&#"\5+4 B"+53n88/1(:'Lo+#,1/ WqK>c?,$ D.3 y     1<+f8$%= CE\"4632'%''7"&7"3654@cOPn[N4BQ$> Iy5GL4&#"5632#"'7326 '%'9BG 18D_-~BS&F<7;0H V $><#n3N8wRc$7"767'63.547.54>32&#";I+MPr* ^MS<%>>!d-*,H|?9 DeU:C2A)8%$5 S6?N +3267'7.'#732>54+732k 69 p)t+X 8% K9NV,?!`4 SS) D>S :SL'46327#"'767''74&#"327#"H9Aw4;(\ 6qPx-$ld5FYS F'-6Ls,>//P*? +77>7'7&'4637654&##&54632&#sE3*4o )-C8^)3-_2W?75 SJ%"4>TFM  G#03 )1A!?,EP/';#"3267'7&=47.547654'y8|{JG5,4s`+<~Z(0(s$QJ&HfE5)aA)H(J %'463!##"& '%'.#"76[H; ^JdX9%!X'&6@PM-5BX}Jo,"H#27'7'7654&+7h9gv5ɓ +,/O2U[Hgy R326=33##"'4632&#"'%il/8lnmm]\I+-+NX8(z5)ISQYTf JP'+74354&'.54>323##"&'%V4',.OC1PR+6?+ih7'&>+;%_;' .:->z ,?Lp9?*:U),/ewEp J23##"&54354&#"56'%fE6ih;-5,` #1 988fU#'9&^9)!V -eF]87265&+532632'74.#"+;#"./#5$1V2H 3",>>B;.I2dQ n (5( RR/-HO>]i<; ;J]R R 72#"&54626546;#"#"'47&546;#";#" !'H9$a( {^EBNW85*(.&`)%t^:IW);&#"3674&+'32654&+5322%,#'9[2! .LK( ,D?D~'XZ&%b1B> %!)#+5&DEa/Z;L@L$+T' QJ5h !GU @2#"&5464+532#"&54632#4'&#"3267'&+'32!!#G`0A9^֍nLS^&HF*9%7H 3XVG,&,$-g?QJ59FcFVdPCj`G:EM!?, #,& ,R?H&5432&"72#"&'73265&'#"&'73254''7&54>32.#"KR?&r&< ~W1n8$@$0b[fi=r Mc*^SE @0f B`3,@aM "Ge&&R+#M. E=R9;*=BW16I33#'P=,.+%2654&''7'7"&54>32&#"%2#"&'71>2RAbZ74.#"326?#"&UC,X?3ch  i) *B`'"  * FX>M^(:=*  *%ZYY2)&  _d0F265#"&'>74.#"327"&54>3273#".53Og ! .7?X8ZL A.H91q*OVO{J/hzpXJ b   F:0DUU@ I @F+-(9;)>'SM1)?TQ*a'46327#"'767''74&#"327#"H9Aw4;(\ 6qPx-$ld5FYS F'-6Ls,>//P*? @%2>54''7&#"#"'532>7'.#"763>32653#"'5"-H*"K9;T*=."3 5-.<'$6;'&-t'F!B-%,%4="9"6]HKV'&;.@BRW H 8 Cy#CA( R F<"&5467654'#"&54>7654&#"763"267#"3267zkbfg &y'{m3PIB765.#"76;#3272>54.##l+e#HYA=1?(Xk"A?[8M3H2:@%Uk:-"b5Nn5/3' 97! *D=2D@A1(5 8%i! Q8 'P:1''3JO     "!G4BO4>32&#"#"'#".5463&'67&5463&2654.'3254&'&'"I 74a>6Si2gJ$ l1PP*Fh4]&  uH! HK.)^"6Qd\C0U>).'I)g**  c(; ,,,< 2F)9" 4%!,@  #FP".5467&'73767'.'"#&'7632&#"#"'+.'732654#"*L@ 211o2MU .=?$n?+51=4cJ3-#"H3 ' bUO  :"%  #BQ1J#,FJ4#"#"'7>72.#"32#"32?&5432#"&54325&'&'7326S 5 (M2s ^hnhZ& OQIHL=9KGidGf0n4[D0_  L* ?() #,,!(  RAyPC{  BH1L37&54>367'.#3327&54;'&'#',*=#6&f)TD 4J6%'+.5467"&54>32&+;#"7674&5472 NZ<'$ ^y((=*S:m08 , *B$ I#'A7y' gTK:*-* S; I=#"D S)HO73267".54?654;7632#'&/#"&5467#".467327"&+*i .4'Q%# k'rL:J N-%5B W' >Co1YV +5%<.;*O ,, =#1I(G) )*(' Q(9,N2"+>1d+HNGN7< 8LI <54-<>@\4%&'#"&547327.54?65&/33274632NB')Q1\aN?2L>w *HG*g6e(AQp &2U,4~T) фyYn5645=JS!C.q( (:D;27c50"3%2#"'#'&'732>7'&''7'7&54632&5l% /bzbiHCT[219 )]Sj NKX.`E 'XT2GL{3@* ,$s0W3b@*BJ0%4#";!"/#53265&+7!2632#"'532kp(!/gK - (U/;j7Y/4 97!K>#?++[R0R,$LR:?!1`BNU O e=3632&'#327#".'#;!"&/#53265&+72;(@$tJr/PK@!  !)(2&N3iN (T/<k?)Nu/  E 8&([  RR,$LR2,%2#73267'.+532654&+5>;8GDPNSZV-"".1:g.u cp 6I4)CzG9[;(U%&V H?! 277>7'7.5467&+9E&9 ,3 iV04341 ZS= $C*Q]M.[$.%2>5#"&467''7&54>32#67>54#"(c>xjGT<,X.m?M-WtlR@--(")P#/9@{`;pJ 9fZ;.=*5H<$C1 K+O- :"6b#+"327#"&547&54672.#"#5'7.?.2v4^n=WTD39Bh7 <Lz>*65N b\Q sA%#"&546354#"327#"&54>7"&56732&#";233#5#>61'0-%;= A"$C!B.8 17)@[%E' oo$!7")2$4L'Wv6++ ,`J*$! J&$ koO472&#"#32327653#'#"&546;254+27#".54>3.jd 68 '#$TV,83#@;.mm]1Mb3L_FRg`= "0X[C*+;3UJ G*& !2( (i$]I#,*YG^oH'=fA7]4*I" <2654&#"'&/#67#"&54?''7&54>32E% =!B= Hp]15L?ARpƨBd%M36P%c$%2.!' -6$1K#4P6I!,B U>_+M6YT.9@ $)!0&3 &84'37>'2#%'7"&54>327#"&''%oP :1YZq  .WAMfIKc{iD,%BU"D9+ $I<  ^BfZ;D'*5&-nL<7PTG>2#"'54>7"'4>54'76323463236grK)@.<=WW=jP"P'0ZJ;UU;F,I.u=7654.#"#&eUmmtHFA!+?HLE XG$ޱJu+*!;U U'U8265'.543253#53265'.5467%&'&#"R5+4 A"+53q6oo:G+5 9'Ll,!-1/ YqJ@a=,$ D.3  1;+f 9&%= CG\ !"365446323#5'%''7"&%> IdMQlZllN3AM+!6.H69CL4&#"5632#"'7326#5'%3'9BG 18D_-~BS&F<7n;2m0H V $><#n3N8OSRIq#)7"7673#5'63&'&547.54>32&#";J+KQmmr* ]''S<&=?!c,*,J|?: C7lU: !2A)8%$5 S6?N5%#5'7.'#732654+532+326735n)u"0%7")K9N!69qm(pS)7$S&8SM@+>`2 E'/77>73#5'7&'4637654&##&54632&#jE3*&z oo)-D8^)4-`1W@66 TJ##5;&FM G#03*1A!?,FO+/+;#"32673#5'7&=47.547654'o5|{JG5+np3q_+;} X(0(s*QJ&F%~E5)aA)F*I  %@ #%&#"7>%463!53#5'%5##"&FW&& [H:nn:!]LcYNH# APf[I!33CU273#5'7'54+7^8fxoo6ʏ,,.N1ZIfzR!326=3353#5'%5##"'4632&#"/32353#5'%5#"&T3&)3 0PC1PS+5?.imm;%h8L=d;' M>?z ,>Lo9?+:_xEpg),-"2353#5'%5##"&54354&#"56]E6ioo:i;-4,_ #288f>eF]j#&8'^9* V =7265&+5326323#7'7.#"+;#"/#5%1U3!"K;9ln; % 2cQ n/ (4'!RR ,ID ]i 0  ;J] R0R77>73#5'7.467&+w:C'9 mm-SKiU13441 0~S>V_ M+ 932654#"#5#"&'7>7''7"&54>32#76732673GP#/<n;d,DU BX/W?M-WthR7 +,(< nC-:",P&'>5#(<9fZ;D'*5H^,M6YT.;?/F!( H$4 $%3/!'.6%'.+8%3275%'7"&54632#53#5#"&''%74'37>IIJc|-XfTXs  mmbDU!C@Q :6%-BfZ;E&3=J;  aq=6PT9+%*432&#"#.֍ $&3:((zx : "(e&4EL@#'#73^{`D3?73267'3265'.'"&5754+7367&'>72&#"#"'&,8O$S@)LMA-Ri-)05nZA*(DX\ '1`A!7-.6A;+#^5R%(@K XC!")8 =;$873267'"&5754+73>54&#"76323267#"&'-%8PLRi- {F)92@*M#A?&54&#"763232767'72&32>5'.'%32767'eX;O\')77]{`89Pj-'24nZ?,'RA11FH-2 ;.D "(:'@( ēo^5R "(AKX6A2%(#-4&#"76323267#'#7.54>)*92?-I$@?%;TT;h0d8[B^{`$?:!=WX=o a :'-70':G%Z7 ɓ*G.*= "[T3#.''#"&563232>7'.'&543253To$*@D5EW*\L] 0LT6  * Dk"=*$n S$$ 9Z3,2I4    @T,73265'.'&=472&#"#'#7&'&'X k1:N Kz(=<4.Bd^#/ 8>$]{`OD:H1' "'7[< +!)4,E)Ǔ ,& 3%2654&'"4672&#"#'#7.5467&5)OFhITlp^@-(FWFSS7":@%^{a2N, [47*:.#4;'6CFCL[C G5/H*Ɠ$0+AS%1#'#7.54>54#"7633274>32^{a3M- >XY>l4A/Kvc=XW=>/ 2${fE퓓&2..@ !2gS90:)$.9)+`-< G67''73'&'77>54'" XRBV8T<;JC4IV)96( i=x)' 'L; ' <2654+'767+&'71E= 9&%/,-BhM`A,0)8 ;0-8=9514>324.'#;65'.1CQ-=mF*L+_80/; B# [/+01>.E94JN/ > 4T267#".5432#"2>#q ,v6=Y-D*N ,-t(q# 4632#"&#3Y'"!#2^e#)%`"#324&5432&!05Qڑ[*a'0"D m j P^!"#324&5432&2"4!!05PْY*Za'0"D m j P8 *TV= 2#"&546'7#%7#')1qe[N0&&%#E["#%7'7>;)!<[Nrd/"Gm1pE#6&LX! 4632#"&7"#%7'7>;="..""*W#A&<01^-u&"-+*"-Y<K&'#".533275432&#"{4 524^\$#?$%*!-^D} 6b2*#K&'#".533275432&#"{4 524^\$#?$%*!-^D} 6b2*#%!5!^72#".5464&#"326.;& . @p %()2>+- & /;j2)<'"%!5!=©^5B 62#"&5464>32&#"3%#"/732654&#'7"&'"#'$,#9;#n 9Ckkggi"I0Lg:&*32'"+2>;g)1"V>UkJ <2#"&546#34&#"5632#".54'7326h&,($*hh9CL->u9  .Q22" WB'9<*'1+1N X 9  :)<.! " ?LJ9 32#"&546'32673#5#"&547.54632&#";#")1)"62;"<}pqz;qU=nYe/*/L~}K')%"3'! ;@k-[JA*:':HV8@Q!F ?2#"&5462#"&5463265'.'&=472&#"#"&'( 5,"%-$j j2:N My'>:6/Bc=Pb/.LN*<)P( # "%).)!+G2& #'7\<')45N';<lB 82#"&54#"';2673#5#".'#73267'&/#732I>' &$L#:7k .upqx6$>)$8 *9P)" 10D fC9q1*3 U#  TN4Y <J2#"&546'47"&54>;#";#"27#"'4672#".32654.#"( '$5fX#G'! J<#I&)?TAM).S\PyQp!#!(=*" 1(!G/D3"5 T5#X.Y~Og;=v pV]Q@3'0 972#"&54627+.5467'."32>5#".546(')",$1gE-L #>*49*#y<(, /:(WB*)1*7'.#"7636321 '$5Xu%' C 66% ".4*+ ;;W*@.&6 6.-A%6AB"'" 1'"G#?6jHf0YWE8F&7_JMW&(>-@FSX*Fa +2#"&546353###"&'7327'.#"'6&1($**gyjopi{M*r )P N-L"=+*&!2+'lQ#_S:[ /&0;[ 12#"&5464&#"7633267#".54>)205+:2B-Ko^.c9wX*NK->YZ>R+&!(&# dQ8-:2'XE*\&>)Q7+>"" -:2#"&5464672&#"#".5467&52>54&'"' %,,4o_@-(FV FSR7-KM)@b8# X7724HfJTW)2))AMZD F57O($4/AR'3:'7C  22#"&546'3265'.'&=472&#"#"'&''0'$*: j2:N Lz'=<4/Ad^#/.LN*jU9Q)$#2+G2&!"'7\=+!)45N'7$ 52#"&5467 54>54#"7633274>32(!$)"5x>XY>l2C/Kvc,BMB,>/'%{Hcb*31'!9.@ !3 gS8)7(.9(7 `#8d (82#"&54%#3%#"'53674&#""76"&533267c>& '$-poE_$H1#&(M,$ %%jsM=(Xt`*" 1n`P 99!Oc*7Rԅe=&!QK"$*11 )ESqO+5G> *72#".546#3".=4+7323767W')  +rp+E&&[<%# (CI&)>' " (MO$,! T-0!  Y 4C ?2#"&5464>32&#"3%2#"'#'&'7;>7'&''7&d&')"* 553b!>9>& 6g|fkKCWb" ._ Q>*)1*,  J( 'ZU.HO~5A"'59e 172#"&546726=3353##'&54>32&#"|&()"*z/:osopr +M0311'$5(8@,joqh(dU'%8().%R M#6GC1"'%"2(! A*:   k\=" 48K/,A?c<' (72#"&5464&#"5632353#5##"&543O' )",B"&=kpqj;.8+b!)" 1*6, X rj$ &;&`+ 972#"&546#3267#".=46;2654+532"&#"N& )"*qp4wW\:PA. `+EX9G|& ",)" 1)A}3f9 ,P3';TU> W +2#"&546265&+7!2;!"./#5>)'/4.>lIeV;K'W*-'(!-%NT\D&H!] U TN 12#"&546%#3#572>7#".54632&#"[(,54popΣ6*7E9's*1"5342#)>(#$'!R W&9a32&'3654r)"$'$6rNgs %! 2QV~ ?+" )2(X*11'" VHm"45V]_8l+QO1 R ^6E`k (2#"&546#3267#".54672&#}& )"*po([ VYA\,kX4.*55)" 1)+b?+GD%Q_N,;lwK :72#"&546#3"27+.54>32#.'&+'32>74&R2165poh+'1(;L5/<fX z%0< V <*91%73267'2#"&5467"&5754+7353#54:QJ()'$5Un. pqV(".*<)2&" `6T$pM 332#"&54>2353###;#"/#7326=4&+5''!* %&1#mop S=. 'F1, *..   7&-5a T1U,!*U# A2#"&5467"&547&54;.'"".=7654&#";27s& "$65̋ O+@-.Oy#=WV8(C$5YB?L cIxMON34"%&"w3$SP %@H6Q  !%.B;)8R7\( )'%"5632#"'53254"&533267 >$!0NS#G0"&%NShpK<(YqWvQgH88#Md^,c3@T Y 5)1"&533267"5632#"'53254.#5'%3pqK;(Zf1"!0LU %>'!&%N #*p=Do(`3?V YQiG+0$Md "W#"&##"&5463267 +W t5#//  E<Y]=&%( f`w'27#".'./&54676;53#";(  ()  % ,5#   "! *( 4`' ,T326=3#"&5432#V-!;-6BkR$<$K4)5Di/EZ#".'6532=35!5!## D*#.)gdAV  B %MAA1|".'#"54632353'32=!/  <1B (G2*)3x.@*H4@x#"&'>753"&#"32>72F.&4qN-(ZA;%\+?J)&9-8\T!1>u$#".54232>54&#"#"5432327 LL?.S(d$).H3+)0C@,-=,<$% 2!: $ k[5654&#"&54>353&8 i<=20=32=O >"$  5nY)!RR$6O #".'3s,VjH.2tIv#"&##"&54632675! f r5"0- 8 4/H N9$ ( G;;Be #&'5327%326=3#"&5432#PcSVbfRZ\W,#;O2CjR&%$2)((B4)LAi/D  gd #".'5327'"&547332>2+iY$G%fQZ]3P 2$! ' 8)(Wf+)MP0/'&z#"&'35!q?#9-.L-X&9665$5!5!2#"&##"&546326788v  q5"0- 11l0013 N9$ ( GR2!5!5!326=3#"&5432#VeeM6!;P1HeR&11p11nJ2+dhLBo$E  @ "#".54632#"54654'&/A! "HQRG'!!#6' J:/TR1/*G$1X 4632#"&10% !$%% 1(00 *3Y327#"'#"54?654'&#"#".54632327"'7&'&54675!5!!5>54&#"cOT]cQP_>  (_ @JKAXZT]^ZM>>|^_|2%,(zBMl**=! 9""3 # L.3,KJ" 8##1aNAAO0G) @45Jfq?455X 4632"&#+5;+,00!H k@``YYE$!&00FAA@ =G[#"'5327'4&##3263>7%4>32!!#"&/47#5!.#"%"&547332>53@kL[]cVX^8Z,kaA6FzajJ" &=kFTv8BLEt'L4OG^v7R/ BUI-$3 V((A('7[5!WYA%EO9&5ZW2AZn"Dk65 в$Ajn+=5KX=I<.' BW &GQe#"'53275#"'5327'4&##3263>7%4>32!!#"&/47#5!.#"%"&5473326534iYP\cUX^lLY]cUX^,Z,kaA6FzajJ" &=kFTv8BLEt'L4OG^v7R/ BU&%)MV''A('V((A('7[5!WYA%EO9&5ZW2AZn"Dk65 в$Ajn+=5KX=&8 ?F BW AL"&5473326534&#"#326!2"'"!!#"#53267{BUI-+KWBY- +Z=v_6F@Cq\=Z5%WMFu'L5D ##$z0X=I<@E CV8[g5 41*H8*AXo#Dk64 !AA;8:Q'"a&"f'"c'"'"+'"i '" '"o '"\ '"'"l'"['"'"'"'"Z'"}'"zz'"E'"'"tx'"'"t'"X!h'"/# T62#"&5426?5;#####"&54>=&'#"&54>54'4&+532676&6+;@@cAN/7 '$+$BJ;&  &8\>]p% "3[9'AFi JE>&:*C$ &WI]7/$;" 2#"&'67&'###535463235#&98F5̈^(8U?CK$>! c/nwu@}}kc52 |-{ %L:cA8'!f# 'bpLT[gn@A:g*P5 B62#"&543567&'#5!4&#"#4>323####"32>2#"&8&}-{MwZ2JC*@"C.am@_|:K$>! X:p+%/|T[gnAf>H$?<$g:A@8V!f# *_p@'&&@"z''&@"z'(&?"]h')M&",'"zh'+&",SX X4632"&26?5;+#+##"&54>=&'#"&54>54'4&+532676O00!H D6+;pWTTJ@ W/AN/7 '$+$BJ;&  &8\>]$!&00 "3[9'AFi JE>&:*C$ &WI]7/$;" %1 %,5% ''Z''W  ", %(E#(K#"&##"&5463267'7 D< r53/!45 @./1P4& Vk_B32=3#"&5432'7N3?:O3>gJ'Dr45p?M|{D9X-= a60''7'327#".'.546;53#";035?  *  1#,",5"LuW    +"J#)L&%3#"'53232=#3.XMG @vXeT58Q;1j%#####5354632&#"354632&#"WSSFFA: :A: : D88DY8?E5RY8?E5R ####53546;#.#";SSFFA:EK: 8DY8?f5R####53546325&#"3SSFFA:M@:'8DY8?G5RD+#5&#"3######5354632&#"3546323DS*:SzSySFFA: :yA: Oi"5R88DY8?E5RY8??")######5354632&#"35463235&#"?SuSySFFA: :yA:.):'88DY8?E5RY8?͆5R&#327#"&5#5353##5354632&#"V'14GGSSFFA: : DF+(DDY8?E5AO3273#5#"&5##5#"&5332>75#".=3;.54>;#"{10_XPWOAQVT) !!ARX$*,: w9B-=3!!!32=3#5#".5#"#5#"&'332>7u+3+-,Q/=WO:k$9% 6**T) !!AQX$*,: 6&@&$BDGaJU'!1%&+9O8G6-+2*A8@4.54>3!632#4&'"##"#5#"&'332>7u+3+-,>]dOX49*-X6**T) !!AQX$*,: 6&@&$GhaI?>"H3l%&+9O8G6-+2*A84<3273#5#"&5#3#".=332>=#"&54>;%;5#"10_XPWOAQ\V=_6I%Xn*-eX%O;C= 0%FS]F4D8 E;C.~"H3O;,-2hA8F4.54>;3253#5#".5'###"#5#"&'332>7u+3+-,+@XO9l1D$X6**T) !!AQX$*,: 6&@&$BF"JV%D7-}l%&+9O8G6-+2*:X&52- 273#"&'3J GRC@OG*RIPPI%-8WX'%X%>53+533"4fW,?L:ꥋZNT<v3R0! L :@X##=47'3>7538}rAZWzFVW{p"zGx)qA&X#!5!W  LL?4X #.'!53:{lWAZ WXamtQ: Lw9X4&#!5!2#!5!265|Tbxy\WysBWLY[LVC%!#467>=!53!W$C;6 aW>MWS`12!/ Hᕙ1P+7.H?5X 2!4&#! o]X[X+7^@%X #.'!5{mWAZ>XamtQ: L#uX4&#!#525#5!2#[Pux<oWtESgdLzFL}a2!5353F FF<&<&<'&<&&:}FX&:FX&:FX'm:6X&X&&X&?:X&1 X&3- (X&)=:X&5X&F'@&8X&9X&%!&H0:X&C3FX&u=9X&/98 X&ME?X&ZF3X&>8&X&)%X&<X'#{X&>&:6&09&?&8HF#53>=3>Q;fhW):WD'DZ9q #4&#"34632354>#3 r]NVYD-:/8**H**HH!PGS_\;A2)1''4,&-$'e!D]`[`]!e{#8!;V RX <&5":W RX\ %!#3!!!#3!!#H\TFWh[ ah#S cU &`g&?5!#'7"%7"''4 i'/;` ad<_- #&'#'#&'5o(O&i%pp%#T eQ bll"G #'5!#3!! 8&u M K 1k R&>7%37"''49)-6e@X$>/ J%##5##533533533##4 ;0*XXZ ;p Dd22^`_`^!!C O&'3!#3!!UX'.54#%%h47NP%4)|r-7&'5!.'5!.'5!g/W$"!W4Wd%(% % (& 3 (( C7%3V)-X$t|3!!!#$h56% &|t-.%!#'#&'5o%oq(S!h}kk}S  %73#&'!#3k _n&E `tj&N hepK'#3%#3#3#3#3l(& j '& 9P(& ~[/(& f#(&|p'Wl*W;\W\W@X 2j-X d%.:%'.'5!>7'67'67&'7"&#!567i> )2#W D. 0tR,-" 1  (&},  wT.'%v ''7237'"#'677{z|WUWX \ X|}z|W^!TX ^V}   7#3!!! `&  h'&d !7#3!!!!!#5&'53N^~X d~#Il~OW"$@aUg'Xj'&Iq%5Mmm$6ff8"!#3.'5%&''foX!k %   Rer)x&P i?X%r %   b iis'Lhd{:A%#5'535&'535&'5333#5&'535&'535&'5333'5!]!ee'>e!D]`[`]! Ee#Be E]`]`XcX\&5":W RX <#8!;W RO(&|,9 #&'5!9S'Y DtC##&'#'#&'57#3%!o(O&i%pp%#T! Zo eQ bll"G  a(c,,#'#&'5%##5##33533o%oq(S!` !y ! ! byZh}kk}S u    iq f`, &'5%#'##3!!%3S!Y%or'U bV(.}S S}kk i'U$|5!#73!!!# ^($m56L#&Sx-.N&< 7%3'5!#V)-^'X$Q  #'#&'5%3#33'co%pq'VU),[d)i}kk}T X%l)) ,&.##5#!!!!#333533%3R!z& WlZ&E`z\V'/R#u%  _&uc'&N iq hQ$%'#%3737&'5%#'#UV)-),xT!n%or'XX%T f}kk,"#'#&'57#3!!#33'o%oq(S! `!Z&h}kk}S  h' a)%^!'#5##335&'5!373U8'=k 6=X ;8),u 8nN6pX% C,'!!!!!#3!#3373#/B;.Wl'C&E d~ZP*,VR&:9_&u'J'&N kM a[$&|",33#7##5!#&#5'5'#&'5$l၃P!& ;4(V9l'p$L$'Q|w..S%=/VUfk%H~%D #&'#'#&'5#33' O(O&i%pp%#TZd! %GQ bll"Gfl( .#'#&'57#3!!o%oq(S! `"h}kk}S  h'!g'#'#&'5#33#3#3o&oq'VN_Z_#Ili}kk}T N)h'\h&&Iq%B#+5!#3#'#'33##33%3 c'H cl<^RkV(.Wj&o(g'T#}%:U%# 5!#%3%5!# ^'V%1^'SL%Q.!.#/#'&'57!!#7#'73#'53!!p%oq(R"l~V  b% a~!, xjllSL<GHF"  5!#&%3!!!#_'& $h56L!&T%r-/N# %'#-373%5!#UV9V(.9%1 ^(UL%S|%33#7##V%1T$i၃50&4N$Hs..73!'#%373337##zV9V'/9++,%j . &1QZ#Or/0 . Y %75337&'5!#5#%5!#&#5'5# ^#T $L% o(O@l&p ef"G%HeQPfkL(3'7##%3%#'5!0$h y !  &V(.]'u Lr/ |/ !  $UOkC#,%#5#/'73533'33###'33#VJ#I cJ(.])>jQ ^vv&IkUs e()f}%R%[g( %#5#%3533'#'5!NV(.]&~]'u }vUr e(&#OkA$-#5'#375'5!3#'#'33#33##]'!J Zt fl:_iQO Uawkl&p(h'k}%T#0&#7#5'#5&'57'##335&'5! p%oq(R"tM]tX bM(jllSW fZNXZg!#33#3#35!#N_Z_#Il* c'"N)h'\h&&Iq% WG87#37373#'#/# `*,9'/V9V! h[Q#  "7#333'5!#5#'%3'# `%nu ^'O!&8 yC h(vkSQ$,0 | A##'5!7#'#'33##33]'u fl;_NjOkl'p(h'N)}% &/5!#5##5##335335%5!# ^'% !z  !\z ^ ^(S$   t   frjkS%'#-373%#3!!wV9V(.9%1 `!UL% h'  #5'#5&'53!!!!#p%oq(R"_ 7l~V xjllSG (L<B 3#7##%3%3>$l yO!&V)-U),Bu. |/Q$.X&X%}#,4333##'##333#%5!#%3$Aقc&J% &$hL̓JL ^'V%1}C-.e(%!|t #SL%&,1:@KT]c5!#5##5##7##33533533&7#35#35#35&'#35#'35#35# ^'TLV! %   &%n]I]_^Jl76l{!- ^JKJlKI!- lLJ7RM<Q= &   |wDKDͅ F5L(DLu# F5L# L( ',5;FMS[c5!#5##5##5##33733733&7#35#37#3'&'#35#'35#35#%%3 ^(TLV "M&%n^I^_^Jl76l {!- @.JKI#lKIZlLJ 1%V%1RM<Q="[|wEKEͅ F5L(DLu# )"4L=L L%I*%%3'5!#%33#7#aV%1 ,E(V%1$ 6၃g&N$.=L%V;./i$W&7'#'7373'#'737375!#8%8%cd8&8#&c; Q!&6K6K7'Y0P(NW,7'#'7373'#'7373%#'#&'58%8%cd8&8#&c<o%oq'=86K6K7'Y0PD h}kk}<0G8!7537373#'#/#5##3J `+*8'/V8U!JJO Qp h]Q# UuY J27-37373#'#'##5##533533533##V)-G),8%1V9U?&4 TW] ;TXL%zJe&?Za]aa!!D '7"/  b. 5!# _(T'.'5!3#&'53w$  fq g(M't$!][WM! C7%3V)-X$  7'73'73y<il> kuy*Hty,F5!#673#&'!#3 _'&EYnO[T&N gY bKj 732654'7#".547&'767Q/0I]SI89{R;Z2!D;J(:AF7yCvH]Zu)AI%!4>/B9IX>H3267&54>73326?3&'#"&5463237#"'#"&54>73#"3268  _= ^) ?*$`n>O %$1"*<6J _fH*96P0D 81V RNkPE@>KF",,SP/=?4#%7IX 23254&#"3267#"&54?#"&54632>?3)-&,$R&>)OQ/>rIM+>(.! Z4< ))!%5`?OX0 hZ$x 6FG6=@2 Z7X3#[X^X7X"&54?'#3673327@_i?BZXZ .kZ5MPQUU(xxFY?X7?6XhZ 6b,%32>7#"&54654#"#>32&#"632-$ L@d <)`AVU6S(-6H.- & ,W %/GH>RP-C'nT$,KA:<F6FOOp "32654&4632#"&cFsT=FsTqaqa`R`Rզzզ0b##"&#"'>32327]$2XB .P 2v5X F#0l'#"&547>54&#"'63232654&COdiSejJ;#"*;2KZI^~9:.69RrdSOqtWK\7),)/J<;~>E'=E'=32326?26-KQbGY"(%1  > % =%&#-CMTPQX[N2tZ  z#/<?X)332?3&'#"&5463237#"&54>73#"326+&>%^) ?*$`n>O 0L1Y _fH*96"4)RNkPE@>KL(WL0D 81#%7:l*423267#"&54654&'#767&5463>54#"EQ $)-XYWKT,/# _!1I:71lH7.HQA &d TI-+2Wm>7N-G 1)9TX)%3267#"&547"&54>73326?3L-&,$R&>)OQ$dD _= Z4%5`?OX0 hZ*n)TO0D 81V,Z=!l=GO'632?6323267#"&54?#"546?654&#"3267>54#"FO40X9N==xl4[*(/ )!2P2X"3>327#"&5473326?3#7&#"2 #5#X-092D_5$ZX_)1*"5NG-4 Q:9FA.I!CPt5g'3267#"&54654.#"#>32+$$)2R %A*A^(!<?_F+D+OT@9$7i2B]7)[Z3#"&FC(^9X732654&#"#3632#"&5P,D+2)_,Z0-E%dYBqX>K8V1-GH!cZAFl,4#"'>3232>7# 54>7>u_ ]%H2ul\S > 1Q=$\ +FB$SNUG(+R_BX +6 #*@$ =9X1363232>7#"&54654#"##"&54733267&["%,W -$ L@d <)^<#52D ^=XF6F%/GH>RP-C*TO$29)V*OX0732>767#"&5467&5733254./3#"'J7" N&QM_u"*BZS: _ZD3C0@',THvX3M'MZs6%"!)-c7I b$/73267#"&54>7&54632&'7>54&#"C9@UTWZ! #H;6/,B:9F)/=lpP(D!2V:H,.. 8 "+.+l%2.#"#"'73254.546# 8"83#.CB.}PTB2K.AB.Il 2'#?25L.K[_9>U847K(9fNX73267#"&5473-&,$R&>)OQ/Z4%5`?OX0 hZ$xZ8l 4.#"32$4632!'267#"l,"/D6.SlTBT,RK +>[a'49;87Au&ShOUPO0&l063263232>7#"&54654#"#654&#"0+mOQ)",W -$ L@d <# -Z4-& rhZF6F%/GH>RP-C6Z(%5#3l 632#7&"#>327654&#"+mOQ/Z$T"_ #5#O* -& rhZ$x1Pt5NG-(MZ(%5#Dl&#"32673#7#".54632hK,D+2)_,Z0-E%dYBqX>K8VϷ-GH!cZANX'%3267#"'#"&54733267473-&,$R&>)U)+QOQ/Z4-&# /Z4%5`?OX0 <3232>7#"&54654#"#7>54&#"#654&#"0+mOQ)"9# L,W -$ L@d <#Z!# -Z4-& rhZ-F6F%/GH>RP-C $Ȫ "!6Z(%5#rrpj#7lz J l'.#"3"3267#"&5467&54632 +4+343@OE=9WQ6`;T6"2wO?m%'27].5?U+c`@g_@a-GAJG41X%%32>7#"&54654#"##7!!632-$ L@d <*`R&,W %/GH>RP-C'RRF6Fnjd#Al!,"&54>54&4632#"'3267>54&#"Yz$A-DSF&S>39[Tz,fV%735&2^)  '?S:[O +<9[ _!5&K[X.% P`^F#VM0D 81. p632#"&'732654&#"qRdaq=eS,JFsT=<4XzF=1Z`R<O,p%#"&54632&#"327Rdaq=eS,JFsT=<4@XzF=1Z`R<NX"&547332>73=Q/Z4-& 7&'7'6_I0B[R S_'N5'"M B.3,=p$dJDyT>,>[N@+  0*yu`Hd97P%'>54'#"54632'&'32J >@eq%=!Zg$V<5|P0$T>e'7R'6?&CD55-T6S Y_fT yj7P 4632&'#"%&'327$5 (VE~/%E,-8pI 1SKX3JP7 mP>32&''67''67&'7> 2*4H =* %;;yV%',JugTT~AI0 (7kO)#F57R.547#"&54732>7M-9/=1CC':L#9 (H+% 1,RTyX_\5'(/#2=*,  '3OW7UP267#"."'>32*Y1cn!A'%1N .'(2%0o<>I>6P'#"&'7632.'&'&'67'>\=][Y3 C<+ #4'p&?'X3_'EaB #b0S65 ?$ee_m) 7=ao-%&''7#52672>54'7#"&547 -&82/&0".#@[:0 (L`}>GTΉ|46tb725#-=7$1E;aZM^-U5;7 Ke "&54632654'7'67&''7&0Qr6fua 3S7+.Eq2n lN  fh$$.(>=[=78"&54732>767'672CF%5?,G;3I*18J.)>@(/' ,!*T:ӛ%7 7"&54>732632'>54#"-;Xmf 5c?G$%#3mB4)= b 0#_!( F 78.547376?'>'3JN'?CJ3?:B74.3_:?&30&NP&@8 "SK^78J2'54#"#"54326,8*-&B1$@&7-".7$EM7 dB2>7.'#"54>7#"5467'632>S.@` 9#5%"84 ~0" !"  $4:A({>3 *")$'/;_W_(bV\$%4H &! *# %I$% ,$ 7R?P 2#"&543254&#"pOK;5M6-O."-PD57#".'&'76--%5 ] (/<"l/'5 1LPKK19WDQ [J/g -ed78 ",52.'#"&54632>4#"%"327&$V_M+*1>TiE#+Y,=RX/M07& % hA7]C/!=uc";V38(-$ 5 -' P 3LXu 'x7P 462&#"6"3277>BM/ApC1~!Ȁm } 7k^>53#"&54747.1:6#1@,Ly6W37W?:X.kH$ /KsID78]'654#"'67632]1-(_D3,Qr-D64=EP<27# 7>77%)!5!+&7WL H[7P2'67654'"#"'732<.$    v XTP+# 0 - 6 7P9>32'>7#"54>7&#"#"'732632 LL6f* &0 O8, & Y " Y V$. $ go 5  ,  $   5 0 7Z>3267.#"7z$+)"h4WN,?98!!$&)M*g(P+7# 7>7'>7&(!5"*%%)!5!+&7XL G\WL H[7#, %>7'>7'>7y%)!5$($&(!5"*%%)!5!+&7T K QYXL G\WL H[7 2#"&546}!/*#-),".,! .n) 9`$&*-24679:;<=DIJMPQSUVWXYZ[\]  !"#$%&+-/389:;<=>?@2  ; Q Z [ \ ] d e f g  $$$$&$*$,$2$4$6$7$9$:$;$<$=$D$F$G$H$I$J$P$Q$R$S$T$U$V$W$X$Y$Z$\$]$m$}$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$ $!$"$#$$$%$&$+$-$/$1$3$8$9$:$;$<$=$>$?$@$ ;$ Q$ Z$ [$ \$ ]$ d$ e$ f$ g$ $ $ $ $ $ $ $$$$%%%%$%&%*%,%2%4%6%7%9%:%;%<%=%D%I%J%M%P%Q%R%S%U%V%W%X%Y%Z%[%\%]%m%}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%% %!%"%#%$%%%&%+%-%/%1%3%8%9%:%;%<%=%>%?%@% ;% Q% Z% [% \% ]% d% e% f% g% % % % % % % %%%%&&&$&&&*&,&2&4&6&7&9&:&;&<&=&D&I&J&M&P&Q&R&S&U&V&W&X&Y&Z&[&\&]&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&& &!&"&#&$&%&&&+&-&/&1&3&8&9&:&;&<&=&>&?&@& ;& Q& Z& [& \& ]& d& e& f& g& & & & & & &&&&'''$'&'*','-'2'4'6'7'9':';'<'='D'F'G'H'I'J'M'P'Q'R'S'T'U'V'W'X'Y'Z'['\']'}''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '''''''''''' '!'"'#'$'%'&'+'-'/'1'3'8'9':';'<'='>'?'@' ;' Q' Z' [' \' ]' d' e' f' g' ' ' ' ' ' ''''($(&(*(,(-(2(4(6(7(9(:(;(<(=(D(F(G(H(I(J(M(P(Q(R(S(T(U(V(W(X(Y(Z([(\(]((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( (((((((((((( (!("(#($(%(&(+(-(/(1(3(8(9(:(;(<(=(>(?(@( ;( Q( Z( [( \( ]( d( e( f( g( ( (((())))$)&)*),)-)2)4)6)9):);)<)=)D)F)G)H)I)J)M)P)Q)R)S)T)U)V)W)X)Y)Z)[)\)])m)}))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) )))))))))))) )!)")#)%)+)-)/)1)3)8)9):);)<)=)>)?)@) ;) Q) Z) [) \) ]) e) g) ) ) ) ))))***$*&***,*2*4*6*7*9*:*;*<*=*D*I*J*M*P*Q*R*S*U*V*W*X*Y*Z*[*\*]*}*********************************************************** ************ *!*"*#*$*%*&*+*-*/*1*3*8*9*:*;*<*=*>*?*@* ;* Q* Z* [* \* ]* d* e* f* g* * * * * * ****,$,&,*,,,2,4,6,7,9,:,;,<,=,D,F,G,H,I,J,P,Q,R,S,T,U,V,W,X,Y,Z,[,\,],,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,, ,!,",#,$,%,&,+,-,/,1,3,8,9,:,;,<,=,>,?,@, ;, Q, Z, [, \, ], d, e, f, g, , ,,,,-$-&-*-,-2-4-6-9-:-;-<-=-D-F-G-H-J-M-P-Q-R-S-T-U-V-X-Y-Z-[-\-]----------------------------------------------------------------------- ------------ -!-"-#-+---/-1-3-8-9-:-;-<-=->-?-@- ;- Q- Z- [- \- ]- - ....&.*.,.2.4.6.D.F.G.H.I.J.M.P.Q.R.S.T.U.V.W.X.Y.Z.[.\.].m.}............................................................ ............ .!.".#.%.+.-./.1.3.9.<.>.@. ;. Q. Z. [. \. ]. e. g. . . . . . ....////&/*/,/2/4/6/7/9/:/</D/F/G/H/I/J/M/P/Q/R/T/U/V/W/X/Y/Z/\/]/m/}///////////////////////////////////////////////////////////// //////////// /!/"/#/$/%/&/+/-///1/3/8/9/:/</>/@/ ;/ Z/ [/ \/ ]/ d/ e/ f/ g/ / / / / / / ////222$2,2627292:2;2<2=2D2J2P2Q2S2U2X2]2}222222222222222222222222222222222222 222 2"2$2&2+2-2/2123282:2;2<2=2>2?2@2 ;2 Q2 Z2 \2 d2 f2 2 2 2 2 3333$3&3*3,3-32343637393:3;3<3=3D3F3G3H3J3P3Q3R3S3T3U3V3X3[3]3m3}333333333333333333333333333333333333333333333333333333333333333333333 333333333333 3!3"3#3$3&3+3-3/3133383:3;3<3=3>3?3@3 ;3 Q3 Z3 [3 \3 ]3 d3 f3 3 3 3 3 3 444$4,4647494:4;4<4=4D4J4P4Q4S4U4X4]4}444444444444444444444444444444444444 444 4"4$4&4+4-4/4143484:4;4<4=4>4?4@4 ;4 Q4 Z4 \4 d4 f4 4 4 4 4 5$5&5*5,52545657595:5;5<5=5D5F5G5H5J5M5P5Q5R5S5T5U5V5X5Y5Z5\5]55555555555555555555555555555555555555555555555555555555555555555555555 555555555555 5!5"5#5$5&5+5-5/515358595:5;5<5=5>5?5@5 ;5 Q5 Z5 [5 \5 ]5 d5 f5 5 6666$6&6*6,62646667696:6;6<6=6I6M6P6Q6R6S6U6W6X6Y6Z6[6\6]6m6}6666666666666666666666666666666666666666666666 66666666666 6"6$6%6&6+6-6/616368696:6;6<6=6>6?6@6 ;6 Q6 Z6 \6 d6 e6 f6 g6 6 6 6 6 6 6 66667777$7&7*7,7-727476797:7;7<7=7D7F7G7H7I7J7P7Q7R7S7T7U7V7W7X7Y7Z7[7\7]7m7}77777777777777777777777777777777777777777777777777777777777777777777777 777777777777 7!7"7#7%7+7-7/717378797:7;7<7=7>7?7@7 ;7 Q7 Z7 [7 \7 ]7 e7 g7 7 7 7 77779999$9&9*9,9-9294969;9=9D9F9G9H9I9J9P9Q9R9S9T9U9V9W9X9Y9Z9[9\9]9m9}9999999999999999999999999999999999999999999999999999999999999999999999 999999999999 9!9"9#9%9+9-9/9193999;9<9=9>9?9@9 ;9 Q9 Z9 [9 \9 ]9 e9 g9 9 9 9999::::$:&:*:,:-:2:4:6:;:=:D:F:G:H:I:J:P:Q:R:S:T:U:V:W:X:Y:Z:[:\:]:m:}:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::: :!:":#:%:+:-:/:1:3:9:;:<:=:>:?:@: ;: Q: Z: [: \: ]: e: g: : : ::::;;;;&;*;,;2;4;6;D;F;G;H;I;J;M;P;Q;R;S;T;U;V;W;X;Y;Z;[;\;];m;};;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;; ;!;";#;%;+;-;/;1;3;9;<;>;@; ;; Q; Z; [; \; ]; e; g; ; ; ; ; ; ;;;;<<<<$<&<*<,<-<2<4<6<;<=<D<F<G<H<I<J<P<Q<R<S<T<U<V<W<X<Y<Z<[<\<]<m<}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<< <!<"<#<%<+<-</<1<3<9<;<<<=<><?<@< ;< Q< Z< [< \< ]< e< g< < < <<<<====&=*=,=2=4=6=D=F=G=H=I=J=M=P=Q=R=S=T=U=V=W=X=Y=Z=[=\=]=m=}============================================================ ============ =!="=#=%=+=-=/=1=3=9=<=>=@= ;= Q= Z= [= \= ]= e= g= = = = = = ====DDDDDDFDGDIDJDPDQDRDTDUDVDWDXDYDZD[D\D]DmD}DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDD!D#D%D+D-D/D1D3D9D<D>D@D2D D D [D ]D eD gD D D D D D DDDDDEEEMEPEQEUEXEYEZE[E\E]EEEEEEEEEE EE+E-E/E1E3E9E<E>E@E2E E E E FFFPFQFUFXF[F]FFFFFFFF FF+F-F/F1F3F<F>F@F2F F F GDGHGJGPGQGSGUGVGXG]GGGGGGGGGGGGGGGGGGGGGGGGGGGG GGG!G#G+G-G/G1G3G<G>G@G2G QG [G ]HHHHDHIHJHMHPHQHSHUHVHWHXHYHZH[H\H]HmHHHHHHHHHHHHHHHHHHHHHHH HHH!H#H%H+H-H/H1H3H9H<H>H@H2H H QH [H ]H eH gH H H H H HHHHHIIIIDIFIGIHIJIPIQIRITIUIVIXI[I]ImI}IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII IIIIIII!I#I+I-I/I1I3I<I>I@I2I I [I ]I I JJJJFJGJHJRJTJmJ}JJJJJJJJJJJJJJJJJJJJJJJ J J J J J KKKKFKGKHKMKPKQKRKSKTKUKVKXKYKZK[K\K]KmK}KKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKK!K#K+K-K/K1K3K9K<K>K@K2K K QK [K ]K K K K K K NNNNDNFNGNHNJNPNQNRNSNTNUNVNXNYNZN\N]NmN}NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN NNNNNNN!N#N+N-N/N1N3N9N<N>N@N2N N QN [N ]N N N N N N PPPPFPGPHPMPPPQPRPSPTPUPVPXPYPZP[P\P]PmP}PPPPPPPPPPPPPPPPPPPPPPPPPPPP PPPPPPP!P#P+P-P/P1P3P9P<P>P@P2P P QP [P ]P P P P P P QQQQFQGQHQMQPQQQRQSQTQUQVQXQYQZQ[Q\Q]QmQ}QQQQQQQQQQQQQQQQQQQQQQQQQQQQ QQQQQQQ!Q#Q+Q-Q/Q1Q3Q9Q<Q>Q@Q2Q Q QQ [Q ]Q Q Q Q Q Q RRRRDRIRJRMRPRQRSRURVRWRXRYRZR[R\R]RmRRRRRRRRRRRRRRRRRRRRRRR RRR!R#R%R+R-R/R1R3R9R<R>R@R2R R QR [R ]R eR gR R R R R RRRRRSSSMSPSQSUSXSYSZS[S\S]SSSSSSSSSS SS+S-S/S1S3S9S<S>S@S2S S S S UUUUDUFUGUHUJUPUQURUTUVUXU[U]UmUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUU!U#U+U-U/U1U3U<U>U@U2U U [U ]U VVVVDVFVIVJVMVPVQVRVSVUVVVWVXVYVZV[V\V]VmV}VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV VVVVVVV!V#V%V+V-V/V1V3V9V<V>V@V2V V QV [V ]V eV gV V V V V V VVVVVWDWFWGWHWIWJWPWQWRWTWUWVWWW[W]W}WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW WWWWWWW!W#W%W<W>W@W2W W W [W ]W eW gW WWWWWXXXXFXGXHXRXTXmX}XXXXXXXXXXXXXXXXXXXXXXX X X X X X YYYYDYFYGYHYIYJYMYPYQYRYSYTYUYVYWYXY[Y]YmY}YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY YYYYYYY!Y#Y%Y+Y-Y/Y1Y3Y<Y>Y@Y2Y Y Y QY [Y ]Y eY gY Y Y Y Y YYYYYZZZZDZFZGZHZIZJZMZPZQZRZSZTZUZVZWZXZ[Z]ZmZ}ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZ!Z#Z%Z+Z-Z/Z1Z3Z<Z>Z@Z2Z Z Z QZ [Z ]Z eZ gZ Z Z Z Z ZZZZZ[[[[D[F[G[H[J[P[Q[R[S[T[U[V[X[Y[Z[\[][m[}[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[![#[+[-[/[1[3[9[<[>[@[2[ [ Q[ [[ ][ [ [ [ [ [ \\\\D\F\G\H\I\J\M\P\Q\R\S\T\U\V\W\X\[\]\m\}\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \\\\\\\!\#\%\+\-\/\1\3\<\>\@\2\ \ \ Q\ [\ ]\ e\ g\ \ \ \ \ \\\\\]]]]D]F]G]H]I]J]M]P]Q]R]S]T]U]V]W]X]Y]Z][]\]]]m]}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] ]]]]]]]!]#]%]+]-]/]1]3]9]<]>]@]2] ] ] Q] [] ]] e] g] ] ] ] ] ] ]]]]]m$m&m*m2m4m6m7m9m:m;m<m=mDmFmGmHmImJmMmPmQmRmSmTmUmWmXmYmZm[m\m]mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmmmmmmmmmm m"m$m%m&m+m-m/m3m8m9m:m;m<m=m>m?m@m2m m ;m Qm Zm \m dm em fm gm m mmmmm}$}&}*}-}2}4}6}7}9}:};}<}=}D}I}J}M}P}Q}S}U}V}W}X}Y}Z}[}\}]}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} }}}}}}}} }!}"}#}$}%}&}+}-}/}3}8}9}:};}<}=}>}?}@}2} } ;} Q} Z} [} \} ]} d} e} f} g} } }}}}}&*,24679:;<=DFGHIJPQRSTUVWXYZ\]m}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g  &*,24679:;<=DFGHIJPQRSTUVWXYZ\]m}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g  &*,24679:;<=DFGHIJPQRSTUVWXYZ\]m}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g  &*,24679:;<=DFGHIJPQRSTUVWXYZ\]m}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g  &*,24679:;<=DFGHIJPQRSTUVWXYZ\]m}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g  &*,24679:;<=DFGHIJPQRSTUVWXYZ\]m}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g  $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,24679:;<=DIJMPQRSUVWXYZ[\]}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,24679:;<=DFGHIJPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,24679:;<=DFGHIJPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,24679:;<=DFGHIJPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,24679:;<=DFGHIJPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,24679:;<=DIJMPQRSUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $,679:;<=DJPQSUX]}  "$&+-/138:;<=>?@ ; Q Z \ d f $,679:;<=DJPQSUX]}  "$&+-/138:;<=>?@ ; Q Z \ d f $,679:;<=DJPQSUX]}  "$&+-/138:;<=>?@ ; Q Z \ d f $,679:;<=DJPQSUX]}  "$&+-/138:;<=>?@ ; Q Z \ d f $,679:;<=DJPQSUX]}  "$&+-/138:;<=>?@ ; Q Z \ d f $,679:;<=DJPQSUX]}  "$&+-/138:;<=>?@ ; Q Z \ d f $&*,-246;=DFGHIJPQRSTUVWXYZ[\]m}  !"#%+-/139;<=>?@ ; Q Z [ \ ] e g  $&*,24679:;<=DIJMPQRSUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g DFGIJPQRTUVWXYZ[\]m} !#%+-/139<>@2   [ ] e g  DFGIJPQRTUVWXYZ[\]m} !#%+-/139<>@2   [ ] e g  DFGIJPQRTUVWXYZ[\]m} !#%+-/139<>@2   [ ] e g  DFGIJPQRTUVWXYZ[\]m} !#%+-/139<>@2   [ ] e g  DFGIJPQRTUVWXYZ[\]m} !#%+-/139<>@2   [ ] e g  DFGIJPQRTUVWXYZ[\]m} !#%+-/139<>@2   [ ] e g  DIJMPQSUVWXYZ[\] !#%+-/139<>@2  Q [ ] e g PQUX[] +-/13<>@2 DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g FGHMPQRSTUVXYZ[\]m} !#+-/139<>@2  Q [ ]  DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g FGHRTm}   FGHRTm}   FGHRTm}   FGHRTm}   DFGHIJMPQRSTUVWX[]m} !#%+-/13<>@2   Q [ ] e g  DFGHIJMPQRSTUVWX[]m} !#%+-/13<>@2   Q [ ] e g  &*,24679:;<=DFGHIJPQRSTUVWXYZ\]m}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g  DFGIJPQRTUVWXYZ[\]m} !#%+-/139<>@2   [ ] e g  &*,24679:;<=DFGHIJPQRSTUVWXYZ\]m}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g  )H 7DFGIJPQRTUVWXYZ[\]m} !#%+-/139<>@2   [ ] e g  &*,24679:;<=DFGHIJPQRSTUVWXYZ\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g DFGIJPQRTUVWXYZ[\] !#%+-/139<>@2   [ ] e g $&*,24679:;<=DIJMPQRSUVWXYZ[\]}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g PQUX[] +-/13<>@2$&*,24679:;<=DIJMPQRSUVWXYZ[\]}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g PQUX[] +-/13<>@2 $&*,24679:;<=DIJMPQRSUVWXYZ[\]}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g PQUX[] +-/13<>@2$&*,24679:;<=DIJMPQRSUVWXYZ[\]}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g PQUX[] +-/13<>@2 $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g DIJMPQSUVWXYZ[\] !#%+-/139<>@2  Q [ ] e g $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g DIJMPQSUVWXYZ[\] !#%+-/139<>@2  Q [ ] e g $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g DIJMPQSUVWXYZ[\] !#%+-/139<>@2  Q [ ] e g $&*,24679:;<=DIJMPQRSUVWXYZ[\]}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g $&*,24679:;<=DIJMPQRSUVWXYZ[\]}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g FGHRTm}   $&*,24679:;<=DIJMPQRSUVWXYZ[\]}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g FGHRTm}   $&*,24679:;<=DIJMPQRSUVWXYZ[\]}  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g FGHRTm}   &*,246DFGHIJMPQRSTUVWXYZ[\]m}  !"#%+-/139<>@ ; Q Z [ \ ] e g  DFGHJPQRSTUVXYZ\]m} !#+-/139<>@2  Q [ ]  &*,24679:<DFGHIJMPQRTUVWXYZ\]m}  !"#$%&+-/1389:<>@ ; Z [ \ ] d e f g  &*,24679:<DFGHIJMPQRTUVWXYZ\]m}  !"#$%&+-/1389:<>@ ; Z [ \ ] d e f g  &*,24679:<DFGHIJMPQRTUVWXYZ\]m}  !"#$%&+-/1389:<>@ ; Z [ \ ] d e f g  &*,24679:<DFGHIJMPQRTUVWXYZ\]m}  !"#$%&+-/1389:<>@ ; Z [ \ ] d e f g       FGHMPQRSTUVXYZ[\]m} !#+-/139<>@2  Q [ ]      FGHMPQRSTUVXYZ[\]m} !#+-/139<>@2  Q [ ]          F G H M P Q R S T U V X Y Z [ \ ] m }                                    ! # + - / 1 3 9 < > @ 2  Q [ ]      $,679:;<=DJPQSUX]}  "$&+-/138:;<=>?@ ; Q Z \ d f     DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g     $,679:;<=DJPQSUX]}  "$&+-/138:;<=>?@ ; Q Z \ d f     DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g     $,679:;<=DJPQSUX]}  "$&+-/138:;<=>?@ ; Q Z \ d f     DIJMPQSUVWXYZ[\]m !#%+-/139<>@2  Q [ ] e g     $&*,-24679:;<=DFGHIJMPQRSTUVWXYZ[\]  !"#$%&+-/1389:;<=>?@ ; Q Z [ \ ] d e f g  DIJMPQSUVWXYZ[\] !#%+-/139<>@2  Q [ ] e g $&*,24679:;<=DFGHJMPQRSTUVXYZ\]  !"#$&+-/1389:;<=>?@ ; Q Z [ \ ] d f  DFGHJPQRTVX[]m !#+-/13<>@2  [ ] $&*,24679:;<=IMPQRSUWXYZ[\]m}  "$%&+-/1389:;<=>?@ ; Q Z \ d e f g       DFIJMPQRSUVWXYZ[\]m} !#%+-/139<>@2  Q [ ] e g          $ & * , 2 4 6 7 9 : ; < = I M P Q R S U W X Y Z [ \ ] m }                                                            " $ % & + - / 1 3 8 9 : ; < = > ? @ ; Q Z \ d e f g           !!!!D!F!I!J!M!P!Q!R!S!U!V!W!X!Y!Z![!\!]!m!}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!#!%!+!-!/!1!3!9!<!>!@!2! ! Q! [! ]! e! g! ! ! ! ! ! !!!!!""""$"&"*","2"4"6"7"9":";"<"="I"M"P"Q"R"S"U"W"X"Y"Z"["\"]"m"}"""""""""""""""""""""""""""""""""""""""""""""" """"""""""" """$"%"&"+"-"/"1"3"8"9":";"<"=">"?"@" ;" Q" Z" \" d" e" f" g" " " " " " " """"####D#F#I#J#M#P#Q#R#S#U#V#W#X#Y#Z#[#\#]#m#}################################ #######!###%#+#-#/#1#3#9#<#>#@#2# # Q# [# ]# e# g# # # # # # #####$$$$$$&$*$,$-$2$4$6$9$:$;$<$=$D$F$G$H$I$J$P$Q$R$S$T$U$V$W$X$Y$Z$[$\$]$m$}$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$ $!$"$#$%$+$-$/$1$3$8$9$:$;$<$=$>$?$@$ ;$ Q$ Z$ [$ \$ ]$ e$ g$ $ $ $ $$$$%D%F%G%H%I%J%P%Q%R%T%U%V%W%[%]%}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%!%#%%%<%>%@%2% % % [% ]% e% g% %%%%%&$&&&*&,&-&2&4&6&9&:&;&<&=&D&F&G&H&I&J&P&Q&R&S&T&U&V&W&X&Y&Z&[&\&]&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&& &!&"&#&%&+&-&/&1&3&8&9&:&;&<&=&>&?&@& ;& Q& Z& [& \& ]& e& g& & &&&&+F+G+H+R+T+++++++++++++++++++++++ ----F-G-H-R-T-m-}----------------------- - - - - - ////F/G/H/R/T/m/}/////////////////////// / / / / / 1F1G1H1R1T11111111111111111111111 3333F3G3H3R3T3m3}33333333333333333333333 3 3 3 3 3 8888$8&8*8,8-8284868;8=8D8F8G8H8I8J8P8Q8R8S8T8U8V8W8X8Y8Z8[8\8]8m8}8888888888888888888888888888888888888888888888888888888888888888888888 888888888888 8!8"8#8%8+8-8/8183898;8<8=8>8?8@8 ;8 Q8 Z8 [8 \8 ]8 e8 g8 8 8 88889999D9F9G9H9I9J9M9P9Q9R9S9T9U9V9W9X9[9]9m9}999999999999999999999999999999999999999 9999999!9#9%9+9-9/91939<9>9@929 9 9 Q9 [9 ]9 e9 g9 9 9 9 9 99999::::$:&:*:,:-:2:4:6:;:=:D:F:G:H:I:J:P:Q:R:S:T:U:V:W:X:Y:Z:[:\:]:m:}:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::: :!:":#:%:+:-:/:1:3:9:;:<:=:>:?:@: ;: Q: Z: [: \: ]: e: g: : : ::::;;;;m;}; ; ; ; ; <<<<D<F<G<H<I<J<M<P<Q<R<S<T<U<V<W<X<Y<Z<[<\<]<m<}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<!<#<%<+<-</<1<3<9<<<><@<2< < < Q< [< ]< e< g< < < < < < <<<<<====m=}= = = = = >>>>D>F>G>H>I>J>M>P>Q>R>S>T>U>V>W>X>Y>Z>[>\>]>m>}>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>!>#>%>+>->/>1>3>9><>>>@>2> > > Q> [> ]> e> g> > > > > > >>>>>????&?*?,?2?4?6?D?F?G?H?I?J?M?P?Q?R?S?T?U?V?W?X?Y?Z?[?\?]?m?}???????????????????????????????????????????????????????????? ???????????? ?!?"?#?%?+?-?/?1?3?9?<?>?@? ;? Q? Z? [? \? ]? e? g? ? ? ? ? ? ????@@@@D@F@G@H@I@J@M@P@Q@R@S@T@U@V@W@X@Y@Z@[@\@]@m@}@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@!@#@%@+@-@/@1@3@9@<@>@@@2@ @ @ Q@ [@ ]@ e@ g@ @ @ @ @ @ @@@@@y$y&y*y,y2y4y6y7y9y:y;y<y=yDyIyJyMyPyQyRySyUyVyWyXyYyZy[y\y]yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy yyyyyyyyyyyy y!y"y#y$y%y&y+y-y/y1y3y8y9y:y;y<y=y>y?y@y ;y Qy Zy [y \y ]y dy ey fy gy y yyyy$&*,-24679:;<=DFGHJPQRSTUVX[]m}  !"#$&+-/138:;<=>?@ ; Q Z [ \ ] d f      2222m2}2 2 2 2 2 jkjwj~jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkksks~sssssssssssssssssssssssssssssssssssssssssssssssssssssukuwu~uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuzkzwz~zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz~~k~kw~kw~kw~    $ & * , 2 4 6 7 9 : ; < = D I J M P Q R S U V W X Y Z [ \ ] m }                ! " # $ % & + - / 1 3 8 9 : ; < = > ? @ ; Q Z [ \ ] d e f g         $ & * , - 2 4 6 7 9 : ; < = D F G H I J M P Q R S T U V W X Y Z [ \ ]                                                                                    ! " # $ % & + - / 1 3 8 9 : ; < = > ? @  ;  Q  Z  [  \  ]  d  e  f  g       D H J P Q S U V X ]                               ! # + - / 1 3 < > @ 2  Q  [  ]    $ & * , - 2 4 6 9 : ; < = D F G H I J M P Q R S T U V W X Y Z [ \ ] m }                                                                                    ! " # % + - / 1 3 8 9 : ; < = > ? @  ;  Q  Z  [  \  ]  e  g              D F G H J P Q R T U V X [ ] m }                                              ! # + - / 1 3 < > @ 2    [  ]        F G H M P Q R S T U V X Y Z [ \ ] m }                                   ! # + - / 1 3 9 < > @ 2    Q  [  ]         ; ; ; ;F ;G ;H ;M ;P ;Q ;R ;S ;T ;U ;V ;X ;Y ;Z ;[ ;\ ;] ;m ;} ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;! ;# ;+ ;- ;/ ;1 ;3 ;9 ;< ;> ;@ ;2 ;  ; Q ; [ ; ] ; ; ; ; ;  ;  P P P P$ P& P* P, P- P2 P4 P6 P7 P9 P: P; P< P= PD PF PG PH PJ PP PQ PR PS PT PU PV PX P[ P] Pm P} P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P! P" P# P$ P& P+ P- P/ P1 P3 P8 P: P; P< P= P> P? P@ P ; P Q P Z P [ P \ P ] P d P f P P P P P  P  Q Q QM QP QQ QU QX QY QZ Q[ Q\ Q] Q Q Q Q Q Q Q Q Q Q Q Q+ Q- Q/ Q1 Q3 Q9 Q< Q> Q@ Q2 Q Q Q Q Z Z Z Z$ Z& Z* Z, Z2 Z4 Z6 Z7 Z9 Z: Z; Z< Z= ZI ZM ZP ZQ ZR ZS ZU ZW ZX ZY ZZ Z[ Z\ Z] Zm Z} Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z" Z$ Z% Z& Z+ Z- Z/ Z1 Z3 Z8 Z9 Z: Z; Z< Z= Z> Z? Z@ Z ; Z Q Z Z Z \ Z d Z e Z f Z g Z Z Z Z Z Z  Z  Z Z Z Z [ [ [ [D [F [I [J [M [P [Q [R [S [U [V [W [X [Y [Z [[ [\ [] [m [} [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [! [# [% [+ [- [/ [1 [3 [9 [< [> [@ [2 [  [ Q [ [ [ ] [ e [ g [ [ [ [ [  [  [ [ [ [ [ \ \ \ \$ \& \* \, \2 \4 \6 \7 \9 \: \; \< \= \I \M \P \Q \R \S \U \W \X \Y \Z \[ \\ \] \m \} \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \" \$ \% \& \+ \- \/ \1 \3 \8 \9 \: \; \< \= \> \? \@ \ ; \ Q \ Z \ \ \ d \ e \ f \ g \ \ \ \ \ \  \  \ \ \ \ ] ] ] ]D ]F ]I ]J ]M ]P ]Q ]R ]S ]U ]V ]W ]X ]Y ]Z ][ ]\ ]] ]m ]} ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ]! ]# ]% ]+ ]- ]/ ]1 ]3 ]9 ]< ]> ]@ ]2 ]  ] Q ] [ ] ] ] e ] g ] ] ] ] ]  ]  ] ] ] ] ] d d d d$ d& d* d, d- d2 d4 d6 d9 d: d; d< d= dD dF dG dH dI dJ dP dQ dR dS dT dU dV dW dX dY dZ d[ d\ d] dm d} d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d! d" d# d% d+ d- d/ d1 d3 d8 d9 d: d; d< d= d> d? d@ d ; d Q d Z d [ d \ d ] d e d g d d d  d  d d d d eD eF eG eH eI eJ eP eQ eR eT eU eV eW e[ e] e} e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e! e# e% e< e> e@ e2 e  e  e [ e ] e e e g e  e e e e e f f f f$ f& f* f, f- f2 f4 f6 f9 f: f; f< f= fD fF fG fH fI fJ fP fQ fR fS fT fU fV fW fX fY fZ f[ f\ f] fm f} f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f! f" f# f% f+ f- f/ f1 f3 f8 f9 f: f; f< f= f> f? f@ f ; f Q f Z f [ f \ f ] f e f g f f f  f  f f f f gD gF gG gH gI gJ gP gQ gR gT gU gV gW g[ g] g} g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g! g# g% g< g> g@ g2 g  g  g [ g ] g e g g g  g g g g g $ & * , - 2 4 6 7 9 : ; < = D F G H I J M P Q R S T U V W X Y Z [ \ ]                ! " # $ % & + - / 1 3 8 9 : ; < = > ? @ ; Q Z [ \ ] d e f g        $ & * , - 2 4 6 ; = D F G H I J P Q R S T U V W X Y Z [ \ ] m }                ! " # % + - / 1 3 9 ; < = > ? @ ; Q Z [ \ ] e g          D F G H I J M P Q R S T U V W X [ ] m }          ! # % + - / 1 3 < > @ 2   Q [ ] e g        $ & * - 2 4 6 ; = D F G H J P Q R S T U V X ]                ! " # + - / 3 ; < = > ? @ 2 ; Q Z [ \ ] $ & * 2 4 6 9 : < D F G H J P Q R S T U V X Y Z [ \ ]                ! " # + - / 3 8 9 : < > @ 2 ; Q Z [ \ ] & * 2 4 6 7 9 : ; < = D F G H I J P Q R S T U V W X Y Z [ \ ]                ! " # $ % & + - / 3 8 9 : ; < = > ? @ 2  ; Q Z [ \ ] d e f g      $ & * - 2 4 6 ; = D F G H J P Q R S T U V X ]                ! " # + - / 3 ; < = > ? @ 2 ; Q Z [ \ ] & * 2 4 6 7 9 : ; < = D F G H I J P Q R S T U V W X Y Z [ \ ]                ! " # $ % & + - / 3 8 9 : ; < = > ? @ 2  ; Q Z [ \ ] d e f g      $ & * 2 4 6 7 9 : ; < = D F G H I J M P Q R S T U W X Y Z [ \ ]                                                                          " $ % & + - / 3 8 9 : ; < = > ? @ 2    ;  Q  Z  \  d  e  f  g        $ & * - 2 4 6 7 9 : ; < = D I J M P Q S U V W X Y Z [ \ ]                                                      ! " # $ % & + - / 3 8 9 : ; < = > ? @ 2    ;  Q  Z  [  \  ]  d  e  f  g       DFGHJPQRTUVX[]m} !#+-/13<>@2  [ ]  0FE%N   +e  -$E   t     J t 6   V   H      f H     !T" r$& :'$*j-Copyleft 2002, 2003, 2005, 2008, 2009, 2010 Free Software Foundation.Copyleft 2002, 2003, 2005, 2008, 2009, 2010 Free Software Foundation.FreeSansFreeSansMediumMediumFontForge 2.0 : Free Sans : 19-9-2010FontForge 2.0 : Free Sans : 19-9-2010Free SansFree SansVersion $Revision: 1.344 $ Version $Revision: 1.344 $ FreeSansFreeSansGNUGNUhttps://savannah.gnu.org/projects/freefont/https://savannah.gnu.org/projects/freefont/This computer font is part of GNU FreeFont. It 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/>. As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.This computer font is part of GNU FreeFont. It 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 . As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.http://www.gnu.org/copyleft/gpl.htmlhttp://www.gnu.org/copyleft/gpl.htmlva?@O<V9NormalNormalNormalNavadnoNormlne1KG=K9NormalNormalOdmiana ZwykBaNormalnormalusisvidjsMediomenengahNormlMittelNormalNormaaliGemiddeldnormaloby ejn=>@<0;5=ArruntaP2  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~        !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ softhyphenmicromiddotAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflexTcedillatcedillaTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018F Epsilonlatinuni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019EObarOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6Yoghuni01B8uni01B9uni01BAuni01BBuni01BEwynnuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFicaronuni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5Gcarongcaronuni01E8uni01E9Oogonekoogonekuni01ECuni01EDuni01EEuni01EFuni01F0uni01F1uni01F2uni01F3uni01F4uni01F5Wynnuni01F8uni01F9 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208 idblgraveuni020Aiinvertedbreveuni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0224uni0225 A_dotaccent a_dotaccentuni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233dotlessjaturnascript ascriptturnbhookcturnccurldtaildhookerevschwa schwahook epsilonlatinepsilonlatinrevepsilonlatinrevhookepsilonlatinrevclosed jdotlessbarghookgscript Gsmallcap gammalatinramshornhturnhhookhenghookibar iotalatin Ismallcap lmidtildelbeltlrthooklezhmturnedmlonglegturnedmhook nhookleftnrthook Nsmallcapobar OEsmallcap omegaclosedphilatinrturnrlonglegturned rhookturnedrlonglegrhook rfishhook rfishhookrev Rsmallcap Rsmallcapinvshookeshdotlessjstrokehook eshshortreveshcurltturntrthookubar upsilonlatinvhookvturnwturnyturn Ysmallcapzrthookzcurlezhezhcurl glottalstopglottalstoprevinvglottalstopinvcstretch bilabialclick Bsmallcap epsilonclosed Gsmallhook Hsmallcap jcrosstailkturn Lsmallcapqhookglottalstopbarglottalstopbarrevdzaltonedezhdzcurltsteshtccurluni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFhsuper hhooksuperjsuperrsuper rturnsuperrturnrthooksuper Rturnsuperwsuperysuperprimemod dblprimemod quoteleftmod apostrophe apostropherev ringhalfright ringhalfleftglottalstopmodglottalstopreversedmodfrontedbackedraisedloweredverticallinemodmacronmodifier acutemodifier gravemodifierverticallinelowmod macronlowmodgravesubacutesubcolontriangularmodcolontriangularhalfmodringhalfrightcenteredringhalfleftcentered tackupmid tackdownmidplusmodminusmoduni02DEuni02DF gammasuperlsuperssuperxsuperglottalrevsuper toneextrahightonehightonemidtonelow toneextralowuni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F3uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF gravecomb acutecomb circumflexcmb tildecomb macroncmb overlinecmbbrevecmb dotaccentcmb diaeresiscomb hookabovecombringcmbhungarumlautcmbcaroncmbverticallineabovecmbdblverticallineabovecmb gravedblnospcandrabinducmbbreveinvertedcmbcommaturnedabovecmb psilicomb dasiacombcommaaboverightcmb gravebelowcmb acutebelowcmblefttackbelowcmbuni0319uni031Ahorncmbringlefthalfsubnospuptackbelowcmbuni031Euni031F minusbelowcmbhookpalatalizedbelowcmbhookretroflexbelowcmb dotbelowcombdieresisbelowcmb ringbelowcmb commasubnosp cedillacmb ogonekcmblinevertsubnospbridgebelowcmbdblarchinvertedbelowcmb caronbelowcmbcircumflexbelowcmb brevebelowcmbbreveinvertedbelowcmb tildebelowcmbmacronbelowcmb lowlinecmb dbllowlinecmbtildeoverlaycmbstrokeshortoverlaycmbstrokelongoverlaycmbsolidusshortoverlaycmbsoliduslongoverlaycmbringrighthalfsubnospbridgeinvertedbelowcmbsquarebelowcmbseagullbelowcmb xabovecmbtildeverticalcmbdbloverlinecmbuni0340uni0341uni0342uni0343diaeresistonosnospuni0345uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni034Funi0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035Auni035Buni035Cuni035Duni035Euni035Funi0360uni0361uni0362uni0363uni0364uni0365uni0366uni0367uni0368uni0369uni036Auni036Buni036Cuni036Duni036Euni036Funi0374uni0375 ypogegrammeniuni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonostheta1 UpsilonhookUpsilonhooktonosUpsilonhookdiaeresisphi1 pisymbolgreekuni03D7uni03F0rhosymbolgreekuni03f4uni03F5Iecyrillic_grave Iocyrillic Djecyrillic Gjecyrillic Ecyrillic Dzecyrillic Icyrillic Yicyrillic Jecyrillic Ljecyrillic Njecyrillic Tshecyrillic KjecyrillicIicyrillic_graveUshortcyrillic Dzhecyrillic Acyrillic Becyrillic Vecyrillic Gecyrillic Decyrillic Iecyrillic Zhecyrillic Zecyrillic IicyrillicIishortcyrillic Kacyrillic Elcyrillic Emcyrillic Encyrillic Ocyrillic Pecyrillic Ercyrillic Escyrillic Tecyrillic Ucyrillic Efcyrillic Khacyrillic Tsecyrillic Checyrillic Shacyrillic ShchacyrillicHardsigncyrillic YericyrillicSoftsigncyrillicEreversedcyrillic IUcyrillic IAcyrillic acyrillic becyrillic vecyrillic gecyrillic decyrillic iecyrillic zhecyrillic zecyrillic iicyrilliciishortcyrillic kacyrillic elcyrillic emcyrillic encyrillic ocyrillic pecyrillic ercyrillic escyrillic tecyrillic ucyrillic efcyrillic khacyrillic tsecyrillic checyrillic shacyrillic shchacyrillichardsigncyrillic yericyrillicsoftsigncyrillicereversedcyrillic iucyrillic iacyrilliciecyrillic_grave iocyrillic djecyrillic gjecyrillic ecyrillic dzecyrillic icyrillic yicyrillic jecyrillic ljecyrillic njecyrillic tshecyrillic kjecyrilliciicyrillic_graveushortcyrillic dzhecyrillic Omegacyrillic omegacyrillicuni0470uni0471 Fitacyrillic fitacyrillicOmegatitlocyrillicomegatitlocyrillic Otcyrillic otcyrillicthousandcyrillictitlocyrilliccmbpalatalizationcyrilliccmbdasiapneumatacyrilliccmbpsilipneumatacyrilliccmbuni0487uni0488uni0489uni048auni048buni048Cuni048Duni048Euni048Funi0490uni0491uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04d9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFDzeabkhasiancyrillicuni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04f6uni04f7uni04F8uni04F9uni0510uni0511uni0512uni0513uni051auni051buni051cuni051duni051euni051funi0531uni0532uni0533uni0534uni0535uni0536uni0537uni0538uni0539uni053Auni053Buni053Cuni053Duni053Euni053Funi0540uni0541uni0542uni0543uni0544uni0545uni0546uni0547uni0548uni0549uni054Auni054Buni054Cuni054Duni054Euni054Funi0550uni0551uni0552uni0553uni0554uni0555uni0556uni0559uni055Auni055Buni055Cuni055Duni055Euni055Funi0561uni0562uni0563uni0564 echarmenianuni0566uni0567uni0568uni0569uni056A iniarmenianuni056C xeharmenianuni056Euni056Funi0570uni0571uni0572uni0573 menarmenianuni0575 nowarmenianuni0577uni0578uni0579uni057Auni057Buni057Cuni057D vewarmenianuni057Funi0580uni0581 yiwnarmenianuni0583uni0584uni0585uni0586echyiwnarmenianuni0589uni058A afii57799 afii57801 afii57800 afii57802 hiriqhebrew tserehebrew segolhebrew patahhebrew qamatshebrew holamhebrew afii57796 dageshhebrew afii57839 afii57645 rafehebrew afii57842 shindothebrew sindothebrewsofpasuqhebrewupperdothebrew alefhebrew bethebrew gimelhebrew dalethebrewhehebrew vavhebrew zayinhebrew hethebrew tethebrew yodhebrewfinalkafhebrew kafhebrew lamedhebrewfinalmemhebrew memhebrewfinalnunhebrew nunhebrew samekhhebrew ayinhebrew finalpehebrewpehebrew tsadifinal tsadihebrew qofhebrew reshhebrew shinhebrew tavhebrew vavvavhebrew vavyodhebrew yodyodhebrew gereshhebrewgershayimhebrewuni0700uni0701uni0702uni0703uni0704uni0705uni0706uni0707uni0708uni0709uni070Auni070Buni070Cuni070Duni070Funi0710uni0711uni0712uni0713uni0714uni0715uni0716uni0717uni0718uni0719uni071Auni071Buni071Cuni071Duni071Euni071Funi0720uni0721uni0722uni0723uni0724uni0725uni0726uni0727uni0728uni0729uni072Auni072Buni072Cuni0730uni0731uni0732uni0733uni0734uni0735uni0736uni0737uni0738uni0739uni073Auni073Buni073Cuni073Duni073Euni073Funi0740uni0741uni0742uni0743uni0744uni0745uni0746uni0747uni0748uni0749uni074Auni0900candrabindudeva anusvaradeva visargadevauni0904adevaaadevaidevaiidevaudevauudeva rvocalicdeva lvocalicdeva ecandradeva eshortdevaedevaaideva ocandradeva oshortdevaodevaaudevakadevakhadevagadevaghadevangadevacadevachadevajadevajhadevanyadevattadevatthadevaddadevaddhadevannadevatadevathadevadadevadhadevanadevannnadevapadevaphadevabadevabhadevamadevayadevaradevarradevaladevalladevallladevavadevashadevassadevasadevahadeva nuktadeva avagrahadevaaavowelsigndevaivowelsigndevaiivowelsigndevauvowelsigndevauuvowelsigndevarvocalicvowelsigndevarrvocalicvowelsigndevaecandravowelsigndevaeshortvowelsigndevaevowelsigndevaaivowelsigndevaocandravowelsigndevaoshortvowelsigndevaovowelsigndevaauvowelsigndeva viramadevauni094eomdeva udattadeva anudattadeva gravedeva acutedevauni0955qadevakhhadevaghhadevazadeva dddhadevarhadevafadevayyadeva rrvocalicdeva llvocalicdevalvocalicvowelsigndevauni0963uni0964dbldandazerodevaonedevatwodevauni0969uni096Auni096Bsixdevauni096D eightdevaninedevaabbreviationsigndevauni0971uni0972uni0979uni097auni097buni097cuni097duni097euni097fbn_candrabindu bn_anusvara bn_visargabn_abn_aabn_ibn_iibn_ubn_uubn_ribn_libn_ebn_aibn_obn_aubn_kabn_khabn_gabn_ghabn_ngabn_cabn_chabn_jabn_jhabn_nyabn_ttabn_tthabn_ddabn_ddhabn_nnabn_tabn_thabn_dabn_dhabn_nabn_pabn_phabn_babn_bhabn_mabn_yabn_rabn_labn_shabn_ssabn_sabn_habn_nukta bn_avagraha bn_aakaarbn_ikaar bn_iikaarbn_ukaar bn_uukaar bn_rikaar bn_rrikaarbn_ekaar bn_aikaarbn_okaar bn_aukaar bn_hasanta bn_half_ta bn_aumarkbn_rrabn_rhabn_yyabn_rribn_lli bn_likaar bn_llikaarbn_zerobn_onebn_twobn_threebn_fourbn_fivebn_sixbn_sevenbn_eightbn_nine bn_asamira bn_asamiba bn_rupeemark bn_rupeesign bn_currency1 bn_currency2 bn_currency3 bn_currency4bn_currencyless bn_currency16 bn_issharuni0A01 bindigurmukhiuni0A03 agurmukhi aagurmukhi igurmukhi iigurmukhi ugurmukhi uugurmukhi eegurmukhi aigurmukhi oogurmukhi augurmukhi kagurmukhi khagurmukhi gagurmukhi ghagurmukhi ngagurmukhi cagurmukhi chagurmukhi jagurmukhi jhagurmukhi nyagurmukhi ttagurmukhi tthagurmukhi ddagurmukhi ddhagurmukhi nnagurmukhi tagurmukhi thagurmukhi dagurmukhi dhagurmukhi nagurmukhi pagurmukhi phagurmukhi bagurmukhi bhagurmukhi magurmukhi yagurmukhi ragurmukhi lagurmukhiuni0A33 vagurmukhi shagurmukhi sagurmukhi hagurmukhi nuktagurmukhiaamatragurmukhiimatragurmukhiiimatragurmukhiumatragurmukhiuumatragurmukhieematragurmukhiaimatragurmukhioomatragurmukhiaumatragurmukhihalantgurmukhiuni0a51 khhagurmukhi ghhagurmukhi zagurmukhi rragurmukhi fagurmukhi zerogurmukhi onegurmukhi twogurmukhi threegurmukhi fourgurmukhi fivegurmukhi sixgurmukhi sevengurmukhi eightgurmukhi ninegurmukhi tippigurmukhi addakgurmukhi irigurmukhi uragurmukhiekonkargurmukhiuni0a75candrabindugujaratianusvaragujarativisargagujarati agujarati aagujarati igujarati iigujarati ugujarati uugujaratirvocalicgujaratiuni0a8cecandragujarati egujarati aigujaratiocandragujarati ogujarati augujarati kagujarati khagujarati gagujarati ghagujarati ngagujarati cagujarati chagujarati jagujarati jhagujarati nyagujarati ttagujarati tthagujarati ddagujarati ddhagujarati nnagujarati tagujarati thagujarati dagujarati dhagujarati nagujarati pagujarati phagujarati bagujarati bhagujarati magujarati yagujarati ragujarati lagujarati llagujarati vagujarati shagujarati ssagujarati sagujarati hagujarati nuktagujaratiuni0ABDaavowelsigngujaratiivowelsigngujaratiiivowelsigngujaratiuvowelsigngujaratiuuvowelsigngujaratiuni0AC3 guj_rrvocalicecandravowelsigngujaratievowelsigngujaratiaivowelsigngujaratiocandravowelsigngujaratiovowelsigngujaratiauvowelsigngujarativiramagujarati omgujaratirrvocalicgujaratiuni0ae1uni0ae2uni0ae3 zerogujarati onegujarati twogujarati threegujarati fourgujarati fivegujarati sixgujarati sevengujarati eightgujarati ninegujaratiuni0AF1uni10A0uni10A1uni10A2uni10A3uni10A4uni10A5uni10A6uni10A7uni10A8uni10A9uni10AAuni10ABuni10ACuni10ADuni10AEuni10AFuni10B0uni10B1uni10B2uni10B3uni10B4uni10B5uni10B6uni10B7uni10B8uni10B9uni10BAuni10BBuni10BCuni10BDuni10BEuni10BFuni10C0uni10D0uni10D1uni10D2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10f9uni10fbuni10fcuni13A0uni13A1uni13A2uni13A3uni13A4uni13A5uni13A6uni13A7uni13A8uni13A9uni13AAuni13ABuni13ACuni13ADuni13AEuni13AFuni13B0uni13B1uni13B2uni13B3uni13B4uni13B5uni13B6uni13B7uni13B8uni13B9uni13BAuni13BBuni13BCuni13BDuni13BEuni13BFuni13C0uni13C1uni13C2uni13C3uni13C4uni13C5uni13C6uni13C7uni13C8uni13C9uni13CAuni13CBuni13CCuni13CDuni13CEuni13CFuni13D0uni13D1uni13D2uni13D3uni13D4uni13D5uni13D6uni13D7uni13D8uni13D9uni13DAuni13DBuni13DCuni13DDuni13DEuni13DFuni13E0uni13E1uni13E2uni13E3uni13E4uni13E5uni13E6uni13E7uni13E8uni13E9uni13EAuni13EBuni13ECuni13EDuni13EEuni13EFuni13F0uni13F1uni13F2uni13F3uni13F4uni1401uni1402uni1403uni1404uni1405uni1406uni1407uni1408uni1409uni140Auni140Buni140Cuni140Duni140Euni140Funi1410uni1411uni1412uni1413uni1414uni1415uni1416uni1417uni1418uni1419uni141Auni141Buni141Cuni141Duni141Euni141Funi1420uni1421uni1422uni1423uni1424uni1425uni1426uni1427uni1428uni1429uni142Auni142Buni142Cuni142Duni142Euni142Funi1430uni1431uni1432uni1433uni1434uni1435uni1436uni1437uni1438uni1439uni143Auni143Buni143Cuni143Duni143Euni143Funi1440uni1441uni1442uni1443uni1444uni1445uni1446uni1447uni1448uni1449uni144Auni144Buni144Cuni144Duni144Euni144Funi1450uni1451uni1452uni1453uni1454uni1455uni1456uni1457uni1458uni1459uni145Auni145Buni145Cuni145Duni145Euni145Funi1460uni1461uni1462uni1463uni1464uni1465uni1466uni1467uni1468uni1469uni146Auni146Buni146Cuni146Duni146Euni146Funi1470uni1471uni1472uni1473uni1474uni1475uni1476uni1477uni1478uni1479uni147Auni147Buni147Cuni147Duni147Euni147Funi1480uni1481uni1482uni1483uni1484uni1485uni1486uni1487uni1488uni1489uni148Auni148Buni148Cuni148Duni148Euni148Funi1490uni1491uni1492uni1493uni1494uni1495uni1496uni1497uni1498uni1499uni149Auni149Buni149Cuni149Duni149Euni149Funi14A0uni14A1uni14A2uni14A3uni14A4uni14A5uni14A6uni14A7uni14A8uni14A9uni14AAuni14ABuni14ACuni14ADuni14AEuni14AFuni14B0uni14B1uni14B2uni14B3uni14B4uni14B5uni14B6uni14B7uni14B8uni14B9uni14BAuni14BBuni14BCuni14BDuni14BEuni14BFuni14C0uni14C1uni14C2uni14C3uni14C4uni14C5uni14C6uni14C7uni14C8uni14C9uni14CAuni14CBuni14CCuni14CDuni14CEuni14CFuni14D0uni14D1uni14D2uni14D3uni14D4uni14D5uni14D6uni14D7uni14D8uni14D9uni14DAuni14DBuni14DCuni14DDuni14DEuni14DFuni14E0uni14E1uni14E2uni14E3uni14E4uni14E5uni14E6uni14E7uni14E8uni14E9uni14EAuni14EBuni14ECuni14EDuni14EEuni14EFuni14F0uni14F1uni14F2uni14F3uni14F4uni14F5uni14F6uni14F7uni14F8uni14F9uni14FAuni14FBuni14FCuni14FDuni14FEuni14FFuni1500uni1501uni1502uni1503uni1504uni1505uni1506uni1507uni1508uni1509uni150Auni150Buni150Cuni150Duni150Euni150Funi1510uni1511uni1512uni1513uni1514uni1515uni1516uni1517uni1518uni1519uni151Auni151Buni151Cuni151Duni151Euni151Funi1520uni1521uni1522uni1523uni1524uni1525uni1526uni1527uni1528uni1529uni152Auni152Buni152Cuni152Duni152Euni152Funi1530uni1531uni1532uni1533uni1534uni1535uni1536uni1537uni1538uni1539uni153Auni153Buni153Cuni153Duni153Euni153Funi1540uni1541uni1542uni1543uni1544uni1545uni1546uni1547uni1548uni1549uni154Auni154Buni154Cuni154Duni154Euni154Funi1550uni1551uni1552uni1553uni1554uni1555uni1556uni1557uni1558uni1559uni155Auni155Buni155Cuni155Duni155Euni155Funi1560uni1561uni1562uni1563uni1564uni1565uni1566uni1567uni1568uni1569uni156Auni156Buni156Cuni156Duni156Euni156Funi1570uni1571uni1572uni1573uni1574uni1575uni1576uni1577uni1578uni1579uni157Auni157Buni157Cuni157Duni157Euni157Funi1580uni1581uni1582uni1583uni1584uni1585uni1586uni1587uni1588uni1589uni158Auni158Buni158Cuni158Duni158Euni158Funi1590uni1591uni1592uni1593uni1594uni1595uni1596uni1597uni1598uni1599uni159Auni159Buni159Cuni159Duni159Euni159Funi15A0uni15A1uni15A2uni15A3uni15A4uni15A5uni15A6uni15A7uni15A8uni15A9uni15AAuni15ABuni15ACuni15ADuni15AEuni15AFuni15B0uni15B1uni15B2uni15B3uni15B4uni15B5uni15B6uni15B7uni15B8uni15B9uni15BAuni15BBuni15BCuni15BDuni15BEuni15BFuni15C0uni15C1uni15C2uni15C3uni15C4uni15C5uni15C6uni15C7uni15C8uni15C9uni15CAuni15CBuni15CCuni15CDuni15CEuni15CFuni15D0uni15D1uni15D2uni15D3uni15D4uni15D5uni15D6uni15D7uni15D8uni15D9uni15DAuni15DBuni15DCuni15DDuni15DEuni15DFuni15E0uni15E1uni15E2uni15E3uni15E4uni15E5uni15E6uni15E7uni15E8uni15E9uni15EAuni15EBuni15ECuni15EDuni15EEuni15EFuni15F0uni15F1uni15F2uni15F3uni15F4uni15F5uni15F6uni15F7uni15F8uni15F9uni15FAuni15FBuni15FCuni15FDuni15FEuni15FFuni1600uni1601uni1602uni1603uni1604uni1605uni1606uni1607uni1608uni1609uni160Auni160Buni160Cuni160Duni160Euni160Funi1610uni1611uni1612uni1613uni1614uni1615uni1616uni1617uni1618uni1619uni161Auni161Buni161Cuni161Duni161Euni161Funi1620uni1621uni1622uni1623uni1624uni1625uni1626uni1627uni1628uni1629uni162Auni162Buni162Cuni162Duni162Euni162Funi1630uni1631uni1632uni1633uni1634uni1635uni1636uni1637uni1638uni1639uni163Auni163Buni163Cuni163Duni163Euni163Funi1640uni1641uni1642uni1643uni1644uni1645uni1646uni1647uni1648uni1649uni164Auni164Buni164Cuni164Duni164Euni164Funi1650uni1651uni1652uni1653uni1654uni1655uni1656uni1657uni1658uni1659uni165Auni165Buni165Cuni165Duni165Euni165Funi1660uni1661uni1662uni1663uni1664uni1665uni1666uni1667uni1668uni1669uni166Auni166Buni166Cuni166Duni166Euni166Funi1670uni1671uni1672uni1673uni1674uni1675uni1676uni1677uni1678uni1679uni167auni167buni167cuni167duni167euni167funi18b0uni18b1uni18b2uni18b3uni18b4uni18b5uni18b6uni18b7uni18b8uni18b9uni18bauni18bbuni18bcuni18bduni18beuni18bfuni18c0uni18c1uni18c2uni18c3uni18c4uni18c5uni18c6uni18c7uni18c8uni18c9uni18cauni18cbuni18ccuni18cduni18ceuni18cfuni18d0uni18d1uni18d2uni18d3uni18d4uni18d5uni18d6uni18d7uni18d8uni18d9uni18dauni18dbuni18dcuni18dduni18deuni18dfuni18e0uni18e1uni18e2uni18e3uni18e4uni18e5uni18e6uni18e7uni18e8uni18e9uni18eauni18ebuni18ecuni18eduni18eeuni18efuni18f0uni18f1uni18f2uni18f3uni18f4uni18f5uni1E00uni1E01 Bdotaccent bdotaccentuni1E04uni1E05uni1E06uni1E07uni1E08uni1E09 Ddotaccent ddotaccentuni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1D Fdotaccent fdotaccentuni1E20uni1E21 Hdotaccent hdotaccentuni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Eidieresisacuteuni1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3F Mdotaccent mdotaccentuni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55 Pdotaccent pdotaccent Rdotaccent rdotaccentuni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5F Sdotaccent sdotaccent Sdotbelow sdotbelowuni1E64uni1E65uni1E66uni1E67uni1E68uni1E69 Tdotaccent tdotaccent Tdotbelow tdotbelowuni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8D Ydotaccent ydotaccentuni1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1e9auni1E9B Adotbelow adotbelow Ahookabove ahookaboveAcircumflexacuteacircumflexacuteAcircumflexgraveacircumflexgraveAcircumflexhookaboveacircumflexhookaboveAcircumflextildeacircumflextildeAcircumflexdotbelowacircumflexdotbelow Abreveacute abreveacute Abrevegrave abrevegraveAbrevehookaboveabrevehookabove Abrevetilde abrevetildeAbrevedotbelowabrevedotbelow Edotbelow edotbelow Ehookabove ehookaboveEtildeetildeEcircumflexacuteecircumflexacuteEcircumflexgraveecircumflexgraveEcircumflexhookaboveecircumflexhookaboveEcircumflextildeecircumflextildeEcircumflexdotbelowecircumflexdotbelow Ihookabove ihookabove Idotbelow idotbelow Odotbelow odotbelow Ohookabove ohookaboveOcircumflexacuteocircumflexacuteOcircumflexgraveocircumflexgraveOcircumflexhookaboveocircumflexhookaboveOcircumflextildeocircumflextildeOcircumflexdotbelowocircumflexdotbelow Ohornacute ohornacute Ohorngrave ohorngraveOhornhookaboveohornhookabove Ohorntilde ohorntilde Ohorndotbelow ohorndotbelow Udotbelow udotbelow Uhookabove uhookaboveuni1EE8 uhornacute Uhorngrave uhorngraveUhornhookaboveuhornhookabove Uhorntilde uhorntilde Uhorndotbelow uhorndotbelowYgraveygrave Ydotbelow ydotbelow Yhookabove yhookaboveYtildeytildeuni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBClenisprosgegrammenipsili perispomenidialytikaperispomeniuni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCC psilivaria psilioxiapsiliperispomeniuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDB dasiavaria dasiaoxiadasiaperispomeniuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECdialytikavaria dialytikaoxiavariauni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCoxiadasiaenquademquadenspaceemspacethreeperemspacefourperemspace sixperemspace figurespacepunctuationspace thinspace hairspacezerowidthspacezerowidthnonjoinerzerojoinuni200Euni200F hyphentwo hyphennobreak figuredash quotedashdblverticalbar underscoredbl quotereversed quotedblrevtrianglebulletonedotenleadertwodotenleader hyphendot lineseparatorparagraphseparatorlrerlepdflrorlouni202Fpertenthousandprimesecond primetripleprimerev primedblrevprimetriplerevcaret referencemark exclamdbl interrobangoverlineuni203Ftie caretinsertasterism hyphenbulletuni2045uni2046uni2047uni2048uni2049uni204Auni204Buni204Cuni204Duni204Euni204Fclosureuni2051uni2052uni2053uni2054uni2055uni2056uni2057uni2058uni2059uni205Auni205Buni205Cuni205Duni205Euni205Funi2060uni2061uni2062uni2063uni2064 zerosuperioruni2071 foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperior plussuperior minussuperior equalsuperiorparenleftsuperiorparenrightsuperior nsuperior zeroinferior oneinferior twoinferior threeinferior fourinferior fiveinferior sixinferior seveninferior eightinferior nineinferior plusinferioruni208B equalinferiorparenleftinferiorparenrightinferioruni2090uni2091uni2092uni2093uni2094 colonmonetarycruzeiroliramilluni20A6pesetauni20A8won afii57636dongEurouni20ADuni20AEuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20b8leftharpoonaccentrightharpoonaccentuni20D2uni20D3uni20d4uni20d5uni20D6uni20D7uni20d8uni20d9uni20dauni20DBuni20DCuni20DDuni20DEuni20DFuni20E0uni20E1uni20e4uni20E5uni20E6uni20E7uni20E8uni20E9uni20EAuni20EBuni20ECuni20EDuni20EEuni20EFuni20F0 accountofaddresssubjectCbb centigradeCLcareofcadaunaEulerscruple fahrenheitHscript HblackletterHbbplanck planckover2piIscriptIfrakturLscriptlitrelbbarNbbnumero recordright weierstrassPbbQbbRscriptRfrakturRbb prescriptionresponse servicemark telephoneversicleZbbOhmmho Zblackletteriotaturn degreekelvinangstromBscript Cblackletter estimatedescriptEscriptFscriptFturnMscript alephmathbethmath gimelmath dalethmathuni2139uni213auni213buni213cuni213euni213funi2141uni2142uni2143uni2144uni214auni214buni214duni214eonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215Funi2160uni2161uni2162uni2163uni2164uni2165uni2166uni2167uni2168uni2169uni216Auni216Buni216Cuni216Duni216Euni216Funi2170uni2171uni2172uni2173uni2174uni2175uni2176uni2177uni2178uni2179uni217Auni217Buni217Cuni217Duni217Euni217F arrowleftarrowup arrowright arrowdown arrowboth arrowupdn arrowupleft arrowuprightarrowdownright arrowdownleft arrowbarright arrowhookleftarrowhookrightcarriagereturnharpoonleftbarbuparrowleftbothalfharpoonupright harpoonupleftharpoonrightbarbuparrowrightbothalfharpoondownrightharpoondownleftharpoonrightleft arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblboth arrowdblbothvarrowsquiggleright universal existentialemptyset Delta.mathgradientelement notelementuni220Asuchthat notcontains ownersmalluni2210uni2213dotplus slashmath backslashmath asteriskmathuni2219 proportionalangledividesnotbarparallel notparallel logicaland logicalor intersectionunionuni222Cuni222Duni222E thereforesimilar reversedtildelazysinv wreathproduct notsimilaruni2242 asymptequalnotasymptequal congruentapproxnotequalnotapproxequaluni2249equivasymptotic approaches equivalence notidenticalmuchless muchgreaternotequivasymptoticnotless notgreater notlessequalnotgreaterequaluni2272uni2273precedesfollows precedesequal followsequal notprecedes notsucceedssubsetsuperset notsubset notsuperset reflexsubsetreflexsuperset notsubseteqlnotsuperseteqluni228C squareimagesquareoriginal subsetsqequalsupersetsqequalintersectionsqunionsq circleplusuni2296circlemultiply circledivide circledot turnstilelefttacklefttackdown perpendicular assertion truestate triangleright trianglelefttriangleftequaltriangrightequalnarylogicaland narylogicalornaryintersection naryuniondotmathuni22C6bowtiepreceedsnotequalfollowsnotequalnotsubsetsqequalnotsupersetsqequaluni2300 ceilingleft ceilingright floorleft floorright integraltp integralbtfrown slurbelow angleleft anglerightuni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23AEuni2400uni2401uni2402uni2403uni2404uni2405uni2406uni2407uni2408uni2409uni240auni240buni240cuni240duni240euni240funi2410uni2411uni2412uni2413uni2414uni2415uni2416uni2417uni2418uni2419uni241auni241buni241cuni241duni241euni241funi2420uni2421blankbblankuni2424uni2460uni2461uni2462uni2463uni2464uni2465uni2466uni2467uni2468uni2469SF100000uni2501SF110000uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250BSF010000uni250Duni250Euni250FSF030000uni2511uni2512uni2513SF020000uni2515uni2516uni2517SF040000uni2519uni251Auni251BSF080000uni251Duni251Euni251Funi2520uni2521uni2522uni2523SF090000uni2525uni2526uni2527uni2528uni2529uni252Auni252BSF060000uni252Duni252Euni252Funi2530uni2531uni2532uni2533SF070000uni2535uni2536uni2537uni2538uni2539uni253Auni253BSF050000uni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254BSF430000SF240000SF510000SF520000SF390000SF220000SF210000SF250000SF500000SF490000SF380000SF280000SF270000SF260000SF360000SF370000SF420000SF190000SF200000SF230000SF470000SF480000SF410000SF450000SF460000SF400000SF540000SF530000SF440000upblockuni2581uni2582uni2583dnblockuni2585uni2586uni2587blockuni2589uni258Auni258Blfblockuni258Duni258Euni258Frtblockltshadeshadedkshadeuni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259F filledboxsquare boxrounded boxnestedsquarehorizontalfillsquareverticalfillsquareorthogonalcrosshatchfillboxleftdiaghatchboxrtdiaghatchboxcrossdiaghatchsquaresmallsolidwhitesmallsquare filledrect rectanglefilledvertrect vertrectanglefilledparallelogram parallelogram trianglesolidtrianglesmalltrianglesld smalltriangleblackrightpointingtrianglewhiterightpointingtrianglesmalltrianglerightsldsmalltrianglerightblackrightpointingpointer triagrtopentriangledownsld triangleinvsmalltriangleinvsldsmalltriangleinvblackleftpointingtrianglewhiteleftpointingtrianglesmalltriangleleftsldsmalltriangleleftblackleftpointingpointer triaglfopen diamondsolid diamondrhombdiamondrhombnestedfisheyecircle dottedcirclecircleverthatchbullseye circlesolid circleleftsldcirclerightsldcirclebottomsld circletopsld circlenesld circlenwopensemicircleleftsldsemicirclelertsld bulletinversewhitecircleinverseinvsemicircleupinvsemicircledn nwquadarc nequadarc sequadarc swquadarctoparc bottomarc trianglesesld triangleswsld tranglenwsld trianglenesld whitebullet squareleftsldsquarerightsld squarenwsld squaresesldsquarevertbisect triangledottriangleleftsldtrianglerightsld largecircleuni25f0uni25f1uni25f2uni25f3uni25f4uni25f5uni25f6uni25f7uni25f8uni25f9uni25fauni25fbuni25fcuni25fduni25feuni25ffuni262Cspade heartopen diamondopenclubspadesuitwhiteheartdiamond clubsuitwhiteuni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi2740uni2A00uni2A01uni2A02uni2A03uni2A04uni2A09uni2D30uni2D31uni2D32uni2D33uni2D34uni2D35uni2D36uni2D37uni2D38uni2D39uni2D3Auni2D3Buni2D3Cuni2D3Duni2D3Euni2D3Funi2D40uni2D41uni2D42uni2D43uni2D44uni2D45uni2D46uni2D47uni2D48uni2D49uni2D4Auni2D4Buni2D4Cuni2D4Duni2D4Euni2D4Funi2D50uni2D51uni2D52uni2D53uni2D54uni2D55uni2D56uni2D57uni2D58uni2D59uni2D5Auni2D5Buni2D5Cuni2D5Duni2D5Euni2D5Funi2D60uni2D61uni2D62uni2D63uni2D64uni2D65uni2D6Funi2E17uniA500uniA501uniA502uniA503uniA504uniA505uniA506uniA507uniA508uniA509uniA50AuniA50BuniA50CuniA50DuniA50EuniA50FuniA510uniA511uniA512uniA513uniA514uniA515uniA516uniA517uniA518uniA519uniA51AuniA51BuniA51CuniA51DuniA51EuniA51FuniA520uniA521uniA522uniA523uniA524uniA525uniA526uniA527uniA528uniA529uniA52AuniA52BuniA52CuniA52DuniA52EuniA52FuniA530uniA531uniA532uniA533uniA534uniA535uniA536uniA537uniA538uniA539uniA53AuniA53BuniA53CuniA53DuniA53EuniA53FuniA540uniA541uniA542uniA543uniA544uniA545uniA546uniA547uniA548uniA549uniA54AuniA54BuniA54CuniA54DuniA54EuniA54FuniA550uniA551uniA552uniA553uniA554uniA555uniA556uniA557uniA558uniA559uniA55AuniA55BuniA55CuniA55DuniA55EuniA55FuniA560uniA561uniA562uniA563uniA564uniA565uniA566uniA567uniA568uniA569uniA56AuniA56BuniA56CuniA56DuniA56EuniA56FuniA570uniA571uniA572uniA573uniA574uniA575uniA576uniA577uniA578uniA579uniA57AuniA57BuniA57CuniA57DuniA57EuniA57FuniA580uniA581uniA582uniA583uniA584uniA585uniA586uniA587uniA588uniA589uniA58AuniA58BuniA58CuniA58DuniA58EuniA58FuniA590uniA591uniA592uniA593uniA594uniA595uniA596uniA597uniA598uniA599uniA59AuniA59BuniA59CuniA59DuniA59EuniA59FuniA5A0uniA5A1uniA5A2uniA5A3uniA5A4uniA5A5uniA5A6uniA5A7uniA5A8uniA5A9uniA5AAuniA5ABuniA5ACuniA5ADuniA5AEuniA5AFuniA5B0uniA5B1uniA5B2uniA5B3uniA5B4uniA5B5uniA5B6uniA5B7uniA5B8uniA5B9uniA5BAuniA5BBuniA5BCuniA5BDuniA5BEuniA5BFuniA5C0uniA5C1uniA5C2uniA5C3uniA5C4uniA5C5uniA5C6uniA5C7uniA5C8uniA5C9uniA5CAuniA5CBuniA5CCuniA5CDuniA5CEuniA5CFuniA5D0uniA5D1uniA5D2uniA5D3uniA5D4uniA5D5uniA5D6uniA5D7uniA5D8uniA5D9uniA5DAuniA5DBuniA5DCuniA5DDuniA5DEuniA5DFuniA5E0uniA5E1uniA5E2uniA5E3uniA5E4uniA5E5uniA5E6uniA5E7uniA5E8uniA5E9uniA5EAuniA5EBuniA5ECuniA5EDuniA5EEuniA5EFuniA5F0uniA5F1uniA5F2uniA5F3uniA5F4uniA5F5uniA5F6uniA5F7uniA5F8uniA5F9uniA5FAuniA5FBuniA5FCuniA5FDuniA5FEuniA5FFuniA600uniA601uniA602uniA603uniA604uniA605uniA606uniA607uniA608uniA609uniA60AuniA60BuniA60CuniA60DuniA60EuniA60FuniA610uniA611uniA612uniA613uniA614uniA615uniA616uniA617uniA618uniA619uniA61AuniA61BuniA61CuniA61DuniA61EuniA61FuniA620uniA621uniA622uniA623uniA624uniA625uniA626uniA627uniA628uniA629uniA62AuniA62B lowcircumflex colonmodifier shortequalsSaltillosaltillocresc_cyrillic dot_cyrillictitlo_cyrillicyi_yi_lig_ukrainian ucas.ringucas.horizlineucas.shortvertucas.righttack ucas.lefttack NameMe.E968 NameMe.E969 NameMe.96A bn_initekaar bn_initaikaarbn_reph bn_kaphala bn_nnaphala bn_taphala bn_thaphala bn_thaphala1 bn_dhaphala bn_naphala bn_below_ba bn_bhaphala bn_raphala bn_laphala bn_half_ka bn_half_kha bn_half_ga bn_half_gha bn_half_nga bn_half_ca bn_half_ca1 bn_half_cha bn_half_ja bn_half_jha bn_half_nya bn_half_tta bn_half_ttha bn_half_dda bn_half_ddha bn_half_nna bn_half_tha bn_half_dha bn_half_da bn_half_na bn_half_pa bn_half_pha bn_half_ba bn_half_bha bn_half_ma bn_half_ya bn_half_ra bn_half_la bn_half_sha bn_half_ssa bn_half_sa bn_half_ha bn_half_rra bn_half_rha bn_half_yyabn_half_asamirabn_half_asamiba bn_khaphala bn_phaphala bn_baphala1 bn_maphala bn_maphala1 bn_yaphalabn_k_rabn_k_ra1bn_kh_rabn_g_rabn_gh_rabn_c_rabn_ch_rabn_j_rabn_tt_ra bn_tth_rabn_dd_ra bn_ddh_rabn_t_rabn_t_ra1bn_th_rabn_d_rabn_dh_rabn_n_rabn_n_ra1bn_p_rabn_ph_rabn_b_rabn_bh_ra bn_bh_ra1bn_m_rabn_y_rabn_sh_rabn_ss_rabn_s_rabn_s_ra1bn_h_ra bn_asamir_ra bn_asamib_ra bn_k_ss_rabn_k_kabn_k_tta bn_k_tt_rabn_k_tabn_k_ta1 bn_k_t_ba bn_k_t_ba1 bn_k_t_ra bn_k_t_ra1 bn_k_t_ra2bn_k_nabn_k_mabn_k_labn_k_ssa bn_k_ss_nna bn_k_ss_mabn_k_sabn_g_gabn_g_dabn_g_dha bn_g_dh_babn_g_labn_g_nabn_g_mabn_gh_nabn_ng_ka bn_ng_k_ra bn_ng_k_ssa bn_ng_k_ss_ra bn_ng_khabn_ng_ga bn_ng_gha bn_ng_gh_rabn_ng_ma bn_ng_ma1bn_c_cabn_c_cha bn_c_ch_ba bn_c_ch_rabn_c_nyabn_c_nabn_j_ja bn_j_j_babn_j_jhabn_j_nyabn_ny_ca bn_ny_chabn_ny_ja bn_ny_jha bn_tt_tta bn_tt_tt_rabn_tt_mabn_dd_ga bn_dd_ddabn_dd_ma bn_nn_tta bn_nn_tt_ra bn_nn_ttha bn_nn_dda bn_nn_dda1 bn_nn_dd_ra bn_nn_dd_ra1 bn_nn_ddha bn_nn_nnabn_nn_mabn_t_ta bn_t_t_babn_t_thabn_t_nabn_t_mabn_t_ma1bn_t_labn_d_gabn_d_ghabn_d_da bn_d_d_ba bn_d_d_rabn_d_dha bn_d_dh_babn_d_nabn_d_bha bn_d_bh_ra bn_d_bh_ra1bn_d_mabn_dh_nabn_dh_mabn_n_tta bn_n_tt_ra bn_n_tthabn_n_dda bn_n_dd_ra bn_n_ddhabn_n_ta bn_n_t_ba bn_n_t_ra bn_n_t_ra1bn_n_thabn_n_da bn_n_d_ba bn_n_d_rabn_n_dha bn_n_dh_ba bn_n_dh_rabn_n_nabn_n_mabn_n_ma1bn_n_sabn_p_ttabn_p_tabn_p_pabn_p_mabn_p_nabn_p_labn_p_sa bn_ph_ttabn_ph_labn_b_jabn_b_da bn_b_d_rabn_b_dhabn_b_nabn_b_bhabn_b_labn_bh_labn_m_tabn_m_thabn_m_dabn_m_nabn_m_na1bn_m_pa bn_m_p_ra bn_m_p_labn_m_pha bn_m_ph_rabn_m_bha bn_m_bh_ra bn_m_bh_ra1bn_m_mabn_m_ma1bn_m_labn_m_la1bn_m_sa bn_m_s_rabn_l_kabn_l_gabn_l_tta bn_l_tt_rabn_l_dda bn_l_dd_rabn_l_tabn_l_dabn_l_dhabn_l_pabn_l_pha bn_l_ph_rabn_l_mabn_l_labn_sh_ca bn_sh_chabn_sh_tabn_sh_nabn_sh_mabn_sh_labn_ss_ka bn_ss_k_ra bn_ss_k_ra1 bn_ss_tta bn_ss_tta1 bn_ss_tt_ra bn_ss_tt_ra1 bn_ss_ttha bn_ss_nnabn_ss_pa bn_ss_p_ra bn_ss_pha bn_ss_ph_rabn_ss_ma bn_ss_ma1bn_s_ka bn_s_k_ra bn_s_k_ra1bn_s_khabn_s_tta bn_s_tta1 bn_s_tt_ra bn_s_tt_ra1bn_s_ta bn_s_t_ba bn_s_t_ra bn_s_t_ra1bn_s_thabn_s_nabn_s_na1bn_s_pa bn_s_p_ra bn_s_p_labn_s_pha bn_s_ph_rabn_s_mabn_s_ma1bn_s_labn_s_la1bn_h_nnabn_h_nabn_h_mabn_h_labn_h_la1bn_rr_gabn_k_babn_g_babn_gh_babn_c_babn_ch_babn_j_babn_tt_babn_dd_babn_nn_babn_t_babn_th_babn_d_babn_dh_ba bn_dh_ba1bn_n_babn_n_ba1bn_p_babn_b_babn_bh_babn_m_ba bn_m_b_rabn_m_ba1 bn_m_b_ra1bn_l_babn_sh_babn_s_babn_s_ba1bn_h_babn_h_ba1 bn_kh_r_ukaarbn_kh_r_uukaar bn_g_ukaar bn_g_r_ukaar bn_g_r_uukaar bn_g_l_ukaar bn_g_l_uukaar bn_j_r_ukaar bn_j_r_uukaar bn_t_r_ukaar bn_t_r_uukaar bn_th_r_ukaarbn_th_r_uukaar bn_d_ukaar bn_d_r_ukaar bn_d_r_uukaar bn_dh_r_ukaarbn_dh_r_uukaar bn_n_ukaar bn_n_uukaar bn_n_rikaar bn_n_t_ukaarbn_n_d_r_ukaarbn_n_d_r_uukaar bn_p_r_ukaar bn_p_r_uukaar bn_p_l_ukaar bn_p_l_uukaar bn_b_r_ukaar bn_b_r_uukaar bn_b_l_ukaar bn_b_l_uukaar bn_bh_r_ukaarbn_bh_r_uukaar bn_m_r_ukaar bn_m_r_uukaarbn_m_p_r_ukaarbn_m_p_r_uukaar bn_r_ukaar bn_r_uukaar bn_l_g_ukaar bn_sh_ukaar bn_sh_r_ukaarbn_sh_r_uukaar bn_sh_l_ukaarbn_sh_l_uukaarbn_ss_p_r_ukaarbn_ss_p_r_uukaar bn_s_ukaar bn_s_uukaar bn_s_rikaar bn_s_t_ukaar bn_s_r_ukaar bn_s_r_uukaarbn_s_p_r_ukaarbn_s_p_r_uukaarbn_s_p_l_ukaarbn_s_p_l_uukaar bn_s_l_ukaar bn_s_l_uukaar bn_h_ukaar bn_h_rikaarbn_asamir_ukaarbn_asamir_uukaarbn_asamib_ukaarbn_asamib_uukaarbn_asamib_r_ukaarbn_asamib_r_uukaar bn_k_hasanta bn_kh_hasanta bn_g_hasanta bn_gh_hasanta bn_ng_hasanta bn_c_hasanta bn_ch_hasanta bn_j_hasanta bn_jh_hasanta bn_ny_hasanta bn_tt_hasantabn_tth_hasanta bn_dd_hasantabn_ddh_hasanta bn_nn_hasanta bn_t_hasanta bn_th_hasanta bn_d_hasanta bn_dh_hasanta bn_n_hasanta bn_p_hasanta bn_ph_hasanta bn_b_hasanta bn_bh_hasanta bn_m_hasanta bn_y_hasanta bn_r_hasanta bn_l_hasanta bn_sh_hasanta bn_ss_hasanta bn_s_hasanta bn_h_hasanta bn_rr_hasanta bn_rh_hasanta bn_yy_hasantabn_asamir_hasantabn_asamib_hasanta bn_post_k_raglyph569glyph570glyph571glyph572glyph57487 bn_baphala bn_below_ba2glyph578glyph579glyph580glyph581glyph582glyph583 bn_sh_ra.001bn_yaphala.002bn_d_yabn_n_ya bn_la.001bn_sh_yabn_ss_yabn_s_yaglyph593bn_h_yaglyph595 bn_ss_tta.002glyph597glyph598 bn_uukaar.1glyph600Adeva002Adeva003 deva_tta_nuktdeva_ttha_nukta.nuktglyph240glyph241deva_ii_anusvara.abvsdeva_e_candra_bindu.abvsdeva_o_cand_bindu.abvsdeva_au_ra_virama_candra_bindudeva_ai_ra_virama_candra_bindudeva_ra_virama_candra_binduhadeva_viramadeva.halnchadeva_virama.nuktchadeva_viramadeva.halnhadeva_rrvoc_ic_deva.blwskadeva_viramadeva_ssadeva.akhnjadeva_viramadeva_nyadeva.akhnuni924_94D.half_924.presuni924_930_94D.blwf.vatuuni936_94D.half_91A.presuni936_930_94D.blwf.vatuuni936_94D.half_935.presuni95B_930_94D.blwf.vatuuni92B_930_94D.blwf.vatuuni95E_930_94D.blwf.vatuuni92A_930_94D.blwf.vatuuni938_930_94D.blwf.vatudeva_ra_u.blwsdeva_ra_uu.blwsuni915_94D.half_928.preskhadeva_094D.half_0928.presghadeva_094D.half_0928.presglyph269deva_ka_virama.halfdeva_kha_virama.halfdeva_ga_virama.halfdeva_gha_virama.halfdeva_ca_virama.halfglyph275deva_ja_virama.halfdeva_jha_virama.halfdeva_nya_virama.halftadeva_094D.half_0928.presthadeva_094D.half_0928.presdadeva_094D.half_0928.presdeva_dha_094D.half_0928.presdeva_nna_virama.halfdeva_ta_virama.halfdeva_tha_virama.halfuni926_94D.hlf2_926_94D.presdeva_dha_virama.halfdeva_na_virama.halfglyph289deva_pa_virama.halfdeva_pha_virama.halfdeva_ba_virama.halfdeva_bha_virama.halfdeva_ma_virama.halfdeva_ya_virama.halfpadeva_094D.half_0928.presglyph297deva_la_virama.halfdeva_lla_virama.halfdeva_llla_virama.halfdeva_va_virama.halfdeva_sha_virama.halfdeva_ssa_virama.halfdeva_sa_virama.halfdeva_ha_virama.halfglyph306badeva_094D.half_0928.presglyph308madeva_094D.half_0928.presvadeva_094D.half_0928.presshadeva_094D.half_0928.preskadeva_094D.half_0915.presngadeva_094D.half_0915.presngadeva_094D.half_0916.presngadeva_094D.half_0917.presngadeva_094D.half_0918.presnyadeva_094D.half_091C.presdadeva_094D.half_0918.presdadeva_094D.half_0926.presdadeva_094D.half_0927.presdadeva_094D.half_092C.presdadeva_094D.half_092D.presdadeva_094D.half_092E.presdadeva_094D.half_092F.presdadeva_094D.half_0935.presttadeva_094D.half_091F.presttadeva_094D.half_0920.prestthadeva_094D.half_0920.presglyph329glyph330glyph331hadeva_094D.half_092E.preshadeva_094D.half_092F.presladeva_094D.half_0939.presglyph335u9_38_4D.hlf_24_4D_30prespresglyph337ra_viramadeva_afii301.halfu91A_94D.hlf2_91A_94D.halfsadeva_0928_094D.half.presglyph341glyph342glyph343glyph344glyph345glyph346glyph347glyph348glyph349uni915_94D_937.akhn_94D.halfu91C_94D_91E.akhn_94D.halfglyph352u924_94D.half_930_94D.blwf.vatuglyph354glyph355glyph356glyph357glyph358glyph359uni939_930_94D.blwf.blwsdeva_ra_virama.rphfdeva_ra_virama.blwfdeva_aa_candrabindu.abvsdeva_aa_anusvara.abvsglyph365glyph366glyph367glyph368glyph369deva_e_anusvara.abvsglyph371glyph372deva_ai_anusvara.abvsglyph374glyph375glyph376glyph377glyph378u9_38_4Dhalf1F_30_4Dblwfvtu2glyph380nadeva_094D.half_0928.presnga_viramadeva.halftta_viramadeva.halfttha_viramadeva.halfdda_viramadeva.halfddha_viramadeva.halfda_viramadeva.halfuni915_930_94D.blwf.vatuuni916_930_94D.blwf.presuni917_930_94D.blwf.presuni918_930_94D.blwf.vatuuni919_930_94D.blwf.vatuuni91A_930_94D.blwf.vatuuni91B_930_94D.blwf.vatuuni91C_930_94D.blwf.vatuuni91D_930_94D.blwf.vatuuni91E_930_94D.blwf.vatuuni91F_930_94D.blwf.vatuuni920_930_94D.blwf.vatuuni921_930_94D.blwf.vatuglyph402uni923_930_94D.blwf.vatutadeva_viramadeva_radeva.presuni925_930_94D.blwf.vatuuni926_930_94D.blwf.vatuuni927_930_94D.blwf.vatuuni928_930_94D.blwf.vatuuni92C_930_94D.blwf.vatuuni92D_930_94D.blwf.vatuuni92E_930_94D.blwf.vatuuni92F_930_94D.blwf.vatuuni930_930_94D.blwf.vatuuni932_930_94D.blwf.vatuuni936_94D.hlf2_932_94D.presuni937_94D.half_91F.presuni937_94D.half_920.presglyph418glyph419glyph420uni936_94D.hlf2_928_94D.presu938_4D.hlff24_4D.30_4Dbfvtuprsu926_94D.half_92E_94D.half.presuni924_94D.hlf2_928_94D.presuni939_94D.half_923.presuni939_94D.half_932.presuni939_94D.half_935.presu92A_94D.half_924_94D.half.presuni939_94D.half_928.presu939_94D.half_92E_94D.half.presu92A_94D.half_930_94D.blwf.vatungadeva_nuktadeva.nukt hadeva_nukt dadeva_nuktttadeva_nukt.halnuni920_93C.nukt_94D.halndddhadeva_viramadeva.halnrhadeva_viramadeva.halndadeva_nukt_viramauni939_93C.nukt_94D.halnuni919_93C.nukt_94D.halnu937_94D.half_91F.pres_94D.halnu937_94D.half_920.pres_94D.halnu939_94D.half_923.pres_94D.halnu939_94D.half_928.pres_94D.halnu939_94D.half_932.pres_94D.halnu939_94D.half_935.pres_94D.halnphadev_nuktadev_vir_dev.nuktjadev_nuktadev_vir_dev.nuktkagujarati_viramagujarati.halfkhagujarati_viramagujarati.halfgagujarati_viramagujarati.halfghagujarati_viramagujarati.halfcagujarati_viramagujarati.halfjagujarati_viramagujarati.halfjhagujarati_viramagujarati.halfnyagujarati_viramagujarati.halfnnagujarati_viramagujarati.halftagujarati_viramagujarati.halfthagujarati_viramagujarati.halfdhagujarati_viramagujarati.halfnagujarati_viramagujarati.halfpagujarati_viramagujarati.halfphagujarati_viramagujarati.halfbagujarati_viramagujarati.halfbhagujarati_viramagujarati.halfmagujarati_viramagujarati.halfyagujarati_viramagujarati.halflagujarati_viramagujarati.halfllagujarati_viramagujarati.halfvagujarati_viramagujarati.halfshagujarati_viramagujarati.halfssagujarati_viramagujarati.halfsagujarati_viramagujarati.halfhagujarati_viramagujarati.halfuniA95_ACD_AB7.akhn_ACD.halfuAA4_ACD.half_AA4_ACD.half.presuniAA4_ACD.half_AB0_ACD.vatuuAA8_ACD.half_AA8.pres_ACD.presuAB6_ACD.half_AB0_ACD.blwf.vatuuA96_ACD.half_AB0_ACD.blwf.vatuuA97_ACD.half_AB0_ACD.blwf.vatuuA98_ACD.half_AB0_ACD.blwf.vatuuA9A_ACD.half_AB0_ACD.blwf.vatuuA9C_ACD.half_AB0_ACD.blwf.vatuuAA5_ACD.half_AB0_ACD.blwf.vatuuAA7_ACD.half_AB0_ACD.blwf.vatuuAA8_ACD.half_AB0_ACD.blwf.vatuuAAA_ACD.half_AB0_ACD.blwf.vatuuAAC_ACD.half_AB0_ACD.blwf.vatuuAAD_ACD.half_AB0_ACD.blwf.vatuuAAE_ACD.half_AB0_ACD.blwf.vatuuAB8_ACD.half_AB0_ACD.blwf.vatuigujarati_anusvaragujarati.abvsguj_ii_anusvara.abvsugujarati_anusvaragujarati.abvsguj_uu_anusvara.abvsuniA95_ACD.half_A95.presuniA95_AB0_ACD.blwf.vatuguj_ja_aa.pstsguj_ja_ii.pstsuniA9C_AB0_ACD.blwf.vatuuniA9D_AB0_ACD.blwf.vatuguj_tta_virama_tta.presguj_tta_virama_ttha.presguj_ttha_virama_ttha.presguj_dda_virama_dda.presguj_dda_virama_ddha.presuniAA6_AB0_ACD.blwf.vatuguj_da_virama_ma.presguj_da_virama_da.presguj_da_virama_dha.presguj_da_virama_va.presuniAAB_AB0_ACD.blwf.vatu guj_ra_u.blwsguj_ra_uu.blwsnounicode_3_1_cduAB5_ACD.half_AB0_ACD.blwf.vatuuniAB6_ACD.half_AB5_ACD.presuniAB9_AB0_ACD.blwf.vatuuniAB9_ACD.half_AAE.presuniAB9_ACD.half_AAF.presuAB6_ACD.half_AA8.pres_ACD.presuniAB6_ACD.half_A9A_ACD.presguj_ddha_virama_ddha.presguj_ka_virama_ssa.akhnuniAA4_ACD.half_AA4.presuniAA4_AB0_ACD.blwf.vatuuniAA8_ACD.half_AA8.presuniAB6_AB0_ACD.blwf.vatuuniA96_AB0_ACD.blwf.vatuuniA97_AB0_ACD.blwf.vatuuniA98_AB0_ACD.blwf.vatuuniA9A_AB0_ACD.blwf.vatuuniAA5_AB0_ACD.blwf.vatuuniAA7_AB0_ACD.blwf.vatuuniAA8_AB0_ACD.blwf.vatuuniAAA_AB0_ACD.blwf.vatuuniAAC_AB0_ACD.blwf.vatuuniAAD_AB0_ACD.blwf.vatuuniAAE_AB0_ACD.blwf.vatuuniAB8_AB0_ACD.blwf.vatuuniAB5_AB0_ACD.blwf.vatuuniAB6_ACD.half_AB5.presuniAB6_ACD.half_AA8.presuniAB6_ACD.half_A9A.presguj_ra_virama.rphfguj_ra_virama.blwfuniAB7_ACD.half_AA0.presuniAB7_ACD.half_A9F.presuA_B7_CD.half_9F_B0_CD.blwfvatuuA_B7_CD.half_A0_B0_CDblwfvatuuniA9F_AB0_ACD.blwf.vatuguj_ja_virama_nya.akhnuniAA1_AB0_ACD.blwf.vatuuniAA0_AB0_ACD.blwf.vatuuniAA2_AB0_ACD.blwf.vatuglyph437glyph438glyph440glyph441guj_e_anusvara.abvsglyph443glyph444glyph445glyph446glyph447glyph448glyph449glyph450glyph451glyph452glyph453kagujarati_nuktagujarati.nuktkhagujarati_nuktagujarati.nuktgagujarati_nuktagujarati.nuktghagujarati_nuktagujarati.nuktngagujarati_nuktagujarati.nuktcagujarati_nuktagujarati.nuktchagujarati_nuktagujarati.nuktjagujarati_nuktagujarati.nuktjhagujarati_nuktagujarati.nuktnyagujarati_nuktagujarati.nuktttagujarati_nuktagujarati.nukttthagujarati_nuktagujarati.nuktddagujarati_nuktagujarati.nuktddhagujarati_nuktagujarati.nuktnnagujarati_nuktagujarati.nukttagujarati_nuktagujarati.nuktthagujarati_nuktagujarati.nuktdagujarati_nuktagujarati.nuktdhagujarati_nuktagujarati.nuktnagujarati_nuktagujarati.nuktpagujarati_nuktagujarati.nuktphagujarati_nuktagujarati.nuktbagujarati_nuktagujarati.nuktbhagujarati_nuktagujarati.nuktmagujarati_nuktagujarati.nuktyagujarati_nuktagujarati.nuktragujarati_nuktagujarati.nuktlagujarati_nuktagujarati.nuktllagujarati_nuktagujarati.nuktvagujarati_nuktagujarati.nuktshagujarati_nuktagujarati.nuktssagujarati_nuktagujarati.nuktsagujarati_nuktagujarati.nukthagujarati_nuktagujarati.nuktuniAA3_ACD.half_AB0_ACD.vatuguj_nna_ra_virama.vatugur_ra_halant.blwfgur_va_halant.blwfgur_ha_halant.blwfgur_ya_halant.pstfglyph152glyph153glyph154glyph155glyph156glyph157uniA30_A4D.blwf_A41.blwsuniA39_A4D.blwf_A41.blwsnounicode_3_1_3glyph161ragurmukhi_A4D.blwf_A42.blwsuniA39_A4D.blwf_A42.blwsnounicode_3_1_2fnounicode_3_1_30gur_na_uumatra_tippi.abvsgur_aamatra_bindi.abvsgur_u_addak.pstsgur_uu_addak.pstsgur_oo_addak.psts gur_ka.nukt gur_gha.nukt gur_nga.nukt gur_ca.nukt gur_cha.nukt gur_jha.nukt gur_nya.nukt gur_tta.nukt gur_ttha.nukt gur_ddha.nukt gur_nna.nukt gur_ta.nukt gur_tha.nukt gur_da.nukt gur_dha.nukt gur_na.nukt gur_pa.nukt gur_ba.nukt gur_bha.nukt gur_ma.nukt gur_ya.nukt gur_ra.nukt gur_va.nuktglyph194 gur_ha.nukt gur_a.nukt gur_aa.nukt gur_i.nukt gur_ii.nukt gur_u.nukt gur_uu.nukt gur_ee.nukt gur_ai.nukt gur_oo.nukt gur_au.nuktgur_aa_bindi.abvsglyph207uniA35_A4D.blwf_A41.blwsuniA35_A4D.blwf_A42.blwsuniA30_A4D.blwf_A4D.blwsuniA39_A4D.blwf_A4D.blwsuniA35_A4D.blwf_A4D.blwsEngsamiffffiffllongs_t m_n_armenian m_e_armenian m_i_armenian v_n_armenian m_x_armenianyodhiriqhebrewuniFB1EyodyodpatahhebrewayinaltonehebrewuniFB21uniFB22uniFB23uniFB24uniFB25uniFB26uniFB27uniFB28uniFB29shinshindothebrewshinsindothebrewshindageshshindothebrewshindageshsindothebrewalefpatahhebrewalefqamatshebrewalefdageshhebrewbetdageshhebrewgimeldageshhebrewdaletdageshhebrewhedageshhebrewvavdageshhebrewzayindageshhebrewtetdageshhebrewyoddageshhebrewfinalkafdageshhebrewkafdageshhebrewlameddageshhebrewmemdageshhebrewnundageshhebrewsamekhdageshhebrewpefinaldageshhebrewpedageshhebrewtsadidageshhebrewqofdageshhebrewreshdageshhebrewshindageshhebrewtavdageshhebrewvavholamhebrew betrafehebrew kafrafehebrew perafehebrewaleflamedhebrewuniFFFDuni10380uni10381uni10382uni10383uni10384uni10385uni10386uni10387uni10388uni10389uni1038auni1038buni1038cuni1038duni1038euni1038funi10390uni10391uni10392uni10393uni10394uni10395uni10396uni10397uni10398uni10399uni1039auni1039buni1039cuni1039duni1039funi103a0uni103a1uni103a2uni103a3uni103a4uni103a5uni103a6uni103a7uni103a8uni103a9uni103aauni103abuni103acuni103aduni103aeuni103afuni103b0uni103b1uni103b2uni103b3uni103b4uni103b5uni103b6uni103b7uni103b8uni103b9uni103bauni103bbuni103bcuni103bduni103beuni103bfuni103c0uni103c1uni103c2uni103c3uni103c8uni103c9uni103cauni103cbuni103ccuni103cduni103ceuni103cfuni103d0uni103d1uni103d2uni103d3uni103d4uni103d5uni10480uni10481uni10482uni10483uni10484uni10485uni10486uni10487uni10488uni10489uni1048auni1048buni1048cuni1048duni1048euni1048funi10490uni10491uni10492uni10493uni10494uni10495uni10496uni10497uni10498uni10499uni1049auni1049buni1049cuni1049duni1049euni1049funi104a0uni104a1uni104a2uni104a3uni104a4uni104a5uni104a6uni104a7uni10900uni10901uni10902uni10903uni10904uni10905uni10906uni10907uni10908uni10909uni1090auni1090buni1090cuni1090duni1090euni1090funi10910uni10911uni10912uni10913uni10914uni10915uni10916uni10917uni10918uni10919uni1091auni1091buni1091f r-./0abcdpqrs^__`#$$%%&&''(()fghiijjknopqqrvw %&,--.011223<=>?BCDEz{         t u        GHHIIJOPQRSTXYYZ[\\]effg    $%%&,--.45566778899:QRRSSTTUUVVW]^^_`aabcddeeffgijjklmmnsttuuvvwz{{|    #$BCCDHIIJ^__`ijjk9::;bcfgijrs"  F 8DFLTzarmnbengbng2cyrldev2devageor$gjr2.grekLgujrVgur2tguruhebrlatnphnxsyrctml2ugarxpeo$  %(*.1  %(*.1  "&-/2  "&-/2  #'+03  #'+03 $), $), JII "ISM .LSM .NLD 6NSM .SKS .! 4aalt:abvs@abvsFabvsLakhnRakhnXakhn^blwfdblwfjblwfpblwfvblws|blwsblwsblwsccmpccmpccmpdligfrachalfhalfhalfhalnhalnhlighliginitligaligaligaligaligaloclnuktnuktnuktprespres pres(pstf0pstf6psts\""# #8'v'')),)D)*,X, -M,L6",bqcr28RX ,>Pbt     .     / $(,28DHLRX&8Z|                                         I "JT^hr|&0:DNXblvsvtvuvvvwvxvyvzv{v|v}v~vvvvvvvvvvvvvvvvvvvvvvvTu$.8BL`jt~w^x^y^^^oz^$^%^{^|^^^o}^~^^/^W^9:;=@CDEFJOS]0V(V`jt~ (2<FPZdnx"""""""""""."/"""""0"""""""""""""2"""""""""" % !&" Cs_]T[" 2o[3oB9@6 , XnoT TTBHNTZ`flrx~   6 "(IOILOLIIWA*  ,,,!YnoT &0:DNXblvUVWXZ[ ]^_^ J<FPZdnx",6@     TWYY[]bdfm ouCC +\fpz$.8BLVblvDoEoFoGooHoJoKoLoooooQoRoSooUoVoXoYoZo[o\o]oo o`oaobocodoeofogoooooooHho9>@LN]24#68&HH)*%PZdnx",6@JT^hr|`abcdeghijklmnoprqstuvwxyz{|}~ #,*  &Pf    "          "         *  TY=YYqu)Xblv",6@JT^hr~(YHYIYJYKY+Y,Y^YaY`YbYnEYLY2YMYNYOY7YPYQYRYGYSYYYYYnnYYYYY Y!Y;YY]_Y\^Y"Y TWYY[\^lrrtt" %S!HR\fpz$.8BLV`jt~5<:7=9;!9<=>?@ABCDEGHIJKLNOPQRTVZ\z}RXfV>HR\fpz$.8BL^^^^^^^^^^^^^^^^^^^^^^^^^^^^  4@Vb._-^/_1a0`Ba "6q5f4e3l'TFg "(AY<qUqWYVg [^Z_@ ^_`ae FV $.@Z n9@LFE; &,LYVGSR }D|C~D  &,28>D\{YzSyRxQwPvKuJt<Dc ?m>l :;DTg@:DNZdv  J\noTV n9@LALBLs@VML4HNLFE;PLL R^LiLkL]lL $V`mL8Y6> DC "(.4\LYVGSR r<q;p:o9 }D|C~D $*06<BHNT\{YzSyRxQwPvKuJt<TOL:;H\DEGLRSTUVXZ\`cdefg:d,NhT"T^J|Xz $  &,28>D &,$*06<  $*06< $*06<"(.4:@FLRX^djpv|   "(.4   &,'&$#"!( &,28>DJPV\bhnt;:8643210/.,+*)$*06<BHNTZ`flIHGFEDCBA@?>=< &,ONMLKJ $*06<BHNT]\[ZYXWUSQP"(.4:@FLRX^djpv|usrqponlkigecb`_ zyxw|`bcdehjkmoqrstuvwx{|}~Z &@,'&,'&,'& 9|8{n.  ?d>c 1fT]:z  *4FPbl(:L^p"4FXbt}  ~                                                         h    : '/=OZgopu, "'=#T$.8#D$D%D&DiXDhXgDHIJK "FPr(& &&% &)% $&,-P *%& +-&Q-%& ,`bijmn*  >>> *z)x[Z*6BN      &0:DNXblv0o.oooooooooooooo?]{|$%%PZdnx",6@JT^hr| #*  &'&/  TDFLTzarmnbengbng2cyrldev2devageorgjr2grekgujr gur2guru(hebr2latnLphnxZsyrcftml2rugar~xpeo    JII    abvmVabvm\blwmbblwmhkernnkerntlfbdzmarkmarkmarkmarkmarkmkmkrtbd     "*2:BLT\dlt|LhPB "$&l,6,-6<DJPV\bhnt,,,,,,,,dX,, P &,28>DJPV\bhntz "(.4:@FLRX^djpv|u}HuuQT6CTSCrn`=y66LT<nYiu!!!Q!!|!!!!!A!!!!6!!!!!!!X!W!!o!!!!;!!!vT!P!!]}G iikmpqv/EIK H 28>DJPV\bhnt].L7W.T+Z.^=@.<#:%_8c8 0&6 XXW{BN  "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<X:|bXXkX.W:lP1mXMW;8nX8l88naiXXXXXXjXXXXmmnmW:{yzyiiXXXbXX{{|{;mml mmS`xZNX8mclX88 888l86888W8($=D]4;CHMU]bglrtz|*389HHPRWWZZ^^beggqrtt  &&//44:;==AE !#')*+ , -1 &,28>DJPV\bhntzK]XbT\\[( # "ZXYVWZ  Z"F. d $*06<BHNTZ`flrx~ &,28>DJPV\bhntz X=aDo6xXXXXXX====aDDDD|z{z66666xxXX=D=D=D=D||||666ax$$((,,2288<<DDHHLLRR XX \\ $*27<AGIOQ*3W89arrcj< y $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv|O3}[XXk{Xp3eO4mXMW? nonfodj3OYk};m `x^NWX9mm?&?m eo<W.#} y$%&'()*+,-./0123456789:;<=DFHJKLMNOPQRSTUVXZ\]45HPQRVWZ^bcdegqrtvyz{| /12347:;=ABCDE__ #&()+- u v: y |< @ B C D EF &,28>DJPV\bhntz "(.4:@FLRX^djpv|^OUR>X>RAUUUU/UU#U :U,X,S[XURUBB=X&U03>s=[T 2:.@  Yh  28RXy B >~ &,28>DJPV\bhntzSSS l SSSmb SS/<yvh^h^hbhhlphhM/hhjhhAhhjhKl5hkoUhQvU3hMkBkBRnR00b$= D]$ w x } ~  V\bhntz ^Adn1;Q(<   (((((d(\4B "FLRX^djpv| [ $8+TKt  2FFTmou{|}~"(.|`C]%f. D &,28>DJPV\bhntz |yfDmJUUkM_wq)bg.&O L|Q6?.'303h00m_weR; idbD9=?CDEFJOTUWX]w}=>?@DKOTY_abdtuvwxyz{|}~cdefo"(.Xtpk<&J #HNTZ`flrx~T ! *O;$&#0+). - #9TMx *   +FGTmouCD"(.zawlf $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTxaejgdi9jjedce _eqehaXaed ciecgffegaggaeac d hcea egeifga>>5|7@^^ag[i_didieeYgaeagWi#aeg4cge.e:cc^d^gddigcgg^g;e>>A;D>>kjd^^gdaedaagda aeWchgaj^9ddaddd ceecg ddg|7@jigeadaa++,.499 <= ?F HHJQSUWY ]]#ww${}%()$%*)),,1-=@3DD7KK8OO9TT:YY;__<ab=dd?t@TUX^_fghlmnotuxz%&ghijr"(.4:@FLkUgXdGLi@8Xjjkkssuuzz~~ kkww~~jksuz~M$$%%&&''(())**,,--..// 22 33 44 55 66 779:;;<<==          "" $$&&88::??yy      P P Z Z \ \ d d f f  x$$&&**,,--224466779:;;<<==DDFFGGHHII JJMM PQ RRSSTTUUVV WW XXYZ[[ \\]]         !! ""## $$%% &&++--//11338899::;;<< ==>> ??@@ ; ; Q Q Z Z [ [ \ \ ] ] d d e e f f g g    ]$%&'()*,-./2345679:;<= "$&8:?y   P Z \ d f ^NDDEEFFGGHHIIJJ KKNN PQRRSSUU VV WW XX YZ[[ \\]]      !! ## %% ++ -- // 11 33 99<<>>@@       ; ; Q Q [ [ ] ] e e g g HDDFF GGHHIIJJMMPQRR SS TTUU VVWWXX YZ[[\\]]          !!##%%++ -- // 11 33 99<<>>@@22     Q Q [ [ ] ] e e g g _DEFGHIJKNPQRSUVWXYZ[\] !#%+-/139<>@    ; Q [ ] e g ,}$$%%&'))**.. //22334466779: ;; << == DD EE FF HHIIJJKKNNPQRRSS UUVVWWXXYZ[[\\]]          !!""##$$%%--//3388 99:: ;; <<== >>?? @@22        ; ; P P Q Q Z Z [ [ \ \ ] ] d d e e f f g g  mm}}      $%&')*./234679:;<=DEFHIJKNPQRSUVWXYZ[\]  !"#$%-/389:;<=>?@2    ; P Q Z [ \ ] d e f g  mm}}         p$$&&**--224466779:;;<<==DDFHII JJMM PQ RRSS TTUU VV WW XX YZ[[ \\]]           !! ""## $$%% &&++ -- // 33 8899::;;<< ==>> ??@@ 22   ; ; Q Q Z Z [ [ \ \ ] ] d d e e f f g g    m}  YȻ!DarkRadiant-2.5.0/install/ui/lightinspector.fbp000066400000000000000000003264511321750546400215360ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY LightInspectorMainPanel 600,345 wxTAB_TRAVERSAL bSizer4 wxVERTICAL none 12 wxALL|wxEXPAND 1 bSizer5 wxHORIZONTAL none 5 wxEXPAND 0 bSizer6 wxVERTICAL none 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Light Volume 0 0 1 LightInspectorVolumeLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 1 bSizer8 wxHORIZONTAL none 12 wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Omni 0 0 80,80 1 LightInspectorOmniButton 1 protected 1 Resizable 1 -1,-1 0 wxFILTER_NONE wxDefaultValidator 0 0 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_staticline1 1 protected 1 Resizable 1 wxLI_VERTICAL 0 0 wxEXPAND 0 bSizer9 wxVERTICAL none 12 wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Projected 0 0 80,80 1 LightInspectorProjectedButton 1 protected 1 Resizable 1 -1,-1 0 wxFILTER_NONE wxDefaultValidator 1 6 wxALIGN_RIGHT|wxTOP 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Use start/end 0 0 1 LightInspectorStartEnd 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Colour 0 0 1 LightInspectorColourLabel 1 protected 1 Resizable 1 0 -1 12 wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 70,25 1 LightInspectorColour 1 protected 1 Resizable 1 -1,-1 wxCLRP_DEFAULT_STYLE|wxCLRP_USE_TEXTCTRL 0 wxFILTER_NONE wxDefaultValidator 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Options 0 0 1 LightInspectorOptionsLabel 1 protected 1 Resizable 1 0 -1 12 wxLEFT 0 bSizer7 wxVERTICAL none 5 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Parallel 0 0 1 LightInspectorParallel 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Do not cast shadows (fast) 0 0 1 LightInspectorNoShadows 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Skip specular lighting 0 0 1 LightInspectorSkipSpecular 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Skip diffuse lighting 0 0 1 LightInspectorSkipDiffuse 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxEXPAND|wxLEFT 1 bSizer10 wxVERTICAL none 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Light Texture 0 0 1 LightInspectorTextureLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 LightInspectorChooserPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer81 wxVERTICAL none DarkRadiant-2.5.0/install/ui/lightinspector.xrc000066400000000000000000000135031321750546400215520ustar00rootroot00000000000000 600,345 wxVERTICAL wxALL|wxEXPAND 12 wxHORIZONTAL wxEXPAND 5 wxVERTICAL wxALL 5 -1 wxEXPAND|wxLEFT 12 wxHORIZONTAL wxRIGHT 12 0 wxEXPAND 0 wxEXPAND 0 wxVERTICAL wxLEFT 12 1 wxALIGN_RIGHT|wxTOP 6 0 wxALL 5 -1 wxLEFT|wxRIGHT 12 #000000 wxALL 5 -1 wxLEFT 12 wxVERTICAL wxALL 5 0 wxALL 5 0 wxALL 5 0 wxALL 5 0 wxEXPAND|wxLEFT 12 wxVERTICAL wxALL 5 -1 wxEXPAND|wxLEFT 12 wxVERTICAL DarkRadiant-2.5.0/install/ui/missioninfoeditdialog.fbp000066400000000000000000005773771321750546400231020ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject3 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY -1,-1 MissionInfoEditDialogMainPanel 900,546 wxTAB_TRAVERSAL bSizer23 wxVERTICAL none 12 wxALL|wxEXPAND 1 bSizer13 wxVERTICAL none 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 450 0 1 MissionInfoEditDialogSplitter 1 protected 1 Resizable 1 350 -1 1 wxSPLIT_VERTICAL wxSP_3D|wxSP_LIVE_UPDATE 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel1 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer14 wxVERTICAL none 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Mission Info 0 0 1 MissionInfoLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 0 2 wxBOTH 1 12 m_textCtrl1 wxFLEX_GROWMODE_SPECIFIED none 7 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Title: 0 0 1 m_staticText8 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 MissionInfoEditDialogTitleEntry 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Author: 0 0 1 m_staticText81 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 MissionInfoEditDialogAuthorEntry 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Description: 0 0 1 m_staticText9 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,-1 1 MissionInfoEditDialogDescriptionEntry 1 protected 1 Resizable 1 -1,80 wxTE_MULTILINE|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Version: 0 0 1 m_staticText811 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 MissionInfoEditDialogVersionEntry 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Required TDM Version: 0 0 1 m_staticText82 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer51 wxHORIZONTAL none 6 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 MissionInfoEditDialogReqTdmVersionEntry 1 protected 1 Resizable 1 60,-1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY (optional, e.g. "2.03") 0 0 1 m_staticText83 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer5 wxVERTICAL none 6 wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Mission Titles: 0 0 1 m_staticText821 1 protected 1 Resizable 1 0 -1 6 wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,93,90,-1,70,0 0 0 wxID_ANY Optional. Fill in if this package is a campaign containing several maps 0 0 1 m_staticText8211 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 MissionInfoEditDialogMissionTitleList 1 protected 1 Resizable 1 -1,150 wxTR_DEFAULT_STYLE 0 6 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Output Path: 0 0 1 m_staticText12 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY C:GamesDarkmodfmsgathers 0 0 1 MissionInfoEditDialogOutputPath 1 protected 1 Resizable 1 0 -1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 MissionInfoEditDialogPreviewPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer7 wxVERTICAL none 12 wxEXPAND|wxTOP 0 3 wxBOTH 0 0 fgSizer2 wxFLEX_GROWMODE_SPECIFIED none 0 0 12 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Edit readme.txt... 0 0 -1,-1 1 MissionInfoEditDialogEditReadmeButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Save 0 0 -1,-1 1 MissionInfoEditDialogSaveButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 -1,-1 1 MissionInfoEditDialogCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/missioninfoeditdialog.xrc000066400000000000000000000221131321750546400230740ustar00rootroot00000000000000 900,546 wxVERTICAL wxALL|wxEXPAND 12 wxVERTICAL wxEXPAND 5 350 1 450 vertical wxVERTICAL wxBOTTOM 6 -1 wxEXPAND|wxLEFT 12 7 2 0 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxALL|wxEXPAND 6 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxALL|wxEXPAND 6 wxTOP 6 -1 wxALL|wxEXPAND 6 -1,80 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxALL|wxEXPAND 6 wxALIGN_CENTER_VERTICAL 0 -1 wxEXPAND 5 wxHORIZONTAL wxALL 6 60,-1 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxVERTICAL wxTOP 6 -1 wxTOP 6 normal 0 -1 wxALL|wxEXPAND 6 -1,150 wxALIGN_CENTER_VERTICAL|wxALL 6 -1 wxALL|wxEXPAND 6 -1 wxVERTICAL wxEXPAND|wxTOP 12 0 3 0 0 0 wxLEFT 12 0 wxLEFT 6 0 wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/missioninforeadmedialog.fbp000066400000000000000000002155371321750546400233750ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject3 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY -1,-1 MissionInfoReadmeDialogMainPanel 900,500 wxTAB_TRAVERSAL bSizer23 wxVERTICAL none 12 wxALL|wxEXPAND 1 bSizer13 wxVERTICAL none 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 450 0 1 MissionInfoReadmeSplitter 1 protected 1 Resizable 1 350 -1 1 wxSPLIT_VERTICAL wxSP_3D|wxSP_LIVE_UPDATE 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel1 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer14 wxVERTICAL none 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Mission Readme 0 0 1 MissionReadmeLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,-1 1 MissionInfoReadmeContentsEntry 1 protected 1 Resizable 1 -1,80 wxTE_MULTILINE|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator 12 wxEXPAND|wxLEFT 0 2 wxBOTH 1 12 m_textCtrl1 wxFLEX_GROWMODE_SPECIFIED none 7 0 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Output Path: 0 0 1 m_staticText12 1 protected 1 Resizable 1 0 -1 6 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY C:GamesDarkmodfmsgathers 0 0 1 MissionInfoReadmeOutputPath 1 protected 1 Resizable 1 0 -1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 MissionInfoReadmeDialogPreviewPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer7 wxVERTICAL none 12 wxALIGN_RIGHT|wxLEFT|wxTOP 0 bSizer9 wxHORIZONTAL none 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Save 0 0 -1,-1 1 MissionInfoReadmeDialogSaveButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 -1,-1 1 MissionInfoReadmeDialogCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/missioninforeadmedialog.xrc000066400000000000000000000075001321750546400234070ustar00rootroot00000000000000 900,500 wxVERTICAL wxALL|wxEXPAND 12 wxVERTICAL wxEXPAND 5 350 1 450 vertical wxVERTICAL wxBOTTOM 6 -1 wxEXPAND|wxLEFT|wxRIGHT 12 -1,80 wxEXPAND|wxLEFT 12 7 2 0 12 1 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxALL|wxEXPAND 6 -1 wxVERTICAL wxALIGN_RIGHT|wxLEFT|wxTOP 12 wxHORIZONTAL wxLEFT 6 0 wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/modelselector.fbp000066400000000000000000002224351321750546400213360ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject1 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY -1,-1 ModelSelectorPanel -1,-1 wxTAB_TRAVERSAL bSizer1 wxVERTICAL none 12 wxALL|wxEXPAND 1 bSizer2 wxHORIZONTAL none 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 50 0 1 ModelSelectorSplitter 1 protected 1 Resizable 0.0 0 -1 1 wxSPLIT_VERTICAL wxSP_3D|wxSP_LIVE_UPDATE 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ModelSelectorLeftPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer4 wxVERTICAL none 0 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ModelSelectorOptionsPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer5 wxHORIZONTAL none 6 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Options: 0 0 1 m_staticText1 1 protected 1 Resizable 1 0 -1 6 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Surround with monsterclip brush 0 0 1 ModelSelectorMonsterClipOption 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ModelSelectorRightPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer61 wxVERTICAL none 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT 0 bSizer6 wxHORIZONTAL none 6 wxRIGHT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_CANCEL Reload Skins 0 0 1 ModelSelectorReloadSkinsButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 24 wxRIGHT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_CANCEL Reload Models 0 0 1 ModelSelectorReloadModelsButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_CANCEL Cancel 0 0 1 ModelSelectorCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_OK OK 0 0 1 ModelSelectorOkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/modelselector.xrc000066400000000000000000000070701321750546400213570ustar00rootroot00000000000000 wxVERTICAL wxALL|wxEXPAND 12 wxHORIZONTAL wxEXPAND 5 0 0 50 vertical wxVERTICAL wxEXPAND 0 wxHORIZONTAL wxALL 6 -1 wxALL 6 0 wxVERTICAL wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT 12 wxHORIZONTAL wxRIGHT 6 0 wxRIGHT 24 0 6 0 wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/objectivecomponentsdialog.fbp000066400000000000000000011472341321750546400237410ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ObjCompMainPanel 700,600 wxTAB_TRAVERSAL bSizer10 wxVERTICAL none 12 wxALL|wxEXPAND 1 bSizer11 wxVERTICAL none 0 wxEXPAND 0 2 wxBOTH 1 12 fgSizer1 wxFLEX_GROWMODE_SPECIFIED none 8 10 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Description 0 0 1 m_staticText4 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompDescription 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Difficulty 0 0 1 Diff 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer12 wxHORIZONTAL none 0 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompDiffPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer13 wxHORIZONTAL none 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Iniital State 0 0 1 m_staticText6 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompInitialState 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Flags 0 0 1 m_staticText26 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer34 wxHORIZONTAL none 0 wxALIGN_CENTER_VERTICAL|wxALL 1 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Mandatory 0 0 1 ObjCompObjMandatory 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxLEFT 1 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Ongoing 0 0 1 ObjCompObjOngoing 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxLEFT 1 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Irreversible 0 0 1 ObjCompObjIrreversible 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxLEFT 1 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Visible 0 0 1 ObjCompObjVisible 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Enabling Objectives 0 0 1 m_staticText7 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompEnablingObjectives 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Sucess Logic 0 0 1 m_staticText8 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer14 wxHORIZONTAL none 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompSuccessLogic 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Failure Logic 0 0 1 m_staticText9 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompFailureLogic 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Completion Script 0 0 1 m_staticText81 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer141 wxHORIZONTAL none 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompCompletionScript 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Failure Script 0 0 1 m_staticText91 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompFailureScript 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Completion Target 0 0 1 m_staticText811 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1411 wxHORIZONTAL none 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompCompletionTarget 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Failure Target 0 0 1 m_staticText911 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompFailureTarget 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Components 0 0 1 ObjCompListLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 1 bSizer25 wxVERTICAL none 5 wxEXPAND 1 bSizer26 wxVERTICAL none 12 wxBOTTOM|wxEXPAND 0 bSizer27 wxHORIZONTAL none 5 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,80 1 ObjCompListViewPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer17 wxVERTICAL none 5 wxEXPAND 0 bSizer28 wxVERTICAL none 6 wxEXPAND|wxLEFT 0 bSizer29 wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ADD Add 0 0 1 ObjCompAddComponentButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 ObjCompDeleteComponentButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompComponentEditPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer35 wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 2 wxBOTH 1 12 fgSizer2 wxFLEX_GROWMODE_SPECIFIED none 2 12 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Type 0 0 1 m_staticText24 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompComponentType 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Flags 0 0 1 m_staticText25 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 0 bSizer31 wxHORIZONTAL none 0 wxALIGN_CENTER_VERTICAL|wxALL 1 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Satisfied at start 0 0 1 ObjCompSatisfiedAtStart 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Irreversible 0 0 1 ObjCompIrreversible 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 1 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Boolean NOT 0 0 1 ObjCompBooleanNOT 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 1 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Player responsible 0 0 1 ObjCompPlayerResponsible 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCompEditorContainer 1 protected 1 Resizable 1 0 wxSTATIC_BORDER|wxTAB_TRAVERSAL bSizer32 wxVERTICAL none 12 wxEXPAND|wxTOP 0 0 1 0 0 0 1 0 0 m_sdbSizer1 protected DarkRadiant-2.5.0/install/ui/objectivecomponentsdialog.xrc000066400000000000000000000405131321750546400237550ustar00rootroot00000000000000 700,600 wxVERTICAL wxALL|wxEXPAND 12 wxVERTICAL wxEXPAND 0 8 2 10 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 5 -1 wxEXPAND 0 wxHORIZONTAL wxEXPAND 0 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 0 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxALL 0 0 wxALIGN_CENTER_VERTICAL|wxLEFT 6 0 wxALIGN_CENTER_VERTICAL|wxLEFT 6 0 wxALIGN_CENTER_VERTICAL|wxLEFT 6 0 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 5 wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 5 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 5 wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 5 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 5 wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 5 wxBOTTOM|wxTOP 12 -1 wxEXPAND|wxLEFT 12 wxVERTICAL wxEXPAND 5 wxVERTICAL wxBOTTOM|wxEXPAND 12 wxHORIZONTAL wxEXPAND | wxALL 5 wxVERTICAL wxEXPAND 5 wxVERTICAL wxEXPAND|wxLEFT 6 wxVERTICAL wxBOTTOM|wxEXPAND 6 0 wxEXPAND 5 0 wxEXPAND | wxALL 0 wxVERTICAL wxBOTTOM|wxEXPAND 6 2 2 12 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 0 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxEXPAND 0 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxALL 0 0 wxALIGN_CENTER_VERTICAL 0 0 wxALIGN_CENTER_VERTICAL|wxALL 0 0 wxALIGN_CENTER_VERTICAL|wxALL 0 0 wxEXPAND 0 wxVERTICAL wxEXPAND|wxTOP 12 wxALIGN_CENTER_HORIZONTAL|wxALL 5 wxALIGN_CENTER_HORIZONTAL|wxALL 5 DarkRadiant-2.5.0/install/ui/objectiveconditionsdialog.fbp000066400000000000000000004503441321750546400237230ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ObjCondDialogMainPanel 500,600 wxTAB_TRAVERSAL bSizer5 wxVERTICAL none 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Objective Conditions 0 0 1 ObjCondDialogTopLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 1 bSizer61 wxHORIZONTAL none 6 wxEXPAND|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCondDialogConditionViewPanel 1 protected 1 Resizable 1 -1,120 0 wxTAB_TRAVERSAL bSizer7 wxVERTICAL none 5 wxEXPAND 0 ButtonSizer1 wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 ObjCondDialogAddConditionButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 ObjCondDialogDeleteConditionButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Condition 0 0 1 ObjCondDialogConditionLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 0 bSizer6 wxHORIZONTAL none 12 wxEXPAND|wxLEFT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCondDialogConditionEditPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL 2 wxBOTH 1 12 fgSizer1 wxFLEX_GROWMODE_SPECIFIED none 7 6 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Source Mission: 0 0 1 m_staticText6 1 protected 1 Resizable 1 0 -1 0 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ObjCondDialogSourceMission 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Source Objective: 0 0 1 m_staticText7 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ObjCondDialogSourceObjective 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Source Objective State: 0 0 1 m_staticText8 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCondDialogSourceObjectiveState 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Target Objective: 0 0 1 m_staticText71 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCondDialogTargetObjective 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Action: 0 0 1 m_staticText9 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCondDialogAction 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Action Value: 0 0 1 m_staticText10 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjCondDialogActionValue 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Sentence 0 0 1 ObjCondDialogSentenceLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 0 bSizer91 wxHORIZONTAL none 12 wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY - 0 0 1 ObjCondDialogSentence 1 protected 1 Resizable 1 0 -1 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 bSizer8 wxHORIZONTAL none 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 ObjCondDialogCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 ObjCondDialogOkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/objectiveconditionsdialog.xrc000066400000000000000000000177271321750546400237540ustar00rootroot00000000000000 500,600 wxVERTICAL wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxRIGHT 6 -1,120 wxVERTICAL wxEXPAND 5 wxVERTICAL wxBOTTOM|wxEXPAND 6 80,-1 0 wxEXPAND 6 80,-1 0 wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxLEFT 12 7 2 6 12 1 wxALIGN_CENTER_VERTICAL 0 -1 wxALL|wxEXPAND 0 0 0 10 wxALIGN_CENTER_VERTICAL 0 -1 wxEXPAND 5 0 0 10 wxALIGN_CENTER_VERTICAL 0 -1 wxEXPAND 0 0 wxALIGN_CENTER_VERTICAL 0 -1 wxEXPAND 0 0 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 0 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxEXPAND 0 0 wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxLEFT 12 -1 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxLEFT 6 0 wxEXPAND|wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/objectiveseditor.fbp000066400000000000000000003761071321750546400220470ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ObjDialogMainPanel 500,600 wxTAB_TRAVERSAL bSizer5 wxVERTICAL none 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Objective Entities 0 0 1 ObjDialogEntityLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 1 bSizer61 wxHORIZONTAL none 6 wxEXPAND|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjDialogEntityPanel 1 protected 1 Resizable 1 -1,120 0 wxTAB_TRAVERSAL bSizer7 wxVERTICAL none 5 wxEXPAND 0 ButtonSizer1 wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 ObjDialogAddEntityButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 ObjDialogDeleteEntityButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Objectives 0 0 1 ObjDialogObjectivesLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 4 bSizer6 wxHORIZONTAL none 6 wxEXPAND|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjDialogObjectivesPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer9 wxVERTICAL none 5 wxEXPAND | wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ObjDialogObjectiveButtonPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL ButtonSizer wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 ObjDialogAddObjectiveButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Delete 0 0 1 ObjDialogDeleteObjectiveButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Clear 0 0 1 ObjDialogClearObjectiveButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Edit 0 0 1 ObjDialogEditObjectiveButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 5 wxEXPAND | wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_staticline1 1 protected 1 Resizable 1 wxLI_HORIZONTAL 0 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Move up 0 0 1 ObjDialogMoveObjUpButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Move down 0 0 1 ObjDialogMoveObjDownButton 1 protected 1 Resizable 1 80,-1 0 wxFILTER_NONE wxDefaultValidator 12 wxLEFT|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Success Logic 0 0 1 ObjDialogLogicLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 0 bSizer91 wxHORIZONTAL none 12 wxLEFT 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Edit Mission Success Logic 0 0 1 ObjDialogSuccessLogicButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Edit Objective Conditions 0 0 1 ObjDialogObjConditionsButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 0 bSizer8 wxHORIZONTAL none 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 ObjDialogCancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 ObjDialogOkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/objectiveseditor.xrc000066400000000000000000000160551321750546400220650ustar00rootroot00000000000000 500,600 wxVERTICAL wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxRIGHT 6 -1,120 wxVERTICAL wxEXPAND 5 wxVERTICAL wxBOTTOM|wxEXPAND 6 80,-1 0 wxEXPAND 6 80,-1 0 wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxRIGHT 6 wxVERTICAL wxEXPAND | wxALL 5 wxVERTICAL wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxEXPAND | wxALL 5 wxBOTTOM|wxEXPAND 6 80,-1 0 wxBOTTOM|wxEXPAND 6 80,-1 0 wxLEFT|wxRIGHT|wxTOP 12 -1 wxEXPAND|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxLEFT 12 0 wxLEFT 6 0 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP 12 wxHORIZONTAL wxEXPAND|wxLEFT 6 0 wxEXPAND|wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/overlaydialog.fbp000066400000000000000000004645321321750546400213440ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject1 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY OverlayDialogMainPanel 500,380 wxTAB_TRAVERSAL bSizer1 wxVERTICAL none 12 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Use background image 0 0 1 OverlayDialogUseBackgroundImage 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 OverlayDialogControlPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL 2 wxBOTH 1 6 fgSizer1 wxFLEX_GROWMODE_SPECIFIED none 6 6 5 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Image file 0 0 1 OverlayDialogLabelFile 1 protected 1 Resizable 1 0 -1 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 Select a file 0 1 OverlayDialogFilePicker 1 protected 1 Resizable 1 wxFLP_DEFAULT_STYLE|wxFLP_OPEN 0 wxFILTER_NONE wxDefaultValidator *.* 5 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Transparency 0 0 1 OverlayDialogLabelTrans 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer11 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY transparent 0 0 1 m_staticText8 1 protected 1 Resizable 1 0 -1 3 wxEXPAND|wxTOP 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 OverlayDialogTransparencySlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 12 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY opaque 0 0 1 m_staticText81 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Image scale 0 0 1 OverlayDialogLabelScale 1 protected 1 Resizable 1 0 -1 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 OverlayDialogScalePanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer5 wxHORIZONTAL none 3 wxTOP 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 2000 0 0 0 1 OverlayDialogScaleSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 100 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Horizontal offset 0 0 1 OverlayDialogLabelHOffset 1 protected 1 Resizable 1 0 -1 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 OverlayDialogHorizOffsetPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer51 wxHORIZONTAL none 3 wxTOP 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 2000 0 -2000 0 1 OverlayDialogHorizOffsetSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Vertical offset 0 0 1 OverlayDialogLabelVOffset 1 protected 1 Resizable 1 0 -1 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 OverlayDialogVertOffsetPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer52 wxHORIZONTAL none 3 wxTOP 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 2000 0 -2000 0 1 OverlayDialogVertOffsetSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Options 0 0 1 OverlayDialogLabelOptions 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer4 wxVERTICAL none 6 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Keep aspect ratio 0 0 1 OverlayDialogKeepAspect 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Zoom image with viewport 0 0 1 OverlayDialogZoomWithViewport 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Pan image with viewport 0 0 1 OverlayDialogPanWithViewport 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_CLOSE Close 0 0 1 OverlayDialogCloseButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/overlaydialog.xrc000066400000000000000000000172301321750546400213560ustar00rootroot00000000000000 500,380 wxVERTICAL wxALL 12 0 wxEXPAND | wxALL 12 6 2 6 6 1 wxALIGN_CENTER_VERTICAL|wxALL 5 -1 wxALL|wxEXPAND 5 Select a file *.* wxALIGN_CENTER_VERTICAL|wxALL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxLEFT 6 -1 wxEXPAND|wxTOP 3 50 0 100 wxALIGN_CENTER_VERTICAL|wxRIGHT 12 -1 wxALL 5 -1 wxEXPAND | wxALL 0 wxHORIZONTAL wxTOP 3 100 0 2000 wxALL 5 -1 wxEXPAND | wxALL 0 wxHORIZONTAL wxTOP 3 0 -2000 2000 wxALL 5 -1 wxEXPAND | wxALL 0 wxHORIZONTAL wxTOP 3 0 -2000 2000 wxALL 5 -1 wxEXPAND 0 wxVERTICAL wxALL 6 0 wxALL 6 0 wxALL 6 0 wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT 12 0 DarkRadiant-2.5.0/install/ui/particleeditor.fbp000066400000000000000000066175221321750546400215220ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject1 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ParticleEditorMainPanel 800,665 wxTAB_TRAVERSAL bSizer1 wxVERTICAL none 12 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 0 1 ParticleEditorSplitter 1 protected 1 Resizable 0.0 0 -1 1 wxSPLIT_VERTICAL wxSP_3D 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 250,-1 1 m_panel1 1 protected 1 Resizable 1 250,-1 0 wxTAB_TRAVERSAL bSizer2 wxVERTICAL none 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Particle Definitions 0 0 1 ParticleEditorDefinitionLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 3 bSizer5 wxHORIZONTAL none 5 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,120 1 ParticleEditorDefinitionView 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer135 wxVERTICAL none 6 wxEXPAND|wxLEFT 0 bSizer6 wxVERTICAL none 0 wxALL 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_NEW New 0 0 1 ParticleEditorNewDefButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_SAVE Save 0 0 1 ParticleEditorSaveDefButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_COPY Copy 0 0 1 ParticleEditorCopyDefButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Particle Stages 0 0 1 ParticleEditorStageLabel 1 protected 1 Resizable 1 0 -1 0 wxEXPAND | wxALL 2 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,120 1 ParticleEditorStagePanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer8 wxHORIZONTAL none 12 wxEXPAND|wxLEFT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 -1,120 1 ParticleEditorStageView 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer136 wxVERTICAL none 6 wxEXPAND|wxLEFT 0 bSizer9 wxVERTICAL none 6 wxBOTTOM|wxEXPAND 1 bSizer11 wxHORIZONTAL none 6 wxRIGHT 1 bSizer61 wxVERTICAL none 0 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 ParticleEditorAddStageButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Remove 0 0 1 ParticleEditorRemoveStageButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Toggle Visibility 0 0 1 ParticleEditorToggleStageButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 1 bSizer62 wxVERTICAL none 0 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Up 0 0 1 ParticleEditorMoveUpStageButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Down 0 0 1 ParticleEditorMoveDownStageButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP|wxEXPAND 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Duplicate 0 0 1 ParticleEditorDuplicateStageButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_RIGHT 0 bSizer14 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Depth Hack: 0 0 1 m_staticText4 1 protected 1 Resizable 1 0 -1 0 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorDepthHack 1 protected 1 Resizable 1 80,-1 wxSP_ARROW_KEYS 0 12 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Stage Settings 0 0 1 ParticleEditorStageSettingsLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ParticleEditorSettingsNotebook 1 protected 1 Resizable 1 0 Shader 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel5 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer26 wxVERTICAL none 6 wxALL|wxEXPAND 1 2 wxBOTH 1 12 fgSizer1 wxFLEX_GROWMODE_SPECIFIED none 7 6 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Shader: 0 0 1 m_staticText8 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ParticleEditorStageShader 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Colour: 0 0 1 m_staticText9 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ParticleEditorStageColour 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Fade Colour: 0 0 1 m_staticText10 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer15 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ParticleEditorStageFadeColour 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALL|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Use Entity Colour 0 0 1 ParticleEditorStageUseEntityColour 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Fade In Fraction: 0 0 1 m_staticText11 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer16 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageFadeInFrac 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageFadeInFracSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Fade Out Fraction: 0 0 1 m_staticText12 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer161 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageFadeOutFrac 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageFadeOutFracSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Fade Index Fraction: 0 0 1 m_staticText13 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1611 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageFadeIdxFrac 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageFadeIdxFracSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Animation: 0 0 1 m_staticText14 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer19 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Frames: 0 0 1 m_staticText15 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 9999 0 0 0 1 ParticleEditorStageAnimFrames 1 protected 1 Resizable 1 60,-1 wxSP_ARROW_KEYS 0 6 wxLEFT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Rate: 0 0 1 m_staticText16 1 protected 1 Resizable 1 0 -1 0 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageAnimRate 1 protected 1 Resizable 1 60,-1 wxSP_ARROW_KEYS 0 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY FPS 0 0 1 m_staticText17 1 protected 1 Resizable 1 0 -1 Count / Time 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel6 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer261 wxVERTICAL none 6 wxALL|wxEXPAND 1 2 wxBOTH 1 12 fgSizer11 wxFLEX_GROWMODE_SPECIFIED none 7 6 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Count: 0 0 1 m_staticText111 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer162 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 999 0 0 0 1 ParticleEditorStageCount 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 999 0 0 0 1 ParticleEditorStageCountSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Duration / sec: 0 0 1 m_staticText121 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1612 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageDuration 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageDurationSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Bunching: 0 0 1 m_staticText131 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer16111 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageBunching 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageBunchingSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Cycles: 0 0 1 m_staticText1311 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer161111 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 999 0 0 0 1 ParticleEditorStageCycles 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 999 0 1 0 1 ParticleEditorStageCyclesSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 1 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Time Offset / sec: 0 0 1 m_staticText13111 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1611111 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageTimeOffset 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageTimeOffsetSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Dead Time / sec: 0 0 1 m_staticText131111 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer16111111 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageDeadTime 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageDeadTimeSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 Size / Speed 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel7 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer2611 wxVERTICAL none 6 wxALL|wxEXPAND 1 2 wxBOTH 1 12 fgSizer111 wxFLEX_GROWMODE_SPECIFIED none 7 6 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Speed: 0 0 1 m_staticText1111 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1621 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY From: 0 0 1 m_staticText66 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageSpeedFrom 1 protected 1 Resizable 1 50,-1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageSpeedFromSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY To: 0 0 1 m_staticText67 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageSpeedTo 1 protected 1 Resizable 1 50,-1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageSpeedToSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Size: 0 0 1 m_staticText1211 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer16211 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY From: 0 0 1 m_staticText661 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageSizeFrom 1 protected 1 Resizable 1 50,-1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageSizeFromSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY To: 0 0 1 m_staticText671 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageSizeTo 1 protected 1 Resizable 1 50,-1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageSizeToSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Rotation Speed: 0 0 1 m_staticText1312 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer162111 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY From: 0 0 1 m_staticText6611 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageRotationSpeedFrom 1 protected 1 Resizable 1 50,-1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageRotationSpeedFromSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY To: 0 0 1 m_staticText6711 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageRotationSpeedTo 1 protected 1 Resizable 1 50,-1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageRotationSpeedToSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Aspect Ratio: 0 0 1 m_staticText13112 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer162112 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY From: 0 0 1 m_staticText6612 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageAspectFrom 1 protected 1 Resizable 1 50,-1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageAspectFromSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY To: 0 0 1 m_staticText6712 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageAspectTo 1 protected 1 Resizable 1 50,-1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageAspectToSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Gravity: 0 0 1 m_staticText131112 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer16111112 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageGravity 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageGravitySlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 6 wxLEFT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Use World Gravity 0 0 1 ParticleEditorStageUseWorldGravity 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Bounds Expansion: 0 0 1 m_staticText1311111 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer161111111 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageBoundsExpansion 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 Distribution 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel61 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer2612 wxVERTICAL none 6 wxALL|wxEXPAND 1 2 wxBOTH 1 12 fgSizer112 wxFLEX_GROWMODE_SPECIFIED none 7 6 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Shape: 0 0 1 m_staticText1112 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1622 wxHORIZONTAL none 3 wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Rectangular 0 0 1 ParticleEditorStageShapeRect 1 protected 1 Resizable 1 wxRB_GROUP 0 wxFILTER_NONE wxDefaultValidator 0 3 wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Cylindric 0 0 1 ParticleEditorStageShapeCyl 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 3 wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Spherical 0 0 1 ParticleEditorStageSpherical 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY X Size: 0 0 1 m_staticText1212 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer16121 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageXSize 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageXSizeSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Y Size: 0 0 1 m_staticText1313 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer161112 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageYSize 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageYSizeSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Z Size: 0 0 1 m_staticText13113 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1611112 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageZSize 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageZSizeSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Ring Size: 0 0 1 ParticleEditorStageRingSizeLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer16111113 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageRingSize 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageRingSizeSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Offset: 0 0 1 m_staticText1311112 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ParticleEditorStageOffset 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Randomness: 0 0 1 m_staticText97 1 protected 1 Resizable 1 0 -1 3 wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Distribute Particles randomly within Volume 0 0 1 ParticleEditorStageRandomDist 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator Direction / Orientation 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel611 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer26121 wxVERTICAL none 6 wxALL|wxEXPAND 1 2 wxBOTH 1 12 fgSizer1121 wxFLEX_GROWMODE_SPECIFIED none 7 6 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Direction: 0 0 1 m_staticText11121 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer16221 wxHORIZONTAL none 3 wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Cone 0 0 1 ParticleEditorStageCone 1 protected 1 Resizable 1 wxRB_GROUP 0 wxFILTER_NONE wxDefaultValidator 0 3 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Outward 0 0 1 ParticleEditorStageOutward 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Cone Angle: 0 0 1 ParticleEditorStageConeAngleLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer161211 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageConeAngle 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageConeAngleSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Upward Bias: 0 0 1 ParticleEditorStageUpwardBiasLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1611121 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageUpwardBias 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageUpwardBiasSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Orientation: 0 0 1 m_staticText131131 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer16111121 wxHORIZONTAL none 3 wxALL|wxBOTTOM|wxRIGHT|wxTOP|wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY View 0 0 1 ParticleEditorStageOrientView 1 protected 1 Resizable 1 wxRB_GROUP 0 wxFILTER_NONE wxDefaultValidator 0 3 wxALL|wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Aimed 0 0 1 ParticleEditorStageOrientAimed 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 3 wxALL|wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY X 0 0 1 ParticleEditorStageOrientX 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 3 wxALL|wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Y 0 0 1 ParticleEditorStageOrientY 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 3 wxALL|wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Z 0 0 1 ParticleEditorStageOrientZ 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Trails: 0 0 1 ParticleEditorStageTrailsLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer161111131 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 500 0 0 0 1 ParticleEditorStageTrails 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageTrailsSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Time: 0 0 1 ParticleEditorStageTimeLabel 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer1611111121 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageAimedTime 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageAimedTimeSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Initial Angle: 0 0 1 m_staticText971 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer16111111211 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageInitialAngle 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageInitialAngleSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 Path 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel6111 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer261211 wxVERTICAL none 6 wxALL|wxEXPAND 1 2 wxBOTH 1 12 fgSizer11211 wxFLEX_GROWMODE_SPECIFIED none 7 6 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Path Type: 0 0 1 m_staticText111211 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer162211 wxHORIZONTAL none 3 wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Standard 0 0 1 ParticleEditorStagePathStandard 1 protected 1 Resizable 1 wxRB_GROUP 0 wxFILTER_NONE wxDefaultValidator 0 3 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Flies 0 0 1 ParticleEditorStagePathFlies 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 3 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Helix 0 0 1 ParticleEditorStagePathHelix 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Radial Speed: 0 0 1 ParticleEditorStageRadialSpeedLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1612111 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageRadialSpeed 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageRadialSpeedSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Axial Speed: 0 0 1 ParticleEditorStageAxialSpeedLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer16111211 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageAxialSpeed 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageAxialSpeedSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Sphere Radius: 0 0 1 ParticleEditorStageSphereRadiusLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer161112111 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageSphereRadius 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageSphereRadiusSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Cylinder Size X: 0 0 1 ParticleEditorStageCylSizeXLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer1611111311 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageCylSizeX 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageCylSizeXSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Cylinder Size Y: 0 0 1 ParticleEditorStageCylSizeYLabel 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer16111111212 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageCylSizeY 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageCylSizeYSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Cylinder Size Z: 0 0 1 ParticleEditorStageCylSizeZLabel 1 protected 1 Resizable 1 0 -1 0 wxEXPAND 1 bSizer161111112111 wxHORIZONTAL none 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ParticleEditorStageCylSizeZ 1 protected 1 Resizable 1 70,-1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 100 0 0 0 1 ParticleEditorStageCylSizeZSlider 1 protected 1 Resizable 1 wxSL_HORIZONTAL 0 wxFILTER_NONE wxDefaultValidator 50 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ParticleEditorPreviewPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer137 wxVERTICAL none 12 wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT 0 bSizer3 wxHORIZONTAL none 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Note: changes will be written to the file ..... 0 0 1 ParticleEditorSaveNote 1 protected 1 Resizable 1 0 -1 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_CLOSE Close 0 0 1 ParticleEditorCloseButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/particleeditor.xrc000066400000000000000000003437261321750546400215430ustar00rootroot00000000000000 800,665 wxVERTICAL wxALL|wxEXPAND 12 0 0 0 vertical 250,-1 wxVERTICAL wxBOTTOM 6 -1 wxEXPAND|wxLEFT 12 wxHORIZONTAL wxEXPAND | wxALL 5 wxVERTICAL wxEXPAND|wxLEFT 6 wxVERTICAL wxALL 0 0 wxTOP 6 0 wxTOP 6 0 wxBOTTOM|wxTOP 6 -1 wxEXPAND | wxALL 0 wxHORIZONTAL wxEXPAND|wxLEFT 12 wxVERTICAL wxEXPAND|wxLEFT 6 wxVERTICAL wxBOTTOM|wxEXPAND 6 wxHORIZONTAL wxRIGHT 6 wxVERTICAL wxALL|wxEXPAND 0 0 wxTOP|wxEXPAND 6 0 wxTOP|wxEXPAND 6 0 0 wxVERTICAL wxALL|wxEXPAND 0 0 wxTOP|wxEXPAND 6 0 wxTOP|wxEXPAND 6 0 wxALIGN_RIGHT 0 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALL 0 80,-1 0 0 10 wxBOTTOM 12 -1 wxEXPAND|wxLEFT 12 0 wxVERTICAL wxALL|wxEXPAND 6 7 2 6 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 wxALL|wxALIGN_CENTER_VERTICAL 5 0 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL|wxRIGHT 6 60,-1 0 0 9999 wxLEFT|wxALIGN_CENTER_VERTICAL 6 -1 wxALL 0 60,-1 0 0 10 wxALIGN_CENTER_VERTICAL|wxLEFT 6 -1 1 wxVERTICAL wxALL|wxEXPAND 6 7 2 6 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 999 wxALIGN_CENTER_VERTICAL 5 0 0 999 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 999 wxALIGN_CENTER_VERTICAL 5 1 1 999 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 0 wxVERTICAL wxALL|wxEXPAND 6 7 2 6 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 6 50,-1 0 0 10 wxALIGN_CENTER_VERTICAL 0 50 0 100 wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 5 50,-1 0 0 10 wxALIGN_CENTER_VERTICAL 0 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 6 50,-1 0 0 10 wxALIGN_CENTER_VERTICAL 0 50 0 100 wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 5 50,-1 0 0 10 wxALIGN_CENTER_VERTICAL 0 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 6 50,-1 0 0 10 wxALIGN_CENTER_VERTICAL 0 50 0 100 wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 5 50,-1 0 0 10 wxALIGN_CENTER_VERTICAL 0 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 6 50,-1 0 0 10 wxALIGN_CENTER_VERTICAL 0 50 0 100 wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 5 50,-1 0 0 10 wxALIGN_CENTER_VERTICAL 0 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxLEFT|wxALIGN_CENTER_VERTICAL 6 0 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 0 wxVERTICAL wxALL|wxEXPAND 6 7 2 6 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT 3 0 wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT 3 0 wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM 3 0 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxALIGN_CENTER_VERTICAL 5 -1 wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM 3 0 0 wxVERTICAL wxALL|wxEXPAND 6 7 2 6 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT 3 0 wxALIGN_CENTER_VERTICAL|wxALL 3 0 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALL|wxBOTTOM|wxRIGHT|wxTOP|wxALIGN_CENTER_VERTICAL 3 0 wxALL|wxALIGN_CENTER_VERTICAL 3 0 wxALL|wxALIGN_CENTER_VERTICAL 3 0 wxALL|wxALIGN_CENTER_VERTICAL 3 0 wxALL|wxALIGN_CENTER_VERTICAL 3 0 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 500 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 0 wxVERTICAL wxALL|wxEXPAND 6 7 2 6 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT 3 0 wxALIGN_CENTER_VERTICAL|wxALL 3 0 wxALL 3 0 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 5 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxALIGN_CENTER_VERTICAL 5 -1 wxEXPAND 0 wxHORIZONTAL wxRIGHT|wxALIGN_CENTER_VERTICAL 6 70,-1 0 0 10 wxALIGN_CENTER_VERTICAL 5 50 0 100 wxVERTICAL wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT 12 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 5 -1 wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/patchcreatedialog.fbp000066400000000000000000001251671321750546400221440ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject4 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY PatchCreatePanel 200,110 wxTAB_TRAVERSAL bSizer25 wxVERTICAL none 12 wxBOTTOM|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Create simple Patch Mesh 0 0 1 PatchCreateTopLabel 1 protected 1 Resizable 1 0 -1 6 wxLEFT 0 bSizer26 wxVERTICAL none 12 wxBOTTOM|wxEXPAND 0 2 wxBOTH 12 fgSizer3 wxFLEX_GROWMODE_SPECIFIED none 2 6 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Width: 0 0 1 m_staticText15 1 protected 1 Resizable 1 0 -1 0 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 PatchCreateWidthChoice 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Height: 0 0 1 m_staticText16 1 protected 1 Resizable 1 0 -1 0 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 PatchCreateHeightChoice 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Remove selected Brush 0 0 1 PatchCreateRemoveSelectedBrush 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/patchcreatedialog.xrc000066400000000000000000000047111321750546400221600ustar00rootroot00000000000000 200,110 wxVERTICAL wxBOTTOM|wxRIGHT 12 -1 wxLEFT 6 wxVERTICAL wxBOTTOM|wxEXPAND 12 2 2 6 12 wxALIGN_CENTER_VERTICAL 0 -1 wxALL 0 0 wxALIGN_CENTER_VERTICAL 0 -1 0 0 wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/patchinspector.fbp000066400000000000000000003514331321750546400215240ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject3 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY PatchInspectorMainPanel 219,285 wxTAB_TRAVERSAL bSizer23 wxVERTICAL none 12 wxALL|wxEXPAND 1 bSizer13 wxVERTICAL none 0 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 PatchInspectorVertexPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer16 wxVERTICAL none 6 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Patch Control Vertices 0 0 1 PatchInspectorVertexLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT 0 2 wxBOTH 1 12 fgSizer1 wxFLEX_GROWMODE_SPECIFIED none 2 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Row: 0 0 1 m_staticText8 1 protected 1 Resizable 1 0 -1 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 "0" 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 PatchInspectorControlRow 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Column: 0 0 1 m_staticText9 1 protected 1 Resizable 1 0 -1 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 "0" 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 PatchInspectorControlColumn 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator 12 wxEXPAND|wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 PatchInspectorCoordPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer15 wxVERTICAL none 8 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Coordinates 0 0 1 PatchInspectorCoordLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 PatchInspectorTessPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer20 wxVERTICAL none 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Patch Tesselation 0 0 1 PatchInspectorTessLabel 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer14 wxVERTICAL none 6 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Fixed Subdivisions 0 0 1 PatchInspectorFixedSubdivisions 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND|wxTOP 0 2 wxBOTH 1 12 fgSizer2 wxFLEX_GROWMODE_SPECIFIED none 2 6 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Horizontal: 0 0 1 PatchInspectorSubdivisionsXLabel 1 protected 1 Resizable 1 0 -1 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 1 32 0 1 0 1 PatchInspectorSubdivisionsX 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Vertical: 0 0 1 PatchInspectorSubdivisionsYLabel 1 protected 1 Resizable 1 0 -1 0 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 1 32 0 1 0 1 PatchInspectorSubdivisionsY 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 DarkRadiant-2.5.0/install/ui/patchinspector.xrc000066400000000000000000000150351321750546400215440ustar00rootroot00000000000000 219,285 wxVERTICAL wxALL|wxEXPAND 12 wxVERTICAL wxEXPAND 0 wxVERTICAL wxBOTTOM 6 -1 wxEXPAND|wxLEFT|wxRIGHT 12 2 2 0 12 1 wxALIGN_CENTER_VERTICAL 5 -1 wxALL|wxEXPAND 5 0 0 wxALIGN_CENTER_VERTICAL 0 -1 wxALL|wxEXPAND 5 0 0 wxEXPAND|wxLEFT|wxRIGHT 12 wxVERTICAL wxBOTTOM|wxTOP 8 -1 wxEXPAND|wxLEFT|wxRIGHT 12 wxVERTICAL wxBOTTOM|wxTOP 6 -1 wxEXPAND 5 wxVERTICAL wxBOTTOM|wxTOP 6 0 wxBOTTOM|wxEXPAND|wxTOP 6 2 2 6 12 1 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxALIGN_CENTER_VERTICAL 0 1 1 32 wxALIGN_CENTER_VERTICAL 0 -1 0 1 1 32 DarkRadiant-2.5.0/install/ui/patchthickendialog.fbp000066400000000000000000001527371321750546400223310ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ThickenDialogMainPanel 300,185 wxTAB_TRAVERSAL bSizer9 wxVERTICAL none 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Thicken selected Patches 0 0 1 ThickenDialogTopLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 0 bSizer10 wxVERTICAL none 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Extrude along Vertex Normals 0 0 1 ThickenDialogExtrudeAlongNormals 1 protected 1 Resizable 1 wxRB_GROUP 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Extrude along X-Axis 0 0 1 ThickenDialogExtrudeAlongX 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Extrude along Y-Axis 0 0 1 ThickenDialogExtrudeAlongY 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Extrude along Z-Axis 0 0 1 ThickenDialogExtrudeAlongZ 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 12 wxEXPAND|wxLEFT|wxRIGHT 0 bSizer11 wxHORIZONTAL none 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Thickness (units): 0 0 1 m_staticText6 1 protected 1 Resizable 1 0 -1 5 wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ThickenDialogThickness 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 16 6 wxBOTTOM|wxTOP 1 bSizer12 wxVERTICAL none 12 wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Create Seams ("side walls") 0 0 1 ThickenDialogCreateSeams 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/patchthickendialog.xrc000066400000000000000000000061271321750546400223450ustar00rootroot00000000000000 300,185 wxVERTICAL wxALL 5 -1 wxEXPAND|wxLEFT 12 wxVERTICAL wxALL 5 0 wxALL 5 0 wxALL 5 0 wxALL 5 0 wxEXPAND|wxLEFT|wxRIGHT 12 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 0 -1 wxALL 5 16 wxBOTTOM|wxTOP 6 wxVERTICAL wxLEFT|wxRIGHT 12 1 DarkRadiant-2.5.0/install/ui/readableeditor.fbp000066400000000000000000013115561321750546400214470ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ReadableEditorMainPanel 850,600 wxTAB_TRAVERSAL bSizer3 wxVERTICAL none 12 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 450 0 1 ReadableEditorSplitter 1 protected 1 Resizable 1 350 -1 1 wxSPLIT_VERTICAL wxSP_3D|wxSP_LIVE_UPDATE 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel1 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer9 wxVERTICAL none 6 wxEXPAND|wxRIGHT 1 bSizer4 wxVERTICAL none 12 wxBOTTOM 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY General Properties 0 0 1 ReadableEditorGeneralLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 0 2 wxBOTH 1 12 fgSizer1 wxFLEX_GROWMODE_SPECIFIED none 4 6 6 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Inventory Name: 0 0 1 m_staticText2 1 protected 1 Resizable 1 0 -1 0 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ReadableEditorInventoryName 1 protected 1 Resizable 1 wxTE_PROCESS_ENTER|wxTE_PROCESS_TAB 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY XData Name: 0 0 1 m_staticText3 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer5 wxHORIZONTAL none 0 wxALIGN_CENTER_VERTICAL|wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ReadableEditorXDataName 1 protected 1 Resizable 1 wxTE_PROCESS_ENTER|wxTE_PROCESS_TAB 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 Load From Art Provider; wxART_FILE_OPEN; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY MyButton 0 0 1 ReadableEditorXDBrowseButton 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Number of Pages: 0 0 1 m_staticText4 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 bSizer6 wxHORIZONTAL none 0 wxALIGN_CENTER_VERTICAL|wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 10 0 0 0 1 ReadableEditorNumPages 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Layout: 0 0 1 m_staticText6 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY One-sided 0 0 1 ReadableEditorOneSided 1 protected 1 Resizable 1 wxRB_GROUP 0 wxFILTER_NONE wxDefaultValidator 1 6 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Two-sided 0 0 1 ReadableEditorTwoSided 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Pageturn Sound: 0 0 1 m_staticText5 1 protected 1 Resizable 1 0 -1 0 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ReadableEditorPageTurnSound 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 12 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Page Editing 0 0 1 ReadableEditorPageLabel 1 protected 1 Resizable 1 0 -1 12 wxEXPAND|wxLEFT 1 bSizer7 wxVERTICAL none 12 wxBOTTOM|wxEXPAND 0 bSizer8 wxHORIZONTAL none 6 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_INSERT Insert Page 0 0 1 ReadableEditorInsertPage 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 Load From Art Provider; wxART_GOTO_FIRST; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY MyButton 0 0 1 ReadableEditorGotoFirstPage 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 Load From Art Provider; wxART_GO_BACK; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY MyButton 0 0 1 ReadableEditorGotoPreviousPage 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxALL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Current Page: 0 0 1 m_staticText15 1 protected 1 Resizable 1 0 -1 0 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 1 0 0 1 ReadableEditorCurPage 1 protected 1 Resizable 1 0 -1 6 wxLEFT 0 1 1 1 1 Load From Art Provider; wxART_GO_FORWARD; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY MyButton 0 0 1 ReadableEditorGotoNextPage 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 Load From Art Provider; wxART_GOTO_LAST; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY MyButton 0 0 1 ReadableEditorGotoLastPage 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_DELETE Remove Page 0 0 1 ReadableEditorDeletePage 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 bSizer71 wxHORIZONTAL none 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY GUI Definition: 0 0 1 m_staticText9 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL|wxLEFT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ReadableEditorGuiDefinition 1 protected 1 Resizable 1 wxTE_PROCESS_ENTER|wxTE_PROCESS_TAB 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 Load From Art Provider; wxART_FILE_OPEN; 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY MyButton 0 0 1 ReadableEditorGuiBrowseButton 1 protected 1 Resizable 1 wxBU_AUTODRAW 0 wxFILTER_NONE wxDefaultValidator 0 wxEXPAND 1 3 wxBOTH 1,2 2 12 fgSizer2 wxFLEX_GROWMODE_SPECIFIED none 3 6 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_staticText10 1 protected 1 Resizable 1 0 -1 6 wxALIGN_BOTTOM|wxALIGN_CENTER_HORIZONTAL|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Left 0 0 1 ReadableEditorPageLeftLabel 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_HORIZONTAL|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Right 0 0 1 ReadableEditorPageRightLabel 1 protected 1 Resizable 1 0 -1 0 wxALIGN_CENTER|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Title 0 0 1 m_staticText13 1 protected 1 Resizable 1 0 -1 0 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ReadableEditorTitleLeft 1 protected 1 Resizable 1 -1,70 wxTE_MULTILINE|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator 0 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ReadableEditorTitleRight 1 protected 1 Resizable 1 -1,70 wxTE_MULTILINE|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Body 0 0 1 m_staticText14 1 protected 1 Resizable 1 0 -1 0 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ReadableEditorBodyLeft 1 protected 1 Resizable 1 -1,-1 wxTE_MULTILINE|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator 0 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ReadableEditorBodyRight 1 protected 1 Resizable 1 wxTE_MULTILINE|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ReadableEditorPreviewPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer13 wxVERTICAL none 12 wxALIGN_RIGHT|wxALL 0 bSizer12 wxHORIZONTAL none 24 wxRIGHT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Tools 0 0 1 ReadableEditorTools 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 ReadableEditorCancel 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Save 0 0 1 ReadableEditorSave 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Save and Close 0 0 1 ReadableEditorSaveAndClose 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/readableeditor.xrc000066400000000000000000000414021321750546400214610ustar00rootroot00000000000000 850,600 wxVERTICAL wxALL|wxEXPAND 12 350 1 450 vertical wxVERTICAL wxEXPAND|wxRIGHT 6 wxVERTICAL wxBOTTOM 12 -1 wxEXPAND|wxLEFT 12 4 2 6 12 1 wxALIGN_CENTER_VERTICAL 6 -1 wxALL|wxEXPAND 0 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxALL 0 wxLEFT 6 undefined.png 0 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxALL 0 0 0 10 wxALIGN_CENTER_VERTICAL|wxLEFT 6 -1 wxALIGN_CENTER_VERTICAL|wxLEFT 6 1 wxALIGN_CENTER_VERTICAL 6 0 wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxALL|wxEXPAND 0 wxBOTTOM|wxTOP 12 -1 wxEXPAND|wxLEFT 12 wxVERTICAL wxBOTTOM|wxEXPAND 12 wxHORIZONTAL 6 0 wxLEFT 6 undefined.png 0 wxLEFT 6 undefined.png 0 wxALIGN_CENTER_VERTICAL|wxALL|wxLEFT 6 -1 wxALIGN_CENTER_VERTICAL|wxLEFT 0 -1 wxLEFT 6 undefined.png 0 wxLEFT 6 undefined.png 0 wxLEFT 6 0 wxBOTTOM|wxEXPAND 6 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxALL 0 -1 wxALIGN_CENTER_VERTICAL|wxLEFT 6 wxALIGN_CENTER_VERTICAL|wxLEFT 6 undefined.png 0 wxEXPAND 0 3 3 6 12 1,2 2 wxALL 5 -1 wxALIGN_BOTTOM|wxALIGN_CENTER_HORIZONTAL|wxTOP 6 -1 wxALIGN_CENTER_HORIZONTAL|wxTOP 6 -1 wxALIGN_CENTER|wxALL 0 -1 wxALL|wxEXPAND 0 -1,70 wxALL|wxEXPAND 0 -1,70 wxALIGN_CENTER|wxALL 0 -1 wxALL|wxEXPAND 0 wxALL|wxEXPAND 0 wxVERTICAL wxALIGN_RIGHT|wxALL 12 wxHORIZONTAL wxRIGHT 24 0 wxLEFT 6 0 wxLEFT 6 0 wxLEFT 6 0 DarkRadiant-2.5.0/install/ui/renderpreview.fbp000066400000000000000000001232731321750546400213560ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject1 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY RenderPreviewPanel 500,300 wxTAB_TRAVERSAL RenderPreviewSizer wxVERTICAL none 0 wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT|wxTOP 0 bSizer2 wxHORIZONTAL none 3 wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 RenderPreviewAnimToolbar 1 1 protected 1 Resizable 5 1 wxTB_HORIZONTAL|wxTB_NODIVIDER 0 Load From Art Provider; darkradiant:media-playback-start-ltr.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_NORMAL startTimeButton startTimeButton protected Start Render Time Load From Art Provider; darkradiant:media-playback-pause.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_NORMAL pauseTimeButton pauseTimeButton protected Pause Render Time Load From Art Provider; darkradiant:media-playback-stop.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_NORMAL stopTimeButton stopTimeButton protected Stop Render Time protected Load From Art Provider; wxART_GO_BACK; wxART_TOOLBAR 0 wxID_ANY wxITEM_NORMAL prevButton prevButton protected Previous Frame Load From Art Provider; wxART_GO_FORWARD; wxART_TOOLBAR 0 wxID_ANY wxITEM_NORMAL nextButton nextButton protected Next Frame 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 RenderPreviewFilterToolbar 1 1 protected 1 Resizable 5 1 wxTB_HORIZONTAL|wxTB_HORZ_TEXT|wxTB_NODIVIDER 0 3 wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP 0 1 1 1 1 20,20 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 RenderPreviewRenderModeToolbar 1 1 protected 1 Resizable 5 1 wxTB_HORIZONTAL|wxTB_NODIVIDER 0 Load From Art Provider; darkradiant:textureMode16.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_RADIO texturedModeButton texturedModeButton protected Switch to Textured Mode Load From Art Provider; darkradiant:lightingMode.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_RADIO lightingModeButton lightingModeButton protected Switch to Lighting Mode 3 wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP 0 1 1 1 1 20,20 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 RenderPreviewUtilToolbar 1 1 protected 1 Resizable 5 1 wxTB_HORIZONTAL|wxTB_NODIVIDER 0 Load From Art Provider; darkradiant:grid_toggle.png; wxART_TOOLBAR 0 wxID_ANY wxITEM_CHECK gridButton gridButton protected Toggle a 16 unit wide Grid DarkRadiant-2.5.0/install/ui/renderpreview.xrc000066400000000000000000000110041321750546400213670ustar00rootroot00000000000000 500,300 wxVERTICAL wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT|wxTOP 0 wxHORIZONTAL wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP 3 1 5 Start Render Time undefined.png Pause Render Time undefined.png Stop Render Time undefined.png Previous Frame undefined.png Next Frame undefined.png wxEXPAND 5 1 5 wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP 3 20,20 1 5 Switch to Textured Mode undefined.png 1 Switch to Lighting Mode undefined.png 1 wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP 3 20,20 1 5 Toggle a 16 unit wide Grid undefined.png 1 DarkRadiant-2.5.0/install/ui/responseeditor.fbp000066400000000000000000001634771321750546400215540ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY ResponseEditorMainPanel 500,416 wxTAB_TRAVERSAL bSizer18 wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 bSizer19 wxHORIZONTAL none 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Type: 0 0 1 m_staticText15 1 protected 1 Resizable 1 0 -1 12 wxALIGN_CENTER_VERTICAL|wxLEFT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ResponseEditorTypeCombo 1 protected 1 Resizable -1 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP|wxBOTTOM 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Active 0 0 1 ResponseEditorActive 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 4 wxEXPAND|wxTOP 0 2 wxBOTH 1 12 fgSizer4 wxFLEX_GROWMODE_SPECIFIED none 10 6 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Chance: 0 0 1 ResponseEditorChance 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ResponseEditorChanceValuePanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer35 wxVERTICAL none 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Random Effects: 0 0 1 ResponseEditorRandomFX 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ResponseEditorRandomFXValue 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 10 wxBOTTOM|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Response Effects 0 0 1 ResponseEditorFXLabel 1 protected 1 Resizable 1 0 -1 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 ResponseEditorFXPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer4 wxVERTICAL none DarkRadiant-2.5.0/install/ui/responseeditor.xrc000066400000000000000000000064511321750546400215650ustar00rootroot00000000000000 500,416 wxVERTICAL wxBOTTOM|wxEXPAND 6 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 0 -1 wxALIGN_CENTER_VERTICAL|wxLEFT 12 wxTOP|wxBOTTOM 6 0 wxEXPAND|wxTOP 4 10 2 6 12 1 wxALIGN_CENTER_VERTICAL 5 0 wxEXPAND | wxALL 0 wxVERTICAL wxALIGN_CENTER_VERTICAL 0 0 wxALL|wxEXPAND 0 wxBOTTOM|wxTOP 10 -1 wxEXPAND | wxALL 0 wxVERTICAL DarkRadiant-2.5.0/install/ui/stimeditor.fbp000066400000000000000000011364441321750546400206650ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect 1000 none 0 MyProject2 . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY StimEditorMainPanel 500,416 wxTAB_TRAVERSAL bSizer18 wxVERTICAL none 6 wxBOTTOM|wxEXPAND 0 bSizer19 wxHORIZONTAL none 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Type: 0 0 1 m_staticText15 1 protected 1 Resizable 1 0 -1 12 wxALIGN_CENTER_VERTICAL|wxLEFT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 StimEditorTypeCombo 1 protected 1 Resizable -1 1 0 wxFILTER_NONE wxDefaultValidator 6 wxTOP|wxBOTTOM 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Active 0 0 1 StimEditorActive 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxBOTTOM|wxEXPAND 0 2 wxBOTH 1 12 fgSizer3 wxFLEX_GROWMODE_SPECIFIED none 2 6 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Activation Timer: 0 0 1 StimEditorActivationTimer 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 StimEditorActivationTimerPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer20 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxFIXED_MINSIZE|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 200 0 0 0 -1,-1 1 StimEditorAcivationTimerHour 1 protected 1 Resizable 1 60,-1 wxSP_ARROW_KEYS 0 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY h 0 0 1 m_staticText21 1 protected 1 Resizable 1 0 -1 6 wxRIGHT|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 59 0 0 0 -1,-1 1 StimEditorAcivationTimerMinute 1 protected 1 Resizable 1 60,-1 wxSP_ARROW_KEYS 0 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY m 0 0 1 m_staticText211 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 59 0 0 0 -1,-1 1 StimEditorAcivationTimerSecond 1 protected 1 Resizable 1 60,-1 wxSP_ARROW_KEYS 0 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY s 0 0 1 m_staticText212 1 protected 1 Resizable 1 0 -1 6 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 999 0 0 0 -1,-1 1 StimEditorAcivationTimerMS 1 protected 1 Resizable 1 60,-1 wxSP_ARROW_KEYS 0 6 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY ms 0 0 1 m_staticText213 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Timer restarts after firing 0 0 1 StimEditorTimerRestarts 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 StimEditorTimerRestartPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer21 wxHORIZONTAL none 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Timer reloads 0 0 1 StimEditorTimerReloads 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 1000 0 0 0 1 StimEditorTimerReloadsTimes 1 protected 1 Resizable 1 60,-1 wxSP_ARROW_KEYS 0 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY times 0 0 1 m_staticText30 1 protected 1 Resizable 1 0 -1 6 wxTOP|wxBOTTOM 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Timer waits for start (when disabled: starts at spawn time) 0 0 1 StimEditorTimerWaitsForStart 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 4 wxEXPAND|wxTOP 0 2 wxBOTH 1 12 fgSizer4 wxFLEX_GROWMODE_SPECIFIED none 10 6 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Time interval: 0 0 1 StimEditorTimeInterval 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxEXPAND 1 bSizer241 wxHORIZONTAL none 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 9999999 0 0 0 1 StimEditorTimeIntervalValue 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY ms 0 0 1 StimEditorTimeIntervalUnitLabel 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Duration: 0 0 1 StimEditorDuration 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxEXPAND 1 bSizer24 wxHORIZONTAL none 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 9999999 0 0 0 1 StimEditorDurationValue 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 6 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY ms 0 0 1 StimEditorDurationUnitLabel 1 protected 1 Resizable 1 0 -1 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Radius 0 0 1 StimEditorRadius 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxEXPAND 1 bSizer23 wxHORIZONTAL none 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 99999 0 0 0 1 StimEditorRadiusValue 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 12 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Use bounds 0 0 1 StimEditorRadiusUseBounds 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Radius changes over time to: 0 0 1 StimEditorRadiusChangesOverTime 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 99999 0 0 0 1 StimEditorRadiusChangesOverTimeValue 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Magnitude: 0 0 1 StimEditorMagnitude 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 StimEditorMagnitudePanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer28 wxHORIZONTAL none 5 wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 1 10000 0 0 0 1 StimEditorMagnitudeValue 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 12 wxALIGN_CENTER_VERTICAL|wxLEFT 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Falloff Exponent: 0 0 1 StimEditorMagnitudeFalloff 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Max Fire Count: 0 0 1 StimEditorMaxFireCount 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxEXPAND|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 1000000 0 0 0 1 StimEditorMaxFireCountValue 1 protected 1 Resizable 1 wxSP_ARROW_KEYS 0 5 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Chance: 0 0 1 StimEditorChance 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 StimEditorChanceValuePanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer35 wxVERTICAL none 0 wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Velocity: 0 0 1 StimEditorVelocity 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 StimEditorVelocityValue 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxALIGN_CENTER_VERTICAL|wxALL 0 1 1 1 1 1 0 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Bounds: 0 0 1 StimEditorBounds 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 0 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 StimEditorBoundsPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer29 wxHORIZONTAL none 6 wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Min: 0 0 1 m_staticText19 1 protected 1 Resizable 1 0 -1 6 wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 StimEditorBoundsMinValue 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 6 wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Max: 0 0 1 m_staticText20 1 protected 1 Resizable 1 0 -1 0 wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 StimEditorBoundsMaxValue 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator DarkRadiant-2.5.0/install/ui/stimeditor.xrc000066400000000000000000000374111321750546400207030ustar00rootroot00000000000000 500,416 wxVERTICAL wxBOTTOM|wxEXPAND 6 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 0 -1 wxALIGN_CENTER_VERTICAL|wxLEFT 12 wxTOP|wxBOTTOM 6 0 wxBOTTOM|wxEXPAND 6 2 2 6 12 1 wxALIGN_CENTER_VERTICAL 5 0 wxEXPAND 0 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxFIXED_MINSIZE|wxRIGHT 6 60,-1 0 0 200 wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxRIGHT|wxALIGN_CENTER_VERTICAL 6 60,-1 0 0 59 wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL|wxRIGHT 6 60,-1 0 0 59 wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxALIGN_CENTER_VERTICAL 6 60,-1 0 0 999 wxALL 6 -1 wxALIGN_CENTER_VERTICAL 5 0 wxEXPAND | wxALL 0 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 5 0 wxLEFT|wxRIGHT 6 60,-1 0 0 1000 wxALIGN_CENTER_VERTICAL|wxLEFT 6 -1 wxTOP|wxBOTTOM 6 0 wxEXPAND|wxTOP 4 10 2 6 12 1 wxALIGN_CENTER_VERTICAL 5 0 wxEXPAND 5 wxHORIZONTAL wxEXPAND 5 0 0 9999999 wxALIGN_CENTER_VERTICAL|wxLEFT 6 -1 wxALIGN_CENTER_VERTICAL 5 0 wxEXPAND 5 wxHORIZONTAL wxEXPAND 5 0 0 9999999 wxALIGN_CENTER_VERTICAL|wxLEFT 6 -1 wxALIGN_CENTER_VERTICAL 5 0 wxEXPAND 5 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 5 0 0 99999 wxALIGN_CENTER_VERTICAL|wxLEFT 12 0 wxALIGN_CENTER_VERTICAL|wxALL 0 0 wxEXPAND 5 0 0 99999 wxALIGN_CENTER_VERTICAL 5 0 wxEXPAND | wxALL 0 wxHORIZONTAL wxALIGN_CENTER_VERTICAL 5 1 0 10000 wxALIGN_CENTER_VERTICAL|wxLEFT 12 0 wxALIGN_CENTER_VERTICAL 5 0 wxEXPAND|wxALIGN_CENTER_VERTICAL 5 0 0 1000000 wxALIGN_CENTER_VERTICAL 5 0 wxEXPAND | wxALL 0 wxVERTICAL wxALIGN_CENTER_VERTICAL 0 0 wxALL|wxEXPAND 0 wxALIGN_CENTER_VERTICAL|wxALL 0 0 wxEXPAND | wxALL 0 wxHORIZONTAL wxALIGN_CENTER_VERTICAL|wxRIGHT 6 -1 wxRIGHT 6 wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT 6 -1 wxALL 0 DarkRadiant-2.5.0/install/user.xml000066400000000000000000000352171321750546400170670ustar00rootroot00000000000000 DarkRadiant-2.5.0/libs/000077500000000000000000000000001321750546400146425ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/BasicTexture2D.h000066400000000000000000000026061321750546400176070ustar00rootroot00000000000000#pragma once #include "Texture.h" /** * \brief * Implementation of Texture for a 2D image texture. */ class BasicTexture2D : public Texture { // The GL bind number for use in OpenGL calls GLuint texture_number; // Texture Dimensions std::size_t _width, _height; // Texture name std::string _name; public: // Constructor BasicTexture2D(GLuint texNum = 0, const std::string& name = "") : texture_number(texNum), _name(name) {} ~BasicTexture2D() { if (texture_number != 0) { // Remove this texture from openGL if it's still loaded glDeleteTextures(1, &texture_number); } } /** * \brief * Set the texture number. */ void setGLTexNum(GLuint texnum) { texture_number = texnum; } /** * \brief * Set the image width. */ void setWidth(std::size_t width) { _width = width; } /** * \brief * Set the image height. */ void setHeight(std::size_t height) { _height = height; } /* Texture interface */ std::string getName() const { return _name; } GLuint getGLTexNum() const { return texture_number; } std::size_t getWidth() const { return _width; } std::size_t getHeight() const { return _height; } }; // class Texture typedef std::shared_ptr BasicTexture2DPtr; DarkRadiant-2.5.0/libs/BasicUndoMemento.h000066400000000000000000000006221321750546400202070ustar00rootroot00000000000000#pragma once #include "iundo.h" namespace undo { /** * An UndoMemento implementation capable of holding a single * copyable object, which is stored by value. */ template class BasicUndoMemento : public IUndoMemento { Copyable _data; public: BasicUndoMemento(const Copyable& data) : _data(data) {} const Copyable& data() const { return _data; } }; } // namespace DarkRadiant-2.5.0/libs/DirectoryArchiveFile.h000066400000000000000000000016171321750546400210660ustar00rootroot00000000000000#pragma once #include "iarchive.h" #include "stream/FileInputStream.h" namespace archive { /// \brief An ArchiveFile which is stored as a single file on disk. class DirectoryArchiveFile : public ArchiveFile { private: std::string _name; stream::FileInputStream _istream; stream::FileInputStream::size_type _size; public: typedef stream::FileInputStream::size_type size_type; DirectoryArchiveFile(const std::string& name, const std::string& filename) : _name(name), _istream(filename) { if (!failed()) { _istream.seek(0, stream::FileInputStream::end); _size = _istream.tell(); _istream.seek(0); } else { _size = 0; } } bool failed() const { return _istream.failed(); } size_type size() const override { return _size; } const std::string& getName() const override { return _name; } InputStream& getInputStream() override { return _istream; } }; }DarkRadiant-2.5.0/libs/DirectoryArchiveTextFile.h000066400000000000000000000015141321750546400217270ustar00rootroot00000000000000#pragma once #include "iarchive.h" #include "stream/TextFileInputStream.h" namespace archive { /// \brief An ArchiveTextFile which is stored as a single file on disk. class DirectoryArchiveTextFile : public ArchiveTextFile { private: std::string _name; TextFileInputStream _inputStream; // Mod directory std::string _modName; public: DirectoryArchiveTextFile(const std::string& name, const std::string& modName, const std::string& filename) : _name(name), _inputStream(filename), _modName(modName) {} bool failed() const { return _inputStream.failed(); } const std::string& getName() const override { return _name; } TextInputStream& getInputStream() override { return _inputStream; } /** * Get mod directory. */ std::string getModName() const override { return _modName; } }; } DarkRadiant-2.5.0/libs/EventRateLimiter.h000066400000000000000000000034761321750546400202500ustar00rootroot00000000000000#pragma once #include /** * \brief * Helper class to restrict the frequency of an event, such as updating a GTK * dialog in response to rapidly-changing data. * * An EventRateLimiter provides a simple means for the frequency of an arbitrary * event to be limited. It provides a single method, readyForEvent(), which * returns a boolean indicating whether the event should be triggered or not. * The EventRateLimiter is initalised with a value indicating how often the * events may be triggered. * * Note that the EventRateLimiter has no knowledge of the underlying event, it * simply returns a value from the readyForEvent() function, assuming that the * calling code will fire the event if the return value is true. */ class EventRateLimiter { // Separation time provided at construction unsigned long _separationTime; // Last clock invocation clock_t _lastClock; public: /** * \brief * Construct an EventRateLimiter with the given event separation time. * * \param separationTime * Number of milliseconds that should elapse between subsequent events. */ EventRateLimiter(unsigned long separationTimeMillis) : _separationTime(separationTimeMillis), _lastClock(clock()) { } /** * \brief * Test whether the event is ready to be triggered. * * If the return value is false, the calling code should not trigger the * event. If the return value is true, the timer is reset and the event may * be triggered by the calling code. */ bool readyForEvent() { clock_t currentClk = clock(); float diffMillis = (currentClk - _lastClock) / (0.001f * CLOCKS_PER_SEC); if (diffMillis >= _separationTime) { _lastClock = currentClk; return true; } else { return false; } } }; DarkRadiant-2.5.0/libs/Makefile.am000066400000000000000000000000651321750546400166770ustar00rootroot00000000000000SUBDIRS = math xmlutil scene wxutil ddslib picomodel DarkRadiant-2.5.0/libs/ObservedSelectable.h000066400000000000000000000030121321750546400205440ustar00rootroot00000000000000#pragma once #include "iselectable.h" #include "iselection.h" namespace selection { /** * \brief * Implementation of the Selectable interface which invokes a user-specified * callback function when the selection state is changed. */ class ObservedSelectable : public ISelectable { // Callback to invoke on selection changed SelectionChangedSlot _onchanged; // Current selection state bool _selected; public: /** * \brief * Construct an ObservedSelectable with the given callback function. */ ObservedSelectable(const SelectionChangedSlot& onchanged) : _onchanged(onchanged), _selected(false) { } /** * \brief * Copy constructor. */ ObservedSelectable(const ObservedSelectable& other) : ISelectable(other), _onchanged(other._onchanged), _selected(false) { setSelected(other.isSelected()); } ObservedSelectable& operator=(const ObservedSelectable& other) { setSelected(other.isSelected()); return *this; } virtual ~ObservedSelectable() { setSelected(false); } /** * \brief * Set the selection state. */ virtual void setSelected(bool select) override { // Change state and invoke callback only if the new state is different // from the current state if (select ^ _selected) { _selected = select; if (_onchanged) { _onchanged(*this); } } } virtual bool isSelected() const override { return _selected; } }; } // namespace DarkRadiant-2.5.0/libs/ObservedUndoable.h000066400000000000000000000026611321750546400202430ustar00rootroot00000000000000#pragma once #include "iundo.h" #include "mapfile.h" #include #include "BasicUndoMemento.h" namespace undo { template class ObservedUndoable : public IUndoable { typedef std::function ImportCallback; Copyable& _object; ImportCallback _importCallback; IUndoStateSaver* _undoStateSaver; IMapFileChangeTracker* _changeTracker; public: ObservedUndoable(Copyable& object, const ImportCallback& importCallback) : _object(object), _importCallback(importCallback), _undoStateSaver(nullptr), _changeTracker(nullptr) {} IMapFileChangeTracker& getUndoChangeTracker() { return *_changeTracker; } void connectUndoSystem(IMapFileChangeTracker& changeTracker) { _changeTracker = &changeTracker; _undoStateSaver = GlobalUndoSystem().getStateSaver(*this, changeTracker); } void disconnectUndoSystem(IMapFileChangeTracker& map) { _undoStateSaver = nullptr; _changeTracker = nullptr; GlobalUndoSystem().releaseStateSaver(*this); } void save() { if (_undoStateSaver != nullptr) { _undoStateSaver->save(*this); } } IUndoMementoPtr exportState() const { return IUndoMementoPtr(new BasicUndoMemento(_object)); } void importState(const IUndoMementoPtr& state) { save(); _importCallback(std::static_pointer_cast >(state)->data()); } }; } // namespace DarkRadiant-2.5.0/libs/RGBAImage.h000066400000000000000000000044451321750546400165000ustar00rootroot00000000000000#pragma once #include "igl.h" #include "iimage.h" #include "BasicTexture2D.h" #include #include "util/Noncopyable.h" struct RGBAPixel { unsigned char red, green, blue, alpha; }; /** * An RGBA image represents a single-mipmap image with certain * dimensions. The memory for the actual pixelmap is allocated * and de-allocated automatically. */ class RGBAImage : public Image, public util::Noncopyable { public: RGBAPixel* pixels; std::size_t width; std::size_t height; RGBAImage(std::size_t _width, std::size_t _height) : pixels(new RGBAPixel[_width * _height]), width(_width), height(_height) {} ~RGBAImage() { delete[] pixels; } virtual byte* getMipMapPixels(std::size_t mipMapIndex) const { assert(mipMapIndex == 0); // only one mipmap is allowed here return reinterpret_cast(pixels); } virtual std::size_t getWidth(std::size_t mipMapIndex) const { assert(mipMapIndex == 0); // only one mipmap is allowed here return width; } virtual std::size_t getHeight(std::size_t mipMapIndex) const { assert(mipMapIndex == 0); // only one mipmap is allowed here return height; } /* BindableTexture implementation */ TexturePtr bindTexture(const std::string& name) const { GLuint textureNum; GlobalOpenGL().assertNoErrors(); // Allocate a new texture number and store it into the Texture structure glGenTextures(1, &textureNum); glBindTexture(GL_TEXTURE_2D, textureNum); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // Download the image to OpenGL gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, static_cast(getWidth(0)), static_cast(getHeight(0)), GL_RGBA, GL_UNSIGNED_BYTE, getMipMapPixels(0) ); // Un-bind the texture glBindTexture(GL_TEXTURE_2D, 0); // Construct texture object BasicTexture2DPtr tex2DObject(new BasicTexture2D(textureNum, name)); tex2DObject->setWidth(getWidth(0)); tex2DObject->setHeight(getHeight(0)); GlobalOpenGL().assertNoErrors(); return tex2DObject; } bool isPrecompressed() const { return false; // not compressed } }; typedef std::shared_ptr RGBAImagePtr; DarkRadiant-2.5.0/libs/RandomOrigin.h000066400000000000000000000014721321750546400174070ustar00rootroot00000000000000#pragma once #include "math/Vector3.h" #include #include /** * Utility class containing a method to generate a random vector within a * certain distance of the world origin. */ class RandomOrigin { public: /** * Generate a random vector within of the world origin, returning * a string formatted correctly as an "origin" key. */ static std::string generate(int maxDist) { // Generate three random numbers between 0 and maxDist float x = maxDist * (float(std::rand()) / float(RAND_MAX)); float y = maxDist * (float(std::rand()) / float(RAND_MAX)); float z = maxDist * (float(std::rand()) / float(RAND_MAX)); // Construct a vector and return the formatted string return string::to_string(Vector3(x, y, z)); } }; DarkRadiant-2.5.0/libs/SurfaceShader.h000066400000000000000000000117021321750546400175330ustar00rootroot00000000000000#pragma once #include #include "debugging/debugging.h" #include "moduleobservers.h" #include "util/Noncopyable.h" #include "irender.h" #include "shaderlib.h" /** * Encapsulates a GL ShaderPtr and keeps track whether this * shader is actually in use in the map or not. The shader * is captured and released based on whether there is a * shadersystem reference available. */ class SurfaceShader : public ModuleObserver, public util::Noncopyable { public: // Observer classes can be attached to SurfaceShader to get notified // on realisation/unrealisation class Observer { public: virtual ~Observer() {} virtual void realiseShader() = 0; virtual void unrealiseShader() = 0; }; private: // greebo: The name of the material std::string _materialName; RenderSystemPtr _renderSystem; ShaderPtr _glShader; // In-use flag bool _inUse; bool _realised; typedef std::set Observers; Observers _observers; public: // Constructor. The renderSystem reference will be kept internally as reference // The SurfaceShader will try to de-reference it when capturing shaders. SurfaceShader(const std::string& materialName, const RenderSystemPtr& renderSystem = RenderSystemPtr()) : _materialName(materialName), _renderSystem(renderSystem), _inUse(false), _realised(false) { captureShader(); } // Destructor virtual ~SurfaceShader() { releaseShader(); } /** * Indicates whether this Shader is actually in use in the scene or not. * The shader is not in use if the owning Patch resides on the UndoStack, forex. */ void setInUse(bool isUsed) { _inUse = isUsed; if (!_glShader) return; // Update the shader's use count if (_inUse) { _glShader->incrementUsed(); } else { _glShader->decrementUsed(); } } /** * \brief * Get the material name. */ const std::string& getMaterialName() const { return _materialName; } /** * \brief * Set the material name. */ void setMaterialName(const std::string& name) { // return, if the shader is the same as the currently used if (shader_equal(_materialName, name)) return; releaseShader(); _materialName = name; captureShader(); } /** * \brief * Return the Shader for rendering. */ const ShaderPtr& getGLShader() const { return _glShader; } // Return the dimensions of the editorimage of the contained material std::size_t getWidth() const { if (_realised) { return _glShader->getMaterial()->getEditorImage()->getWidth(); } return 1; } std::size_t getHeight() const { if (_realised) { return _glShader->getMaterial()->getEditorImage()->getHeight(); } return 1; } void setRenderSystem(const RenderSystemPtr& renderSystem) { _renderSystem = renderSystem; captureShader(); } void attachObserver(Observer& observer) { // Insert the observer into our observer set std::pair result = _observers.insert(&observer); ASSERT_MESSAGE(result.second, "SurfaceShader::attachObserver(): Observer already attached."); if (_realised) { observer.realiseShader(); } } void detachObserver(Observer& observer) { if (_realised) { observer.unrealiseShader(); } ASSERT_MESSAGE(_observers.find(&observer) != _observers.end(), "SurfaceShader::detachObserver(): Cannot detach non-existing observer."); // Remove after unrealising _observers.erase(&observer); } // ModuleObserver methods void realise() override { assert(!_realised); _realised = true; for (auto i : _observers) i->realiseShader(); } void unrealise() override { assert(_realised); for (auto i : _observers) i->unrealiseShader(); _realised = false; } private: // Shader capture and release void captureShader() { // Check if we have a rendersystem - can we capture already? if (_renderSystem) { releaseShader(); _glShader = _renderSystem->capture(_materialName); assert(_glShader); _glShader->attach(*this); if (_inUse) { _glShader->incrementUsed(); } } else { releaseShader(); } } void releaseShader() { if (_glShader) { if (_inUse) { _glShader->decrementUsed(); } _glShader->detach(*this); _glShader.reset(); } } }; DarkRadiant-2.5.0/libs/ThreadedDefLoader.h000066400000000000000000000051621321750546400203050ustar00rootroot00000000000000#pragma once #include #include namespace util { /** * Helper class used to asynchronically parse/load def files in a separate thread. * * The worker thread itself is ensured to be called in a thread-safe * way (to prevent the worker from being invoked twice). Subsequent calls to * get() or start() will not start the loader again, unless the reset() method * is called. * * Client code (even from multiple threads) can retrieve (and wait for) the result * by calling the get() method. */ template class ThreadedDefLoader { typedef std::function LoadFunction; LoadFunction _loadFunc; std::shared_future _result; std::mutex _mutex; bool _loadingStarted; public: ThreadedDefLoader(const LoadFunction& loadFunc) : _loadFunc(loadFunc), _loadingStarted(false) {} ~ThreadedDefLoader() { // wait for any worker thread to finish reset(); } // Starts the loader in the background. This can be called multiple // times from separate threads, the worker will only launched once and // cannot be started a second time unless reset() is called. void start() { ensureLoaderStarted(); } // Ensrues that the worker thread has been started and is done processing // This will block and wait for the worker execution before returning to the caller. void ensureFinished() { get(); } // Retrieve the result of the asynchronous worker function // This will block and wait for the result if worker has not been // run yet or in case it's still running ReturnType get() { // Make sure we already started the loader ensureLoaderStarted(); // Wait for the result or return if it's already done. return _result.get(); } // Resets the state of the loader to the state it had after construction. // If a background thread has been started, this will block and wait for it to finish. void reset() { std::lock_guard lock(_mutex); // Wait for any running thread to finish if (_loadingStarted) { _loadingStarted = false; if (_result.valid()) { _result.get(); } _result = std::shared_future(); } } private: void ensureLoaderStarted() { std::lock_guard lock(_mutex); if (!_loadingStarted) { _loadingStarted = true; _result = std::async(std::launch::async, _loadFunc); } } }; } DarkRadiant-2.5.0/libs/Transformable.h000066400000000000000000000126011321750546400176120ustar00rootroot00000000000000#pragma once #include "itransformable.h" #include "math/Matrix4.h" #include "math/Quaternion.h" const Vector3 c_translation_identity(0, 0, 0); const Quaternion c_rotation_identity(Quaternion::Identity()); const Vector3 c_scale_identity(1, 1, 1); /** * Base implementation of the ITransformable interface. */ class Transformable : public ITransformable { protected: // Flags to signal which type of transformation this is about enum TransformationType { NoTransform = 0, Translation = 1 << 0, Rotation = 1 << 1, Scale = 1 << 2, }; private: Vector3 _translation; Quaternion _rotation; Vector3 _scale; TransformModifierType _type; unsigned int _transformationType; public: Transformable() : _translation(c_translation_identity), _rotation(Quaternion::Identity()), _scale(c_scale_identity), _type(TRANSFORM_PRIMITIVE), _transformationType(NoTransform) {} void setType(TransformModifierType type) override { _type = type; } TransformModifierType getType() const { return _type; } void setTranslation(const Vector3& value) override { _translation = value; _transformationType |= Translation; _onTransformationChanged(); } void setRotation(const Quaternion& value) override { _rotation = value; _transformationType |= Rotation; _onTransformationChanged(); } void setRotation(const Quaternion& value, const Vector3& worldPivot, const Matrix4& localToWorld) override { // greebo: When rotating around a pivot, the operation can be split into a rotation // and a translation part. Calculate the translation part and apply it. // Translate the world pivot into local coordinates (we only care about the translation part) Vector3 localPivot = worldPivot - localToWorld.t().getVector3(); Matrix4 rotation = Matrix4::getRotationQuantised(value); // This is basically T = P - R*P Vector3 translation( localPivot.x() - rotation.xx()*localPivot.x() - rotation.yx()*localPivot.y() - rotation.zx()*localPivot.z(), localPivot.y() - rotation.xy()*localPivot.x() - rotation.yy()*localPivot.y() - rotation.zy()*localPivot.z(), localPivot.z() - rotation.xz()*localPivot.x() - rotation.yz()*localPivot.y() - rotation.zz()*localPivot.z() ); _translation = translation; _transformationType |= Translation; // Regardless of the pivot, the object rotates "by itself", so let's apply the rotation in any case _rotation = value; _transformationType |= Rotation; _onTransformationChanged(); } void setScale(const Vector3& value) override { _scale = value; _transformationType |= Scale; _onTransformationChanged(); } void freezeTransform() override { if (_translation != c_translation_identity || _rotation != c_rotation_identity || _scale != c_scale_identity) { _applyTransformation(); _translation = c_translation_identity; _rotation = c_rotation_identity; _scale = c_scale_identity; _transformationType = NoTransform; _onTransformationChanged(); } } /* greebo: This reverts the currently active transformation * by setting the scale/rotation/translation to identity. * It's enough to call _onTransformationChanged() as this * usually marks the node's geometry as "needs re-evaluation", * and during next rendering turn everything will be updated. */ void revertTransform() override { _translation = c_translation_identity; _rotation = c_rotation_identity; _scale = c_scale_identity; _transformationType = NoTransform; _onTransformationChanged(); } const Vector3& getTranslation() const { return _translation; } const Quaternion& getRotation() const { return _rotation; } const Vector3& getScale() const { return _scale; } Matrix4 calculateTransform() const { return getMatrixForComponents(getTranslation(), getRotation(), getScale()); } protected: /** * Returns a bitmask indicating which transformation classes we're dealing with * to allow subclasses to specialise their math on the requested transformation. * See TransformationType enum for bit values. */ unsigned int getTransformationType() const { return _transformationType; } /** * greebo: Signal method for subclasses. This gets called * as soon as anything (translation, scale, rotation) is changed. * * To be implemented by subclasses */ virtual void _onTransformationChanged() = 0; /** * greebo: Signal method to be implemented by subclasses. * Is invoked whenever the transformation is frozen. */ virtual void _applyTransformation() = 0; // For rotation around pivot points the code needs to know the object center // before the operation started. // Subclasses need to provide this information. virtual const Vector3& getUntransformedOrigin() override { static Vector3 center(0, 0, 0); return center; } private: static Matrix4 getMatrixForComponents(const Vector3& translation, const Quaternion& rotation, const Vector3& scale) { Matrix4 result(Matrix4::getRotationQuantised(rotation)); result.x().getVector3() *= scale.x(); result.y().getVector3() *= scale.y(); result.z().getVector3() *= scale.z(); result.tx() = translation.x(); result.ty() = translation.y(); result.tz() = translation.z(); return result; } }; DarkRadiant-2.5.0/libs/UndoFileChangeTracker.h000066400000000000000000000033061321750546400211440ustar00rootroot00000000000000#pragma once #include "iundo.h" #include "mapfile.h" #include "itextstream.h" #include #include class UndoFileChangeTracker : public UndoSystem::Tracker, public IMapFileChangeTracker { private: const std::size_t MAPFILE_MAX_CHANGES; std::size_t _size; std::size_t _saved; typedef void (UndoFileChangeTracker::*Pending)(); Pending _pending; std::function _changed; public: UndoFileChangeTracker() : MAPFILE_MAX_CHANGES(std::numeric_limits::max()), _size(0), _saved(MAPFILE_MAX_CHANGES), _pending(0) {} void print() { rMessage() << "saved: " << _saved << " size: " << _size << std::endl; } void push() { ++_size; _changed(); //print(); } void pop() { --_size; _changed(); //print(); } void pushOperation() { if (_size < _saved) { // redo queue has been flushed.. it is now impossible to get back to the saved state via undo/redo _saved = MAPFILE_MAX_CHANGES; } push(); } void clear() override { _size = 0; _changed(); //print(); } void begin() override { _pending = Pending(&UndoFileChangeTracker::pushOperation); } void undo() override { _pending = Pending(&UndoFileChangeTracker::pop); } void redo() override { _pending = Pending(&UndoFileChangeTracker::push); } void changed() override { if (_pending != 0) { ((*this).*_pending)(); _pending = 0; } } void save() override { _saved = _size; _changed(); } bool saved() const override { return _saved == _size; } void setChangedCallback(const std::function& changed) override { _changed = changed; _changed(); } std::size_t changes() const override { return _size; } }; DarkRadiant-2.5.0/libs/character.h000066400000000000000000000024101321750546400167440ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(INCLUDED_CHARACTER_H) #define INCLUDED_CHARACTER_H /// \file /// \brief Character encoding. /// \brief Returns true if \p c is an ASCII character that can be represented with 7 bits. inline bool char_is_ascii(char c) { return (c & 0x80) == 0; } /// \brief Returns true if \p string consists entirely of ASCII characters. inline bool string_is_ascii(const char* string) { while(*string != '\0') { if(!char_is_ascii(*string++)) { return false; } } return true; } #endif DarkRadiant-2.5.0/libs/ddslib.h000066400000000000000000000137551321750546400162670ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- DDS Library Based on code from Nvidia's DDS example: http://www.nvidia.com/object/dxtc_decompression_code.html Copyright (c) 2003 Randy Reddig All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #ifndef DDSLIB_H #define DDSLIB_H /* dependencies */ #include #include /* c++ marker */ #ifdef __cplusplus extern "C" { #endif /* dds definition */ typedef enum { DDS_PF_ARGB8888, DDS_PF_DXT1, DDS_PF_DXT2, DDS_PF_DXT3, DDS_PF_DXT4, DDS_PF_DXT5, DDS_PF_DXT5_RXGB, /* Doom 3's swizzled format */ DDS_PF_UNKNOWN } ddsPF_t; // greebo: General parameters which depend on the DDS format /*struct DDSLoadInfo { unsigned int divSize = 4; unsigned int blockBytes; GLenum internalFormat; }; DDSLoadInfo loadInfoDXT1 = { 8, GL_COMPRESSED_RGBA_S3TC_DXT1 }; DdsLoadInfo loadInfoDXT3 = { 16, GL_COMPRESSED_RGBA_S3TC_DXT3 }; DdsLoadInfo loadInfoDXT5 = { 16, GL_COMPRESSED_RGBA_S3TC_DXT5 };*/ /* 16bpp stuff */ #define DDS_LOW_5 0x001F; #define DDS_MID_6 0x07E0; #define DDS_HIGH_5 0xF800; #define DDS_MID_555 0x03E0; #define DDS_HI_555 0x7C00; /* structures */ typedef struct ddsColorKey_s { unsigned int colorSpaceLowValue; unsigned int colorSpaceHighValue; } ddsColorKey_t; typedef struct ddsCaps_s { unsigned int caps1; unsigned int caps2; unsigned int caps3; unsigned int caps4; } ddsCaps_t; typedef struct ddsMultiSampleCaps_s { unsigned short flipMSTypes; unsigned short bltMSTypes; } ddsMultiSampleCaps_t; typedef struct ddsPixelFormat_s { unsigned int size; unsigned int flags; unsigned char fourCC[4]; union { unsigned int rgbBitCount; unsigned int yuvBitCount; unsigned int zBufferBitDepth; unsigned int alphaBitDepth; unsigned int luminanceBitCount; unsigned int bumpBitCount; unsigned int privateFormatBitCount; }; union { unsigned int rBitMask; unsigned int yBitMask; unsigned int stencilBitDepth; unsigned int luminanceBitMask; unsigned int bumpDuBitMask; unsigned int operations; }; union { unsigned int gBitMask; unsigned int uBitMask; unsigned int zBitMask; unsigned int bumpDvBitMask; ddsMultiSampleCaps_t multiSampleCaps; }; union { unsigned int bBitMask; unsigned int vBitMask; unsigned int stencilBitMask; unsigned int bumpLuminanceBitMask; }; union { unsigned int rgbAlphaBitMask; unsigned int yuvAlphaBitMask; unsigned int luminanceAlphaBitMask; unsigned int rgbZBitMask; unsigned int yuvZBitMask; }; } ddsPixelFormat_t; // DDS header flags #define DDSD_CAPS 0x00000001 #define DDSD_HEIGHT 0x00000002 #define DDSD_WIDTH 0x00000004 #define DDSD_PITCH 0x00000008 #define DDSD_PIXELFORMAT 0x00001000 #define DDSD_MIPMAPCOUNT 0x00020000 #define DDSD_LINEARSIZE 0x00080000 #define DDSD_DEPTH 0x00800000 struct DDSHeader { /* magic: 'dds ' */ char magic[ 4 ]; /* directdraw surface */ unsigned int size; unsigned int flags; unsigned int height; unsigned int width; union { int pitch; unsigned int linearSize; }; unsigned int backBufferCount; union { unsigned int mipMapCount; unsigned int refreshRate; unsigned int srcVBHandle; }; unsigned int alphaBitDepth; unsigned int reserved; unsigned int surface; // greebo: Changed this to unsigned int for 64-bit compatibility (should be 32 bits wide) union { ddsColorKey_t ckDestOverlay; unsigned int emptyFaceColor; }; ddsColorKey_t ckDestBlt; ddsColorKey_t ckSrcOverlay; ddsColorKey_t ckSrcBlt; ddsPixelFormat_t pixelFormat; ddsCaps_t ddsCaps; unsigned int textureStage; }; typedef struct ddsBuffer_s { DDSHeader header; /* data (Varying size) */ unsigned char data[ 4 ]; } ddsBuffer_t; /** greebo: This represents a 64 bit DDS block containing * either the alpha or the colour data. */ typedef struct ddsColorBlock_s { unsigned short colors[ 2 ];// 32 bit (2 x 16 bit) unsigned char row[ 4 ]; // 32 bit (4 x 8 bit) } ddsColorBlock_t; typedef struct ddsAlphaBlockExplicit_s { unsigned short row[ 4 ]; } ddsAlphaBlockExplicit_t; typedef struct ddsAlphaBlock3BitLinear_s { unsigned char alpha0; unsigned char alpha1; unsigned char stuff[ 6 ]; } ddsAlphaBlock3BitLinear_t; typedef struct ddsColor_s { unsigned char r, g, b, a; } ddsColor_t; /* public functions */ int DDSGetInfo( const DDSHeader* header, int *width, int *height, ddsPF_t *pf ); int DDSDecompress( const DDSHeader* header, const unsigned char* buffer, unsigned char *pixels ); /* end marker */ #ifdef __cplusplus } #endif #endif DarkRadiant-2.5.0/libs/ddslib/000077500000000000000000000000001321750546400161035ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/ddslib/Makefile.am000066400000000000000000000002761321750546400201440ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs AM_CXXFLAGS = -fPIC pkglib_LTLIBRARIES = libdds.la libdds_la_LDFLAGS = -release @PACKAGE_VERSION@ libdds_la_SOURCES = ddslib.cpp DarkRadiant-2.5.0/libs/ddslib/ddslib.cpp000066400000000000000000000566331321750546400200650ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- DDS Library Based on code from Nvidia's DDS example: http://www.nvidia.com/object/dxtc_decompression_code.html Copyright (c) 2003 Randy Reddig All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define DDSLIB_C /* dependencies */ #include "ddslib.h" #if !defined(_MSC_VER) #include // greebo: This isn't needed in VC++ 2005 #endif /* endian tomfoolery */ typedef union { float f; char c[ 4 ]; } floatSwapUnion; #ifndef __BIG_ENDIAN__ #ifdef _SGI_SOURCE #define __BIG_ENDIAN__ #endif #endif #ifdef __BIG_ENDIAN__ int DDSBigLong( int src ) { return src; } short DDSBigShort( short src ) { return src; } float DDSBigFloat( float src ) { return src; } int DDSLittleLong( int src ) { return ((src & 0xFF000000) >> 24) | ((src & 0x00FF0000) >> 8) | ((src & 0x0000FF00) << 8) | ((src & 0x000000FF) << 24); } short DDSLittleShort( short src ) { return ((src & 0xFF00) >> 8) | ((src & 0x00FF) << 8); } float DDSLittleFloat( float src ) { floatSwapUnion in,out; in.f = src; out.c[ 0 ] = in.c[ 3 ]; out.c[ 1 ] = in.c[ 2 ]; out.c[ 2 ] = in.c[ 1 ]; out.c[ 3 ] = in.c[ 0 ]; return out.f; } #else /*__BIG_ENDIAN__*/ int DDSLittleLong( int src ) { return src; } short DDSLittleShort( short src ) { return src; } float DDSLittleFloat( float src ) { return src; } int DDSBigLong( int src ) { return ((src & 0xFF000000) >> 24) | ((src & 0x00FF0000) >> 8) | ((src & 0x0000FF00) << 8) | ((src & 0x000000FF) << 24); } short DDSBigShort( short src ) { return ((src & 0xFF00) >> 8) | ((src & 0x00FF) << 8); } float DDSBigFloat( float src ) { floatSwapUnion in,out; in.f = src; out.c[ 0 ] = in.c[ 3 ]; out.c[ 1 ] = in.c[ 2 ]; out.c[ 2 ] = in.c[ 1 ]; out.c[ 3 ] = in.c[ 0 ]; return out.f; } #endif /*__BIG_ENDIAN__*/ /* DDSDecodePixelFormat() determines which pixel format the dds texture is in */ static void DDSDecodePixelFormat( const DDSHeader* header, ddsPF_t *pf ) { unsigned char fourCC[4]; /* dummy check */ if( header == NULL || pf == NULL ) return; /* extract fourCC */ fourCC[0] = header->pixelFormat.fourCC[0]; fourCC[1] = header->pixelFormat.fourCC[1]; fourCC[2] = header->pixelFormat.fourCC[2]; fourCC[3] = header->pixelFormat.fourCC[3]; /* test it */ if (fourCC[0] == 0 && fourCC[1] == 0 && fourCC[2] == 0 && fourCC[3] == 0) *pf = DDS_PF_ARGB8888; else if (fourCC[0] == 'D' && fourCC[1] == 'X' && fourCC[2] == 'T' && fourCC[3] == '1') *pf = DDS_PF_DXT1; else if (fourCC[0] == 'D' && fourCC[1] == 'X' && fourCC[2] == 'T' && fourCC[3] == '2') *pf = DDS_PF_DXT2; else if (fourCC[0] == 'D' && fourCC[1] == 'X' && fourCC[2] == 'T' && fourCC[3] == '3') *pf = DDS_PF_DXT3; else if (fourCC[0] == 'D' && fourCC[1] == 'X' && fourCC[2] == 'T' && fourCC[3] == '4') *pf = DDS_PF_DXT4; else if (fourCC[0] == 'D' && fourCC[1] == 'X' && fourCC[2] == 'T' && fourCC[3] == '5') *pf = DDS_PF_DXT5; else if (fourCC[0] == 'R' && fourCC[1] == 'X' && fourCC[2] == 'G' && fourCC[3] == 'B') *pf = DDS_PF_DXT5_RXGB; else *pf = DDS_PF_UNKNOWN; } /* DDSGetInfo() extracts relevant info from a dds texture, returns 0 on success */ int DDSGetInfo(const DDSHeader* header, int *width, int *height, ddsPF_t *pf ) { /* dummy test */ if( header == NULL ) return -1; /* test dds header */ if( *((int*) header->magic) != *((int*) "DDS ") ) return -1; if( DDSLittleLong( header->size ) != 124 ) return -1; /* extract width and height */ if( width != NULL ) *width = DDSLittleLong( header->width ); if( height != NULL ) *height = DDSLittleLong( header->height ); /* get pixel format */ DDSDecodePixelFormat( header, pf ); /* return ok */ return 0; } /* DDSGetColorBlockColors() extracts colors from a dds color block */ static void DDSGetColorBlockColors( ddsColorBlock_t *block, ddsColor_t colors[ 4 ] ) { unsigned short word; /* color 0 */ word = DDSLittleShort( block->colors[ 0 ] ); colors[ 0 ].a = 0xff; /* extract rgb bits */ colors[ 0 ].b = (unsigned char) word; colors[ 0 ].b <<= 3; colors[ 0 ].b |= (colors[ 0 ].b >> 5); word >>= 5; colors[ 0 ].g = (unsigned char) word; colors[ 0 ].g <<= 2; colors[ 0 ].g |= (colors[ 0 ].g >> 6); word >>= 6; colors[ 0 ].r = (unsigned char) word; colors[ 0 ].r <<= 3; colors[ 0 ].r |= (colors[ 0 ].r >> 5); /* same for color 1 */ word = DDSLittleShort( block->colors[ 1 ] ); colors[ 1 ].a = 0xff; /* extract rgb bits */ colors[ 1 ].b = (unsigned char) word; colors[ 1 ].b <<= 3; colors[ 1 ].b |= (colors[ 1 ].b >> 5); word >>= 5; colors[ 1 ].g = (unsigned char) word; colors[ 1 ].g <<= 2; colors[ 1 ].g |= (colors[ 1 ].g >> 6); word >>= 6; colors[ 1 ].r = (unsigned char) word; colors[ 1 ].r <<= 3; colors[ 1 ].r |= (colors[ 1 ].r >> 5); /* use this for all but the super-freak math method */ if( block->colors[ 0 ] > block->colors[ 1 ] ) { /* four-color block: derive the other two colors. 00 = color 0, 01 = color 1, 10 = color 2, 11 = color 3 these two bit codes correspond to the 2-bit fields stored in the 64-bit block. */ word = ((unsigned short) colors[ 0 ].r * 2 + (unsigned short) colors[ 1 ].r ) / 3; /* no +1 for rounding */ /* as bits have been shifted to 888 */ colors[ 2 ].r = (unsigned char) word; word = ((unsigned short) colors[ 0 ].g * 2 + (unsigned short) colors[ 1 ].g) / 3; colors[ 2 ].g = (unsigned char) word; word = ((unsigned short) colors[ 0 ].b * 2 + (unsigned short) colors[ 1 ].b) / 3; colors[ 2 ].b = (unsigned char) word; colors[ 2 ].a = 0xff; word = ((unsigned short) colors[ 0 ].r + (unsigned short) colors[ 1 ].r * 2) / 3; colors[ 3 ].r = (unsigned char) word; word = ((unsigned short) colors[ 0 ].g + (unsigned short) colors[ 1 ].g * 2) / 3; colors[ 3 ].g = (unsigned char) word; word = ((unsigned short) colors[ 0 ].b + (unsigned short) colors[ 1 ].b * 2) / 3; colors[ 3 ].b = (unsigned char) word; colors[ 3 ].a = 0xff; } else { /* three-color block: derive the other color. 00 = color 0, 01 = color 1, 10 = color 2, 11 = transparent. These two bit codes correspond to the 2-bit fields stored in the 64-bit block */ word = ((unsigned short) colors[ 0 ].r + (unsigned short) colors[ 1 ].r) / 2; colors[ 2 ].r = (unsigned char) word; word = ((unsigned short) colors[ 0 ].g + (unsigned short) colors[ 1 ].g) / 2; colors[ 2 ].g = (unsigned char) word; word = ((unsigned short) colors[ 0 ].b + (unsigned short) colors[ 1 ].b) / 2; colors[ 2 ].b = (unsigned char) word; colors[ 2 ].a = 0xff; /* random color to indicate alpha */ colors[ 3 ].r = 0x00; colors[ 3 ].g = 0xff; colors[ 3 ].b = 0xff; colors[ 3 ].a = 0x00; } } /* DDSDecodeColorBlock() decodes a dds color block fixme: make endian-safe */ static void DDSDecodeColorBlock( unsigned int *pixel, ddsColorBlock_t *block, int width, unsigned int colors[ 4 ] ) { int r, n; unsigned int bits; unsigned int masks[] = { 3, 12, 3 << 4, 3 << 6 }; /* bit masks = 00000011, 00001100, 00110000, 11000000 */ int shift[] = { 0, 2, 4, 6 }; /* r steps through lines in y */ for( r = 0; r < 4; r++, pixel += (width - 4) ) /* no width * 4 as unsigned int ptr inc will * 4 */ { /* width * 4 bytes per pixel per line, each j dxtc row is 4 lines of pixels */ /* n steps through pixels */ for( n = 0; n < 4; n++ ) { bits = block->row[ r ] & masks[ n ]; bits >>= shift[ n ]; *pixel = colors[bits]; pixel++; } } } /* DDSDecodeAlphaExplicit() decodes a dds explicit alpha block */ static void DDSDecodeAlphaExplicit( unsigned int *pixel, ddsAlphaBlockExplicit_t *alphaBlock, int width, unsigned int alphaZero ) { int row, pix; unsigned short word; ddsColor_t color; /* clear color */ color.r = 0; color.g = 0; color.b = 0; /* walk rows */ for( row = 0; row < 4; row++, pixel += (width - 4) ) { word = DDSLittleShort( alphaBlock->row[ row ] ); /* walk pixels */ for( pix = 0; pix < 4; pix++ ) { /* zero the alpha bits of image pixel */ *pixel &= alphaZero; color.a = word & 0x000F; color.a = color.a | (color.a << 4); *pixel |= *((unsigned int*) &color); word >>= 4; /* move next bits to lowest 4 */ pixel++; /* move to next pixel in the row */ } } } /* DDSDecodeAlpha3BitLinear() decodes interpolated alpha block */ static void DDSDecodeAlpha3BitLinear( unsigned int *pixel, ddsAlphaBlock3BitLinear_t *alphaBlock, int width, unsigned int alphaZero ) { int row, pix; unsigned int stuff; unsigned char bits[ 4 ][ 4 ]; unsigned short alphas[ 8 ]; ddsColor_t aColors[ 4 ][ 4 ]; /* get initial alphas */ alphas[ 0 ] = alphaBlock->alpha0; alphas[ 1 ] = alphaBlock->alpha1; /* 8-alpha block */ if( alphas[ 0 ] > alphas[ 1 ] ) { /* 000 = alpha_0, 001 = alpha_1, others are interpolated */ alphas[ 2 ] = ( 6 * alphas[ 0 ] + alphas[ 1 ]) / 7; /* bit code 010 */ alphas[ 3 ] = ( 5 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 7; /* bit code 011 */ alphas[ 4 ] = ( 4 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 7; /* bit code 100 */ alphas[ 5 ] = ( 3 * alphas[ 0 ] + 4 * alphas[ 1 ]) / 7; /* bit code 101 */ alphas[ 6 ] = ( 2 * alphas[ 0 ] + 5 * alphas[ 1 ]) / 7; /* bit code 110 */ alphas[ 7 ] = ( alphas[ 0 ] + 6 * alphas[ 1 ]) / 7; /* bit code 111 */ } /* 6-alpha block */ else { /* 000 = alpha_0, 001 = alpha_1, others are interpolated */ alphas[ 2 ] = (4 * alphas[ 0 ] + alphas[ 1 ]) / 5; /* bit code 010 */ alphas[ 3 ] = (3 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 5; /* bit code 011 */ alphas[ 4 ] = (2 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 5; /* bit code 100 */ alphas[ 5 ] = ( alphas[ 0 ] + 4 * alphas[ 1 ]) / 5; /* bit code 101 */ alphas[ 6 ] = 0; /* bit code 110 */ alphas[ 7 ] = 255; /* bit code 111 */ } /* decode 3-bit fields into array of 16 bytes with same value */ /* first two rows of 4 pixels each */ stuff = *((unsigned int*) &(alphaBlock->stuff[ 0 ])); bits[ 0 ][ 0 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 0 ][ 1 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 0 ][ 2 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 0 ][ 3 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 1 ][ 0 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 1 ][ 1 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 1 ][ 2 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 1 ][ 3 ] = (unsigned char) (stuff & 0x00000007); /* last two rows */ stuff = *((unsigned int*) &(alphaBlock->stuff[ 3 ])); /* last 3 bytes */ bits[ 2 ][ 0 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 2 ][ 1 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 2 ][ 2 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 2 ][ 3 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 3 ][ 0 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 3 ][ 1 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 3 ][ 2 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 3 ][ 3 ] = (unsigned char) (stuff & 0x00000007); /* decode the codes into alpha values */ for( row = 0; row < 4; row++ ) { for( pix=0; pix < 4; pix++ ) { aColors[ row ][ pix ].r = 0; aColors[ row ][ pix ].g = 0; aColors[ row ][ pix ].b = 0; aColors[ row ][ pix ].a = (unsigned char) alphas[ bits[ row ][ pix ] ]; } } /* write out alpha values to the image bits */ for( row = 0; row < 4; row++, pixel += width-4 ) { for( pix = 0; pix < 4; pix++ ) { /* zero the alpha bits of image pixel */ *pixel &= alphaZero; /* or the bits into the prev. nulled alpha */ *pixel |= *((unsigned int*) &(aColors[ row ][ pix ])); pixel++; } } } /** greebo: Decodes the alpha channel into the red channel for RXGB-encoded images */ static void DDSDecodeRXGBAlpha3BitLinear( unsigned int *pixel, ddsAlphaBlock3BitLinear_t *alphaBlock, int width, unsigned int redZero ) { int row, pix; unsigned int stuff; unsigned char bits[ 4 ][ 4 ]; unsigned short alphas[ 8 ]; ddsColor_t aColors[ 4 ][ 4 ]; /* get initial alphas */ alphas[ 0 ] = alphaBlock->alpha0; alphas[ 1 ] = alphaBlock->alpha1; /* 8-alpha block */ if( alphas[ 0 ] > alphas[ 1 ] ) { /* 000 = alpha_0, 001 = alpha_1, others are interpolated */ alphas[ 2 ] = ( 6 * alphas[ 0 ] + alphas[ 1 ]) / 7; /* bit code 010 */ alphas[ 3 ] = ( 5 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 7; /* bit code 011 */ alphas[ 4 ] = ( 4 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 7; /* bit code 100 */ alphas[ 5 ] = ( 3 * alphas[ 0 ] + 4 * alphas[ 1 ]) / 7; /* bit code 101 */ alphas[ 6 ] = ( 2 * alphas[ 0 ] + 5 * alphas[ 1 ]) / 7; /* bit code 110 */ alphas[ 7 ] = ( alphas[ 0 ] + 6 * alphas[ 1 ]) / 7; /* bit code 111 */ } /* 6-alpha block */ else { /* 000 = alpha_0, 001 = alpha_1, others are interpolated */ alphas[ 2 ] = (4 * alphas[ 0 ] + alphas[ 1 ]) / 5; /* bit code 010 */ alphas[ 3 ] = (3 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 5; /* bit code 011 */ alphas[ 4 ] = (2 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 5; /* bit code 100 */ alphas[ 5 ] = ( alphas[ 0 ] + 4 * alphas[ 1 ]) / 5; /* bit code 101 */ alphas[ 6 ] = 0; /* bit code 110 */ alphas[ 7 ] = 255; /* bit code 111 */ } /* decode 3-bit fields into array of 16 bytes with same value */ /* first two rows of 4 pixels each */ stuff = *((unsigned int*) &(alphaBlock->stuff[ 0 ])); bits[ 0 ][ 0 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 0 ][ 1 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 0 ][ 2 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 0 ][ 3 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 1 ][ 0 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 1 ][ 1 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 1 ][ 2 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 1 ][ 3 ] = (unsigned char) (stuff & 0x00000007); /* last two rows */ stuff = *((unsigned int*) &(alphaBlock->stuff[ 3 ])); /* last 3 bytes */ bits[ 2 ][ 0 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 2 ][ 1 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 2 ][ 2 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 2 ][ 3 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 3 ][ 0 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 3 ][ 1 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 3 ][ 2 ] = (unsigned char) (stuff & 0x00000007); stuff >>= 3; bits[ 3 ][ 3 ] = (unsigned char) (stuff & 0x00000007); /* decode the codes into red values */ for( row = 0; row < 4; row++ ) { for( pix=0; pix < 4; pix++ ) { aColors[ row ][ pix ].a = 0; aColors[ row ][ pix ].g = 0; aColors[ row ][ pix ].b = 0; aColors[ row ][ pix ].r = (unsigned char) alphas[ bits[ row ][ pix ] ]; } } /* write out alpha values to the image bits */ for( row = 0; row < 4; row++, pixel += width-4 ) { for( pix = 0; pix < 4; pix++ ) { /* zero the red bits of image pixel */ *pixel &= redZero; /* or the bits into the prev. nulled red */ *pixel |= *((unsigned int*) &(aColors[ row ][ pix ])); pixel++; } } } /* DDSDecompressDXT1() decompresses a dxt1 format texture */ static int DDSDecompressDXT1( const unsigned char* buffer, int width, int height, unsigned char *pixels ) { int x, y, xBlocks, yBlocks; unsigned int *pixel; ddsColorBlock_t *block; ddsColor_t colors[ 4 ]; /* setup */ xBlocks = width / 4; yBlocks = height / 4; /* walk y */ for( y = 0; y < yBlocks; y++ ) { /* 8 bytes per block */ block = (ddsColorBlock_t*) ((uintptr_t) buffer + y * xBlocks * 8); /* walk x */ for( x = 0; x < xBlocks; x++, block++ ) { DDSGetColorBlockColors( block, colors ); pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4); DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors ); } } /* return ok */ return 0; } /* DDSDecompressDXT3() decompresses a dxt3 format texture */ static int DDSDecompressDXT3( const unsigned char* buffer, int width, int height, unsigned char *pixels ) { int x, y, xBlocks, yBlocks; unsigned int *pixel, alphaZero; ddsColorBlock_t *block; ddsAlphaBlockExplicit_t *alphaBlock; ddsColor_t colors[ 4 ]; /* setup */ xBlocks = width / 4; yBlocks = height / 4; /* create zero alpha */ colors[ 0 ].a = 0; colors[ 0 ].r = 0xFF; colors[ 0 ].g = 0xFF; colors[ 0 ].b = 0xFF; alphaZero = *((unsigned int*) &colors[ 0 ]); /* walk y */ for( y = 0; y < yBlocks; y++ ) { /* 8 bytes per block, 1 block for alpha, 1 block for color */ block = (ddsColorBlock_t*) ((uintptr_t) buffer + y * xBlocks * 16); /* walk x */ for( x = 0; x < xBlocks; x++, block++ ) { /* get alpha block */ alphaBlock = (ddsAlphaBlockExplicit_t*) block; /* get color block */ block++; DDSGetColorBlockColors( block, colors ); /* decode color block */ pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4); DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors ); /* overwrite alpha bits with alpha block */ DDSDecodeAlphaExplicit( pixel, alphaBlock, width, alphaZero ); } } /* return ok */ return 0; } /* DDSDecompressDXT5() decompresses a dxt5 format texture */ static int DDSDecompressDXT5( const unsigned char* buffer, int width, int height, unsigned char *pixels ) { int x, y, xBlocks, yBlocks; unsigned int *pixel, alphaZero; ddsColorBlock_t *block; ddsAlphaBlock3BitLinear_t *alphaBlock; ddsColor_t colors[ 4 ]; /* setup */ xBlocks = width / 4; yBlocks = height / 4; /* create zero alpha */ colors[ 0 ].a = 0; colors[ 0 ].r = 0xFF; colors[ 0 ].g = 0xFF; colors[ 0 ].b = 0xFF; alphaZero = *((unsigned int*) &colors[ 0 ]); /* walk y */ for( y = 0; y < yBlocks; y++ ) { /* 8 bytes per block, 1 block for alpha, 1 block for color */ block = (ddsColorBlock_t*) ((uintptr_t) buffer + y * xBlocks * 16); /* walk x */ for( x = 0; x < xBlocks; x++, block++ ) { /* get alpha block */ alphaBlock = (ddsAlphaBlock3BitLinear_t*) block; /* get color block */ block++; DDSGetColorBlockColors( block, colors ); /* decode color block */ pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4); DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors ); /* overwrite alpha bits with alpha block */ DDSDecodeAlpha3BitLinear( pixel, alphaBlock, width, alphaZero ); } } /* return ok */ return 0; } /* DDSDecompressDXT2() decompresses a dxt2 format texture (fixme: un-premultiply alpha) */ static int DDSDecompressDXT2( const unsigned char* buffer, int width, int height, unsigned char *pixels ) { int r; /* decompress dxt3 first */ r = DDSDecompressDXT3( buffer, width, height, pixels ); /* return to sender */ return r; } /* DDSDecompressDXT4() decompresses a dxt4 format texture (fixme: un-premultiply alpha) */ static int DDSDecompressDXT4( const unsigned char* buffer, int width, int height, unsigned char *pixels ) { int r; /* decompress dxt5 first */ r = DDSDecompressDXT5( buffer, width, height, pixels ); /* return to sender */ return r; } /* DDSDecompressARGB8888() decompresses an argb 8888 format texture */ static int DDSDecompressARGB8888( const unsigned char* buffer, int width, int height, unsigned char *pixels ) { int x, y; const unsigned char *in; unsigned char *out; /* setup */ in = buffer; out = pixels; /* walk y */ for( y = 0; y < height; y++ ) { /* walk x */ for( x = 0; x < width; x++ ) { *out++ = *in++; *out++ = *in++; *out++ = *in++; *out++ = *in++; } } /* return ok */ return 0; } /** greebo: This decompresses a DXT5 RXGB texture as used by the Doom3 engine. */ static int DDSDecompressRXGB( const unsigned char* buffer, int width, int height, unsigned char *pixels ) { int x, y, xBlocks, yBlocks; unsigned int *pixel, redZero; ddsColorBlock_t *block; ddsAlphaBlock3BitLinear_t *alphaBlock; ddsColor_t colors[ 4 ]; /* setup */ xBlocks = width / 4; yBlocks = height / 4; /* create zero red */ colors[ 0 ].r = 0; colors[ 0 ].a = 0xFF; colors[ 0 ].g = 0xFF; colors[ 0 ].b = 0xFF; redZero = *((unsigned int*) &colors[ 0 ]); /* walk y */ for( y = 0; y < yBlocks; y++ ) { /* 8 bytes (64 bit) per block, 1 block for alpha, 1 block for color */ block = (ddsColorBlock_t*) ((uintptr_t) buffer + y * xBlocks * 16); /* walk x */ for( x = 0; x < xBlocks; x++, block++ ) { /* get alpha block */ alphaBlock = (ddsAlphaBlock3BitLinear_t*) block; /* get color block */ block++; // Decode the colour values from the 5:6:5 word and derive the c2 and c3 colours // The result is stored in the variable DDSGetColorBlockColors( block, colors ); /* decode color block */ pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4); DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors ); /* overwrite alpha bits with alpha block */ DDSDecodeRXGBAlpha3BitLinear( pixel, alphaBlock, width, redZero ); } } /* return ok */ return 0; } /* DDSDecompress() decompresses a dds texture into an rgba image buffer, returns 0 on success */ int DDSDecompress( const DDSHeader* header, const unsigned char* buffer, unsigned char *pixels ) { int width, height, r; ddsPF_t pf; /* get dds info */ r = DDSGetInfo( header, &width, &height, &pf ); if( r ) return r; /* decompress */ switch( pf ) { case DDS_PF_ARGB8888: /* fixme: support other [a]rgb formats */ r = DDSDecompressARGB8888( buffer, width, height, pixels ); break; case DDS_PF_DXT1: r = DDSDecompressDXT1( buffer, width, height, pixels ); break; case DDS_PF_DXT2: r = DDSDecompressDXT2( buffer, width, height, pixels ); break; case DDS_PF_DXT3: r = DDSDecompressDXT3( buffer, width, height, pixels ); break; case DDS_PF_DXT4: r = DDSDecompressDXT4( buffer, width, height, pixels ); break; case DDS_PF_DXT5: r = DDSDecompressDXT5( buffer, width, height, pixels ); break; case DDS_PF_DXT5_RXGB: r = DDSDecompressRXGB( buffer, width, height, pixels ); break; default: case DDS_PF_UNKNOWN: memset( pixels, 0xFF, width * height * 4 ); r = -1; break; } /* return to sender */ return r; } DarkRadiant-2.5.0/libs/debugging/000077500000000000000000000000001321750546400165755ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/debugging/ScenegraphUtils.h000066400000000000000000000043631321750546400220540ustar00rootroot00000000000000#pragma once #include "ipath.h" #include "inode.h" #include "math/AABB.h" #include "iscenegraph.h" #include "string/convert.h" #include "itextstream.h" inline std::string getNameForNodeType(scene::INode::Type type) { switch (type) { case scene::INode::Type::MapRoot: return "map"; case scene::INode::Type::Entity: return "entity"; case scene::INode::Type::Brush: return "brush"; case scene::INode::Type::Patch: return "patch"; case scene::INode::Type::Model: return "model"; case scene::INode::Type::Particle: return "particle"; case scene::INode::Type::EntityConnection: return "entityconnection"; default: return "unknown"; }; } // greebo: Return information about the given node inline std::string getNodeInfo(const scene::INodePtr& node) { std::string returnValue; if (node == NULL) { return "NULL"; } returnValue += getNameForNodeType(node->getNodeType()); returnValue += " (" + node->name() + ")"; return returnValue; } // greebo: Stream insertion operator for scene::INode inline std::ostream& operator<<(std::ostream& st, const scene::INodePtr& node) { st << getNodeInfo(node); return st; } inline std::string getPathInfo(const scene::Path& path) { std::string name; for (scene::Path::const_iterator i = path.begin(); i != path.end(); i++) { // Cast the INode onto a scene::Node scene::INodePtr node = *i; name += (name.empty()) ? "" : ", "; name += getNodeInfo(node); } name = std::string("[") + string::to_string(path.size()) + "] {" + name + "} extents: "; name += "<" + string::to_string(path.top()->worldAABB().extents.x()) + ","; name += string::to_string(path.top()->worldAABB().extents.y()) + ","; name += string::to_string(path.top()->worldAABB().extents.z()) + ">"; return name; } // greebo: Stream-insertion operator for scene::Paths, printing out information on the referenced scene::Nodes inline std::ostream& operator<<(std::ostream& st, const scene::Path& path) { st << getPathInfo(path); return st; } class SceneGraphDumper : public scene::NodeVisitor { public: bool pre(const scene::INodePtr& node) { rMessage() << getNodeInfo(node) << "\n"; return true; } }; inline void dumpSceneGraph() { SceneGraphDumper dumper; GlobalSceneGraph().root()->traverseChildren(dumper); } DarkRadiant-2.5.0/libs/debugging/ScopedDebugTimer.h000066400000000000000000000047131321750546400221400ustar00rootroot00000000000000#pragma once #include "itextstream.h" #if defined(_MSC_VER) || defined(_WINDOWS_) #include #include #undef min #undef max #if !defined(_WINSOCK2API_) && !defined(_WINSOCKAPI_) struct timeval { long tv_sec; long tv_usec; }; #endif #else #include #endif #if defined(_MSC_VER) || defined(_WINDOWS_) inline int gettimeofday(struct timeval* tv, void*) { union { long long ns100; FILETIME ft; } now; GetSystemTimeAsFileTime (&now.ft); tv->tv_usec = (long) ((now.ns100 / 10LL) % 1000000LL); tv->tv_sec = (long) ((now.ns100 - 116444736000000000LL) / 10000000LL); return (0); } #endif #include #include "stream/TextFileInputStream.h" namespace { const double MILLION = 1000000.0; /** * Operator- for timeval structures. * * @returns * Double-precision float representing the difference in seconds between * the two times. */ double operator-(const timeval& l, const timeval& r) { // Convert timevals to double double dl = (double) l.tv_sec + ((double) l.tv_usec / MILLION); double dr = (double) r.tv_sec + ((double) r.tv_usec / MILLION); return dl - dr; } } /** * Debugging class to time a particular event. The clock is saved during * construction, and the time difference calculated at destruction. */ class ScopedDebugTimer { private: // Start time timeval _s; // Name of operation std::string _op; // Show FPS? bool _fps; public: /** * Constructor. Set the name of the operation to be printed out on * destruction. * * @param name * The name of the operation. * * @param showFps * If true, a nominal FPS value will be calculated for the given operation * time. */ ScopedDebugTimer(const std::string& name, bool showFps = false) : _op(name), _fps(showFps) { #ifndef NDEBUG // Save start time gettimeofday(&_s, NULL); #endif } /** * Destructor. Prints out the time of the operation. */ ~ScopedDebugTimer() { #ifndef NDEBUG // Get the current time timeval end; gettimeofday(&end, NULL); // Calculate duration double duration = end - _s; TemporaryThreadsafeStream stream = rMessage(); stream << "[ScopedDebugTimer] \"" << _op << "\" in " << duration << " seconds"; if (_fps) { stream << " (" << (1.0 / duration) << " FPS)"; } stream << std::endl; #endif } }; DarkRadiant-2.5.0/libs/debugging/debugging.cpp000066400000000000000000000015441321750546400212400ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "debugging.h" void TEST_ASSERT() { ERROR_MESSAGE("test"); ASSERT_NOTNULL(0); } DarkRadiant-2.5.0/libs/debugging/debugging.h000066400000000000000000000027531321750546400207100ustar00rootroot00000000000000#pragma once /// \brief Debugging macros for fatal error/assert messages. #if defined(_DEBUG) #define DEBUG_ASSERTS #endif #include "imodule.h" // for GlobalErrorHandler() #if defined(DEBUG_ASSERTS) // Define the breakpoint function, to fire up the debugger #if defined(_MSC_VER) && defined(_M_IX86) #define DEBUGGER_BREAKPOINT() __asm { int 3 } #elif defined(_MSC_VER) && defined(_WIN64) #define DEBUGGER_BREAKPOINT() __debugbreak() #elif defined(__linux__) #include #define DEBUGGER_BREAKPOINT() raise(SIGTRAP); #else #define DEBUGGER_BREAKPOINT() #endif #define STR(x) #x #define STR2(x) STR(x) #define FILE_LINE __FILE__ ":" STR2(__LINE__) /// \brief Sends a \p message to the current debug-message-handler text-output-stream if \p condition evaluates to false. #define ASSERT_MESSAGE(condition, message)\ if(!(condition)) { GlobalErrorHandler()("DarkRadiant - Assertion Failure", std::string(FILE_LINE) + "\nAssertion failure: " + message + "\nBreak into the debugger?"); } /// \brief Sends a \p message to the current debug-message-handler text-output-stream. #define ERROR_MESSAGE(message)\ { GlobalErrorHandler()("DarkRadiant - Runtime Error", std::string(FILE_LINE) + "\nRuntime Error: " + message + "\nBreak into the debugger?"); } #define ASSERT_NOTNULL(ptr) ASSERT_MESSAGE(ptr != 0, "pointer \"" #ptr "\" is null") #else // Release Builds #define ASSERT_MESSAGE(condition, message) #define ERROR_MESSAGE(message) #define ASSERT_NOTNULL(ptr) #define DEBUGGER_BREAKPOINT() #endif DarkRadiant-2.5.0/libs/debugging/render.h000066400000000000000000000056011321750546400202270ustar00rootroot00000000000000#pragma once #include #include "render/Colour4.h" namespace debug { namespace detail { inline bool boolFromGLBool(GLboolean b) { return b == GL_TRUE; } } /// Streamable object to insert render state flags struct StateFlagsInserter { int flags; StateFlagsInserter(int f): flags(f) { } }; inline std::ostream& operator<<(std::ostream& os, const StateFlagsInserter& s) { #define OUTPUT_RENDERFLAG(x) if (s.flags & (x)) { os << "|" << #x; } OUTPUT_RENDERFLAG(RENDER_LINESTIPPLE); OUTPUT_RENDERFLAG(RENDER_POLYGONSTIPPLE); OUTPUT_RENDERFLAG(RENDER_ALPHATEST); OUTPUT_RENDERFLAG(RENDER_DEPTHTEST); OUTPUT_RENDERFLAG(RENDER_DEPTHWRITE); OUTPUT_RENDERFLAG(RENDER_MASKCOLOUR); OUTPUT_RENDERFLAG(RENDER_CULLFACE); OUTPUT_RENDERFLAG(RENDER_SCALED); OUTPUT_RENDERFLAG(RENDER_SMOOTH); OUTPUT_RENDERFLAG(RENDER_LIGHTING); OUTPUT_RENDERFLAG(RENDER_BLEND); OUTPUT_RENDERFLAG(RENDER_OFFSETLINE); OUTPUT_RENDERFLAG(RENDER_FILL); OUTPUT_RENDERFLAG(RENDER_VERTEX_COLOUR); OUTPUT_RENDERFLAG(RENDER_TEXTURE_2D); OUTPUT_RENDERFLAG(RENDER_TEXTURE_CUBEMAP); OUTPUT_RENDERFLAG(RENDER_BUMP); OUTPUT_RENDERFLAG(RENDER_PROGRAM); OUTPUT_RENDERFLAG(RENDER_OVERRIDE); return os; } /// Streamable object to insert glColorMask value class ColorMaskInserter { }; inline std::ostream& operator<<(std::ostream& os, const ColorMaskInserter& i) { using namespace detail; GLboolean vals[4]; glGetBooleanv(GL_COLOR_WRITEMASK, &vals[0]); os << "{ R = " << boolFromGLBool(vals[0]) << ", G = " << boolFromGLBool(vals[1]) << ", B = " << boolFromGLBool(vals[2]) << ", A = " << boolFromGLBool(vals[3]) << " }"; return os; } /// Streamable object to insert glDepthMask value class DepthMaskInserter { }; inline std::ostream& operator<<(std::ostream& os, const DepthMaskInserter& i) { GLboolean mask; glGetBooleanv(GL_DEPTH_WRITEMASK, &mask); os << detail::boolFromGLBool(mask); return os; } /// Get the current GL_COLOR as a Colour4 for debugging inline Colour4 getGLColor() { Colour4 result; glGetDoublev(GL_CURRENT_COLOR, &result[0]); return result; } /// Get a GL integer for debugging inline int getGLInt(GLenum name) { int result; glGetIntegerv(name, &result); return result; } /// Get a GL boolean for debugging inline bool getGLBool(GLenum name) { GLboolean result; glGetBooleanv(name, &result); return result == GL_TRUE; } } DarkRadiant-2.5.0/libs/dragplanes.h000066400000000000000000000242721321750546400171420ustar00rootroot00000000000000#pragma once #include "iselectiontest.h" #include "ObservedSelectable.h" #include "math/AABB.h" #include "math/Line.h" namespace selection { /** * Selection-test and transformation helper for drag-resizable objects. * This is used by PatchNodes, LightNodes and SpeakerNodes. */ class DragPlanes { private: ObservedSelectable _selectableRight; // +x ObservedSelectable _selectableLeft; // -x ObservedSelectable _selectableFront; // +y ObservedSelectable _selectableBack; // -y ObservedSelectable _selectableTop; // +z ObservedSelectable _selectableBottom; // -z public: AABB m_bounds; DragPlanes(const SelectionChangedSlot& onchanged) : _selectableRight(onchanged), _selectableLeft(onchanged), _selectableFront(onchanged), _selectableBack(onchanged), _selectableTop(onchanged), _selectableBottom(onchanged) {} // Returns true if any of the 6 planes is selected bool isSelected() const { return _selectableRight.isSelected() || _selectableLeft.isSelected() || _selectableFront.isSelected() || _selectableBack.isSelected() || _selectableTop.isSelected() || _selectableBottom.isSelected(); } void setSelected(bool selected) { _selectableRight.setSelected(selected); _selectableLeft.setSelected(selected); _selectableFront.setSelected(selected); _selectableBack.setSelected(selected); _selectableTop.setSelected(selected); _selectableBottom.setSelected(selected); } // greebo: Test-select each of the 6 planes and add suitable ones to the Selector // for further consideration. The basic idea of this method is to do a simple // "is this test position in front of any of these 6 planes"-test by calculating // dot products with all corresponding 4 corner vertices of each plane. For a plane // to pass the test it's required that all its 4 corner vertices need to pass. // Everything passed in here should be specified in local coordinates, any // local2world transformation needs to be set up in the SelectionTest beforehand. void selectPlanes(const AABB& aabb, Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback) { // Provided that the object's local2World matrix has been fed into the SelectionTest // the getNear() and getFar() methods will return local coordinates. Line line(test.getNear(), test.getFar()); // Calculate the corners (local coords) Vector3 corners[8]; aabb.getCorners(corners); Plane3 planes[6]; aabb.getPlanes(planes); // Next calculate the vectors for the upcoming dot-product // Take the closest point of the selection ray and calc the distance vector for (Vector3* i = corners; i != corners + 8; ++i) { *i = line.getClosestPoint(*i) - (*i); } if (planes[0].normal().dot(corners[1]) > 0 && planes[0].normal().dot(corners[2]) > 0 && planes[0].normal().dot(corners[5]) > 0 && planes[0].normal().dot(corners[6]) > 0) { Selector_add(selector, _selectableRight); selectedPlaneCallback(planes[0]); //rMessage() << "right\n"; } if (planes[1].normal().dot(corners[0]) > 0 && planes[1].normal().dot(corners[3]) > 0 && planes[1].normal().dot(corners[4]) > 0 && planes[1].normal().dot(corners[7]) > 0) { Selector_add(selector, _selectableLeft); selectedPlaneCallback(planes[1]); //rMessage() << "left\n"; } if (planes[2].normal().dot(corners[0]) > 0 && planes[2].normal().dot(corners[1]) > 0 && planes[2].normal().dot(corners[4]) > 0 && planes[2].normal().dot(corners[5]) > 0) { Selector_add(selector, _selectableFront); selectedPlaneCallback(planes[2]); //rMessage() << "front\n"; } if (planes[3].normal().dot(corners[2]) > 0 && planes[3].normal().dot(corners[3]) > 0 && planes[3].normal().dot(corners[6]) > 0 && planes[3].normal().dot(corners[7]) > 0) { Selector_add(selector, _selectableBack); selectedPlaneCallback(planes[3]); //rMessage() << "back\n"; } if (planes[4].normal().dot(corners[0]) > 0 && planes[4].normal().dot(corners[1]) > 0 && planes[4].normal().dot(corners[2]) > 0 && planes[4].normal().dot(corners[3]) > 0) { Selector_add(selector, _selectableTop); selectedPlaneCallback(planes[4]); //rMessage() << "top\n"; } if (planes[5].normal().dot(corners[4]) > 0 && planes[5].normal().dot(corners[5]) > 0 && planes[5].normal().dot(corners[6]) > 0 && planes[5].normal().dot(corners[7]) > 0) { Selector_add(selector, _selectableBottom); selectedPlaneCallback(planes[5]); //rMessage() << "bottom\n"; } m_bounds = aabb; } void selectReversedPlanes(const AABB& aabb, Selector& selector, const SelectedPlanes& selectedPlanes) { Plane3 planes[6]; aabb.getPlanes(planes); if (selectedPlanes.contains(-planes[0])) { Selector_add(selector, _selectableRight); } if (selectedPlanes.contains(-planes[1])) { Selector_add(selector, _selectableLeft); } if (selectedPlanes.contains(-planes[2])) { Selector_add(selector, _selectableFront); } if (selectedPlanes.contains(-planes[3])) { Selector_add(selector, _selectableBack); } if (selectedPlanes.contains(-planes[4])) { Selector_add(selector, _selectableTop); } if (selectedPlanes.contains(-planes[5])) { Selector_add(selector, _selectableBottom); } } // greebo: This calculates a new AABB for the given translation. Everything used in here is in local coordinates: // The m_bounds member (which has to be set beforehand) as well as the translation vector. // Based on the selection status of the 6 planes a new min/max vector pair is calculated // and the resulting AABB is returned. AABB evaluateResize(const Vector3& translation) const { Vector3 min = m_bounds.getOrigin() - m_bounds.getExtents(); Vector3 max = m_bounds.getOrigin() + m_bounds.getExtents(); // Handle x translation if (m_bounds.extents.x() != 0) { if (_selectableRight.isSelected()) { max.x() += translation.x(); //rMessage() << "moving right\n"; } if (_selectableLeft.isSelected()) { min.x() += translation.x(); //rMessage() << "moving left\n"; } } // Handle y translation if (m_bounds.extents.y() != 0) { if (_selectableFront.isSelected()) { max.y() += translation.y(); //rMessage() << "moving front\n"; } if (_selectableBack.isSelected()) { min.y() += translation.y(); //rMessage() << "moving back\n"; } } // Handle z translation if (m_bounds.extents.z() != 0) { if (_selectableTop.isSelected()) { max.z() += translation.z(); //rMessage() << "moving top\n"; } if(_selectableBottom.isSelected()) { min.z() += translation.z(); //rMessage() << "moving bottom\n"; } } return AABB::createFromMinMax(min, max); } // greebo: Evaluates the given translation for the rotated m_bounds and return a new one. // Translation is in world coordinates, whereas the (previously set) m_bounds AABB is in local coords. // The given rotation matrix is used to transform the incoming translation. Only those planes which // have been selected beforehand will be translated. The m_bounds member needs to be set beforehand. // As PatchNodes and SpeakerNodes are quasi non-rotated objects, this code applies to LightNodes only. AABB evaluateResize(const Vector3& translation, const Matrix4& rotation) const { //rMessage() << "Translation: " << translation << ", to local: " << translationToLocal(translation, rotation) << std::endl; // Convert the translation to local coords and calculate a resized AABB (in local coords). AABB aabb(evaluateResize(translationToLocal(translation, rotation))); // The origin is moved by this operation as well: // Rotate the diff vector between oldLocalOrigin and newLocalOrigin and apply it to the AABB aabb.origin = m_bounds.origin + translationFromLocal(aabb.origin - m_bounds.origin, rotation); return aabb; } // greebo: This is used by PatchNodes to calculate a generic transformation matrix from the given // drag-manipulation, which is then applied to the whole object. Matrix4 evaluateTransform(const Vector3& translation) const { AABB aabb(evaluateResize(translation)); Vector3 scale( m_bounds.extents[0] != 0 ? aabb.extents[0] / m_bounds.extents[0] : 1, m_bounds.extents[1] != 0 ? aabb.extents[1] / m_bounds.extents[1] : 1, m_bounds.extents[2] != 0 ? aabb.extents[2] / m_bounds.extents[2] : 1 ); Matrix4 matrix = Matrix4::getTranslation(aabb.origin - m_bounds.origin); matrix.scaleBy(scale, m_bounds.origin); return matrix; } private: // local must be a pure rotation Vector3 translationToLocal(const Vector3& translation, const Matrix4& local) const { return local.getTransposed().getTranslatedBy(translation).getMultipliedBy(local).translation(); } // local must be a pure rotation Vector3 translationFromLocal(const Vector3& translation, const Matrix4& local) const { return local.getTranslatedBy(translation).getMultipliedBy(local.getTransposed()).translation(); } }; } // namespace DarkRadiant-2.5.0/libs/eclass.h000066400000000000000000000074611321750546400162750ustar00rootroot00000000000000/** * \file * Helper functions for dealing with IEntityClass and related objects. */ #pragma once #include "ieclass.h" #include #include #include "string/predicate.h" namespace eclass { typedef std::vector AttributeList; namespace detail { class AttributeSuffixComparator { // Starting position to convert to a number std::size_t _startPos; public: /// Constructor. Initialise the start position. AttributeSuffixComparator(std::size_t startPos) : _startPos(startPos) { } bool operator() (const EntityClassAttribute& x, const EntityClassAttribute& y) const { // Get both substrings. An empty suffix comes first. std::string sx = x.getName().substr(_startPos); std::string sy = y.getName().substr(_startPos); if (sx.empty()) return true; else if (sy.empty()) return false; // Try numeric sort first, then fall back to lexicographic if the // prefixes are not integers. try { int ix = std::stoi(sx); int iy = std::stoi(sy); // Perform the comparison and return return ix < iy; } catch (std::logic_error&) { // greebo: Non-numeric operands, use ordinary string comparison return sx < sy; } } }; inline void addIfMatches(AttributeList& list, const EntityClassAttribute& attr, const std::string& prefix, bool includeInherited) { if (string::istarts_with(attr.getName(), prefix) && (includeInherited || !attr.inherited)) { list.push_back(attr); } } } /** * \brief * Return a list of all class spawnargs matching the given prefix. * * The list is sorted by the numeric or lexicographic ordering of the suffixes. * This ensures that "editor_usage1", "editor_usage2" etc are returned in the * correct order. * * \param eclass * Entity class object to search * * \param prefix * String prefix for the spawnargs of interest * * \param includeInherited * Whether to include class spawnargs inherited from the parent class. Defaults * to true. */ inline AttributeList getSpawnargsWithPrefix(const IEntityClass& eclass, const std::string& prefix, bool includeInherited = true) { // Populate the list with with matching attributes AttributeList matches; eclass.forEachClassAttribute( std::bind(&detail::addIfMatches, std::ref(matches), std::placeholders::_1, prefix, includeInherited), true // include editor_keys ); // Sort the list in suffix order before returning detail::AttributeSuffixComparator comp(prefix.length()); std::sort(matches.begin(), matches.end(), comp); return matches; } /** * \brief * Get the usage text for an entity class. * * The usage text consists of the values of all "editor_usage" spawnargs * concatenated in order. */ inline std::string getUsage(const IEntityClass& entityClass) { // Find all relevant spawnargs in order AttributeList usageAttrs = getSpawnargsWithPrefix( entityClass, "editor_usage", false ); // Build the string std::ostringstream usage; bool firstLine = true; for (const EntityClassAttribute& a : usageAttrs) { if (firstLine) { usage << a.getValue(); firstLine = false; } else { usage << '\n' << a.getValue(); } } return usage.str(); } } DarkRadiant-2.5.0/libs/entitylib.h000066400000000000000000000261731321750546400170270ustar00rootroot00000000000000#pragma once #include "debugging/debugging.h" #include "ientity.h" #include "ieclass.h" #include "irender.h" #include "igl.h" #include "iselectiontest.h" #include "generic/callback.h" #include "math/AABB.h" #include "scenelib.h" #include #include /* greebo: draws a pyramid defined by 5 vertices * points[0] is the top of the pyramid * points[1] to points[4] is the base rectangle */ inline void drawPyramid(const Vector3 points[5]) { typedef unsigned int index_t; index_t indices[16] = { 0, 1, // top to first 0, 2, // top to second 0, 3, // top to third 0, 4, // top to fourth 1, 2, // first to second 2, 3, // second to third 3, 4, // third to second 4, 1, // fourth to first }; glVertexPointer(3, GL_DOUBLE, 0, points); glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices); } /* greebo: draws a frustum defined by 8 vertices * points[0] to points[3] define the top area vertices (clockwise starting from the "upper right" corner) * points[4] to points[7] define the base rectangle (clockwise starting from the "upper right" corner) */ inline void drawFrustum(const Vector3 points[8]) { typedef unsigned int index_t; index_t indices[24] = { 0, 4, // top up right to bottom up right 1, 5, // top down right to bottom down right 2, 6, // top down left to bottom down left 3, 7, // top up left to bottom up left 0, 1, // top up right to top down right 1, 2, // top down right to top down left 2, 3, // top down left to top up left 3, 0, // top up left to top up right 4, 5, // bottom up right to bottom down right 5, 6, // bottom down right to bottom down left 6, 7, // bottom down left to bottom up left 7, 4, // bottom up left to bottom up right }; glVertexPointer(3, GL_DOUBLE, 0, points); glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices); } inline void arrow_draw(const Vector3& origin, const Vector3& direction) { Vector3 up(0, 0, 1); Vector3 left(-direction[1], direction[0], 0); Vector3 endpoint(origin + direction*32.0); Vector3 tip1(endpoint + direction *(-8.0) + up*(-4.0)); Vector3 tip2(tip1 + up*8.0); Vector3 tip3(endpoint + direction*(-8.0) + left*(-4.0)); Vector3 tip4(tip3 + left*8.0); glBegin (GL_LINES); glVertex3dv(origin); glVertex3dv(endpoint); glVertex3dv(endpoint); glVertex3dv(tip1); glVertex3dv(endpoint); glVertex3dv(tip2); glVertex3dv(endpoint); glVertex3dv(tip3); glVertex3dv(endpoint); glVertex3dv(tip4); glVertex3dv(tip1); glVertex3dv(tip3); glVertex3dv(tip3); glVertex3dv(tip2); glVertex3dv(tip2); glVertex3dv(tip4); glVertex3dv(tip4); glVertex3dv(tip1); glEnd(); } class SelectionIntersection; inline void aabb_testselect(const AABB& aabb, SelectionTest& test, SelectionIntersection& best) { const IndexPointer::index_type indices[24] = { 2, 1, 5, 6, 1, 0, 4, 5, 0, 1, 2, 3, 3, 7, 4, 0, 3, 2, 6, 7, 7, 6, 5, 4, }; Vector3 points[8]; aabb.getCorners(points); VertexPointer pointer(points, sizeof(Vector3)); test.TestQuads(pointer, IndexPointer(indices, 24), best); } inline void aabb_draw_wire(const Vector3 points[8]) { typedef unsigned int index_t; index_t indices[24] = { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7, }; #if 1 glVertexPointer(3, GL_DOUBLE, 0, points); glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices); #else glBegin(GL_LINES); for(std::size_t i = 0; i < sizeof(indices)/sizeof(index_t); ++i) { glVertex3dv(points[indices[i]]); } glEnd(); #endif } inline void aabb_draw_flatshade(const Vector3 points[8]) { glBegin(GL_QUADS); glNormal3dv(aabb_normals[0]); glVertex3dv(points[2]); glVertex3dv(points[1]); glVertex3dv(points[5]); glVertex3dv(points[6]); glNormal3dv(aabb_normals[1]); glVertex3dv(points[1]); glVertex3dv(points[0]); glVertex3dv(points[4]); glVertex3dv(points[5]); glNormal3dv(aabb_normals[2]); glVertex3dv(points[0]); glVertex3dv(points[1]); glVertex3dv(points[2]); glVertex3dv(points[3]); glNormal3dv(aabb_normals[3]); glVertex3dv(points[0]); glVertex3dv(points[3]); glVertex3dv(points[7]); glVertex3dv(points[4]); glNormal3dv(aabb_normals[4]); glVertex3dv(points[3]); glVertex3dv(points[2]); glVertex3dv(points[6]); glVertex3dv(points[7]); glNormal3dv(aabb_normals[5]); glVertex3dv(points[7]); glVertex3dv(points[6]); glVertex3dv(points[5]); glVertex3dv(points[4]); glEnd(); } inline void aabb_draw_wire(const AABB& aabb) { Vector3 points[8]; aabb.getCorners(points); aabb_draw_wire(points); } inline void aabb_draw_flatshade(const AABB& aabb) { Vector3 points[8]; aabb.getCorners(points); aabb_draw_flatshade(points); } inline void aabb_draw_textured(const AABB& aabb) { Vector3 points[8]; aabb.getCorners(points); glBegin(GL_QUADS); glNormal3dv(aabb_normals[0]); glTexCoord2dv(aabb_texcoord_topleft); glVertex3dv(points[2]); glTexCoord2dv(aabb_texcoord_topright); glVertex3dv(points[1]); glTexCoord2dv(aabb_texcoord_botright); glVertex3dv(points[5]); glTexCoord2dv(aabb_texcoord_botleft); glVertex3dv(points[6]); glNormal3dv(aabb_normals[1]); glTexCoord2dv(aabb_texcoord_topleft); glVertex3dv(points[1]); glTexCoord2dv(aabb_texcoord_topright); glVertex3dv(points[0]); glTexCoord2dv(aabb_texcoord_botright); glVertex3dv(points[4]); glTexCoord2dv(aabb_texcoord_botleft); glVertex3dv(points[5]); glNormal3dv(aabb_normals[2]); glTexCoord2dv(aabb_texcoord_topleft); glVertex3dv(points[0]); glTexCoord2dv(aabb_texcoord_topright); glVertex3dv(points[1]); glTexCoord2dv(aabb_texcoord_botright); glVertex3dv(points[2]); glTexCoord2dv(aabb_texcoord_botleft); glVertex3dv(points[3]); glNormal3dv(aabb_normals[3]); glTexCoord2dv(aabb_texcoord_topleft); glVertex3dv(points[0]); glTexCoord2dv(aabb_texcoord_topright); glVertex3dv(points[3]); glTexCoord2dv(aabb_texcoord_botright); glVertex3dv(points[7]); glTexCoord2dv(aabb_texcoord_botleft); glVertex3dv(points[4]); glNormal3dv(aabb_normals[4]); glTexCoord2dv(aabb_texcoord_topleft); glVertex3dv(points[3]); glTexCoord2dv(aabb_texcoord_topright); glVertex3dv(points[2]); glTexCoord2dv(aabb_texcoord_botright); glVertex3dv(points[6]); glTexCoord2dv(aabb_texcoord_botleft); glVertex3dv(points[7]); glNormal3dv(aabb_normals[5]); glTexCoord2dv(aabb_texcoord_topleft); glVertex3dv(points[7]); glTexCoord2dv(aabb_texcoord_topright); glVertex3dv(points[6]); glTexCoord2dv(aabb_texcoord_botright); glVertex3dv(points[5]); glTexCoord2dv(aabb_texcoord_botleft); glVertex3dv(points[4]); glEnd(); } inline void aabb_draw_solid(const AABB& aabb, RenderStateFlags state) { if(state & RENDER_TEXTURE_2D) { aabb_draw_textured(aabb); } else { aabb_draw_flatshade(aabb); } } inline void aabb_draw(const AABB& aabb, RenderStateFlags state) { if(state & RENDER_FILL) { aabb_draw_solid(aabb, state); } else { aabb_draw_wire(aabb); } } class RenderableSolidAABB : public OpenGLRenderable { const AABB& m_aabb; public: RenderableSolidAABB(const AABB& aabb) : m_aabb(aabb) { } void render(const RenderInfo& info) const { aabb_draw_solid(m_aabb, info.getFlags()); } const AABB& getAABB() const { return m_aabb; } }; class RenderableWireframeAABB : public OpenGLRenderable { const AABB& m_aabb; public: RenderableWireframeAABB(const AABB& aabb) : m_aabb(aabb) { } void render(const RenderInfo& info) const { aabb_draw_wire(m_aabb); } }; /** * Stream insertion for Entity objects. */ inline std::ostream& operator<< (std::ostream& os, const Entity& entity) { os << "Entity { name=\"" << entity.getKeyValue("name") << "\", " << "classname=\"" << entity.getKeyValue("classname") << "\", " << "origin=\"" << entity.getKeyValue("origin") << "\" }"; return os; } class EntityNodeFindByClassnameWalker : public scene::NodeVisitor { protected: // Name to search for std::string _name; // The search result scene::INodePtr _entityNode; public: // Constructor EntityNodeFindByClassnameWalker(const std::string& name) : _name(name) {} scene::INodePtr getEntityNode() { return _entityNode; } Entity* getEntity() { return _entityNode != NULL ? Node_getEntity(_entityNode) : NULL; } // Pre-descent callback bool pre(const scene::INodePtr& node) { if (_entityNode == NULL) { // Entity not found yet Entity* entity = Node_getEntity(node); if (entity != NULL) { // Got an entity, let's see if the name matches if (entity->getKeyValue("classname") == _name) { _entityNode = node; } return false; // don't traverse entities } else { // Not an entity, traverse return true; } } else { // Entity already found, don't traverse any further return false; } } }; /* greebo: Finds an entity with the given classname */ inline Entity* Scene_FindEntityByClass(const std::string& className) { // Instantiate a walker to find the entity EntityNodeFindByClassnameWalker walker(className); // Walk the scenegraph GlobalSceneGraph().root()->traverse(walker); return walker.getEntity(); } /* Check if a node is the worldspawn. */ inline bool Node_isWorldspawn(const scene::INodePtr& node) { Entity* entity = Node_getEntity(node); return entity != nullptr && entity->isWorldspawn(); } /** * greebo: Changing the entity classname is a non-trivial operation in DarkRadiant, as * the actual c++ class of an entity is depending on it. Changing the classname * therefore means 1) to recreate a new entity 2) to copy all spawnargs over from the old one * and 3) re-parent any child nodes to the new entity. * * @node: The entity node to change the classname of. * @classname: The new classname. * * @returns: The new entity node. */ inline scene::INodePtr changeEntityClassname(const scene::INodePtr& node, const std::string& classname) { // Make a copy of this node first scene::INodePtr oldNode(node); // greebo: First, get the eclass IEntityClassPtr eclass = GlobalEntityClassManager().findOrInsert( classname, scene::hasChildPrimitives(oldNode) // whether this entity has child primitives ); // must not fail, findOrInsert always returns non-NULL assert(eclass); // Create a new entity with the given class IEntityNodePtr newNode(GlobalEntityCreator().createEntity(eclass)); Entity* oldEntity = Node_getEntity(oldNode); // Traverse the old entity with a walker Entity& newEntity = newNode->getEntity(); // Copy all keyvalues except classname oldEntity->forEachKeyValue([&](const std::string& key, const std::string& value) { if (key != "classname") { newEntity.setKeyValue(key, value); } }); // The old node must not be the root node (size of path >= 2) scene::INodePtr parent = oldNode->getParent(); assert(parent); // Remove the old entity node from the parent scene::removeNodeFromParent(oldNode); // Traverse the child and reparent all primitives to the new entity node scene::parentPrimitives(oldNode, newNode); // Insert the new entity to the parent parent->addChildNode(newNode); return newNode; } DarkRadiant-2.5.0/libs/gamelib.h000066400000000000000000000027411321750546400164170ustar00rootroot00000000000000#pragma once #include "igame.h" #include "registry/registry.h" #include "string/convert.h" #include "os/path.h" namespace game { namespace current { /** * \brief * Get the value of the current game's tree in the registry and convert it * to type T. If the key cannot be found or is not convertible to the * required type, a default-constructed T will be returned. * * T must be default-constructible, copy-constructible and convertible from * an std::string using string::convert. */ template inline T getValue(const std::string& localXPath, T defaultVal = T()) { xml::NodeList list = GlobalGameManager().currentGame()->getLocalXPath(localXPath); return list.empty() ? defaultVal : string::convert(list[0].getAttributeValue("value")); } /** * Returns the current "mod" part of the full path. For Doom3-style games this is the part * relative to the engine path. For TDM-style games there's not necessarily a fs_game or * fs_game_base set, in this case we fall back to the current game's name. */ inline std::string getModPath(const std::string& fullPath) { std::string relPath = os::getRelativePathMinusFilename(fullPath, registry::getValue(RKEY_ENGINE_PATH)); // For the TDM game we don't necessarily have a "base" or "fs_game" directory // Fall back to the game name instead if (relPath.empty()) { return GlobalGameManager().currentGame()->getKeyValue("name"); } return relPath; } } // namespace } // namespace DarkRadiant-2.5.0/libs/generic/000077500000000000000000000000001321750546400162565ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/generic/callback.h000066400000000000000000000003271321750546400201650ustar00rootroot00000000000000#ifndef _CALLBACK_H_ #define _CALLBACK_H_ #include // Typedef to satisfy old code - in many places the "Callback" type is still used typedef std::function Callback; #endif /* _CALLBACK_H_ */ DarkRadiant-2.5.0/libs/libfmt/000077500000000000000000000000001321750546400161175ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/libfmt/LICENSE000066400000000000000000000024361321750546400171310ustar00rootroot00000000000000Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. DarkRadiant-2.5.0/libs/libfmt/fmt/000077500000000000000000000000001321750546400167055ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/libfmt/fmt/format.cc000066400000000000000000000701511321750546400205100ustar00rootroot00000000000000/* Formatting library for C++ Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "format.h" #include #include #include #include #include #include #include // for std::ptrdiff_t #if defined(_WIN32) && defined(__MINGW32__) # include #endif #if FMT_USE_WINDOWS_H # if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) # define WIN32_LEAN_AND_MEAN # endif # if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) # include # else # define NOMINMAX # include # undef NOMINMAX # endif #endif using fmt::internal::Arg; #if FMT_EXCEPTIONS # define FMT_TRY try # define FMT_CATCH(x) catch (x) #else # define FMT_TRY if (true) # define FMT_CATCH(x) if (false) #endif #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable: 4702) // unreachable code // Disable deprecation warning for strerror. The latter is not called but // MSVC fails to detect it. # pragma warning(disable: 4996) #endif // Dummy implementations of strerror_r and strerror_s called if corresponding // system functions are not available. static inline fmt::internal::Null<> strerror_r(int, char *, ...) { return fmt::internal::Null<>(); } static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { return fmt::internal::Null<>(); } namespace fmt { FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {} FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {} FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {} namespace { #ifndef _MSC_VER # define FMT_SNPRINTF snprintf #else // _MSC_VER inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { va_list args; va_start(args, format); int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); va_end(args); return result; } # define FMT_SNPRINTF fmt_snprintf #endif // _MSC_VER #if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) # define FMT_SWPRINTF snwprintf #else # define FMT_SWPRINTF swprintf #endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. template struct IntChecker { template static bool fits_in_int(T value) { unsigned max = INT_MAX; return value <= max; } static bool fits_in_int(bool) { return true; } }; template <> struct IntChecker { template static bool fits_in_int(T value) { return value >= INT_MIN && value <= INT_MAX; } static bool fits_in_int(int) { return true; } }; const char RESET_COLOR[] = "\x1b[0m"; typedef void (*FormatFunc)(Writer &, int, StringRef); // Portable thread-safe version of strerror. // Sets buffer to point to a string describing the error code. // This can be either a pointer to a string stored in buffer, // or a pointer to some static immutable string. // Returns one of the following values: // 0 - success // ERANGE - buffer is not large enough to store the error message // other - failure // Buffer should be at least of size 1. int safe_strerror( int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); class StrError { private: int error_code_; char *&buffer_; std::size_t buffer_size_; // A noop assignment operator to avoid bogus warnings. void operator=(const StrError &) {} // Handle the result of XSI-compliant version of strerror_r. int handle(int result) { // glibc versions before 2.13 return result in errno. return result == -1 ? errno : result; } // Handle the result of GNU-specific version of strerror_r. int handle(char *message) { // If the buffer is full then the message is probably truncated. if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) return ERANGE; buffer_ = message; return 0; } // Handle the case when strerror_r is not available. int handle(internal::Null<>) { return fallback(strerror_s(buffer_, buffer_size_, error_code_)); } // Fallback to strerror_s when strerror_r is not available. int fallback(int result) { // If the buffer is full then the message is probably truncated. return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE : result; } // Fallback to strerror if strerror_r and strerror_s are not available. int fallback(internal::Null<>) { errno = 0; buffer_ = strerror(error_code_); return errno; } public: StrError(int err_code, char *&buf, std::size_t buf_size) : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} int run() { // Suppress a warning about unused strerror_r. strerror_r(0, FMT_NULL, ""); return handle(strerror_r(error_code_, buffer_, buffer_size_)); } }; return StrError(error_code, buffer, buffer_size).run(); } void format_error_code(Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { // Report error code making sure that the output fits into // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential // bad_alloc. out.clear(); static const char SEP[] = ": "; static const char ERROR_STR[] = "error "; // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; typedef internal::IntTraits::MainType MainType; MainType abs_value = static_cast(error_code); if (internal::is_negative(error_code)) { abs_value = 0 - abs_value; ++error_code_size; } error_code_size += internal::count_digits(abs_value); if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) out << message << SEP; out << ERROR_STR << error_code; assert(out.size() <= internal::INLINE_BUFFER_SIZE); } void report_error(FormatFunc func, int error_code, StringRef message) FMT_NOEXCEPT { MemoryWriter full_message; func(full_message, error_code, message); // Use Writer::data instead of Writer::c_str to avoid potential memory // allocation. std::fwrite(full_message.data(), full_message.size(), 1, stderr); std::fputc('\n', stderr); } // IsZeroInt::visit(arg) returns true iff arg is a zero integer. class IsZeroInt : public ArgVisitor { public: template bool visit_any_int(T value) { return value == 0; } }; // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. class WidthHandler : public ArgVisitor { private: FormatSpec &spec_; FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); public: explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} void report_unhandled_arg() { FMT_THROW(FormatError("width is not integer")); } template unsigned visit_any_int(T value) { typedef typename internal::IntTraits::MainType UnsignedType; UnsignedType width = static_cast(value); if (internal::is_negative(value)) { spec_.align_ = ALIGN_LEFT; width = 0 - width; } if (width > INT_MAX) FMT_THROW(FormatError("number is too big")); return static_cast(width); } }; class PrecisionHandler : public ArgVisitor { public: void report_unhandled_arg() { FMT_THROW(FormatError("precision is not integer")); } template int visit_any_int(T value) { if (!IntChecker::is_signed>::fits_in_int(value)) FMT_THROW(FormatError("number is too big")); return static_cast(value); } }; template struct is_same { enum { value = 0 }; }; template struct is_same { enum { value = 1 }; }; // An argument visitor that converts an integer argument to T for printf, // if T is an integral type. If T is void, the argument is converted to // corresponding signed or unsigned type depending on the type specifier: // 'd' and 'i' - signed, other - unsigned) template class ArgConverter : public ArgVisitor, void> { private: internal::Arg &arg_; wchar_t type_; FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); public: ArgConverter(internal::Arg &arg, wchar_t type) : arg_(arg), type_(type) {} void visit_bool(bool value) { if (type_ != 's') visit_any_int(value); } template void visit_any_int(U value) { bool is_signed = type_ == 'd' || type_ == 'i'; using internal::Arg; typedef typename internal::Conditional< is_same::value, U, T>::type TargetType; if (sizeof(TargetType) <= sizeof(int)) { // Extra casts are used to silence warnings. if (is_signed) { arg_.type = Arg::INT; arg_.int_value = static_cast(static_cast(value)); } else { arg_.type = Arg::UINT; typedef typename internal::MakeUnsigned::Type Unsigned; arg_.uint_value = static_cast(static_cast(value)); } } else { if (is_signed) { arg_.type = Arg::LONG_LONG; // glibc's printf doesn't sign extend arguments of smaller types: // std::printf("%lld", -42); // prints "4294967254" // but we don't have to do the same because it's a UB. arg_.long_long_value = static_cast(value); } else { arg_.type = Arg::ULONG_LONG; arg_.ulong_long_value = static_cast::Type>(value); } } } }; // Converts an integer argument to char for printf. class CharConverter : public ArgVisitor { private: internal::Arg &arg_; FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); public: explicit CharConverter(internal::Arg &arg) : arg_(arg) {} template void visit_any_int(T value) { arg_.type = internal::Arg::CHAR; arg_.int_value = static_cast(value); } }; } // namespace namespace internal { template class PrintfArgFormatter : public ArgFormatterBase, Char> { void write_null_pointer() { this->spec().type_ = 0; this->write("(nil)"); } typedef ArgFormatterBase, Char> Base; public: PrintfArgFormatter(BasicWriter &w, FormatSpec &s) : ArgFormatterBase, Char>(w, s) {} void visit_bool(bool value) { FormatSpec &fmt_spec = this->spec(); if (fmt_spec.type_ != 's') return this->visit_any_int(value); fmt_spec.type_ = 0; this->write(value); } void visit_char(int value) { const FormatSpec &fmt_spec = this->spec(); BasicWriter &w = this->writer(); if (fmt_spec.type_ && fmt_spec.type_ != 'c') w.write_int(value, fmt_spec); typedef typename BasicWriter::CharPtr CharPtr; CharPtr out = CharPtr(); if (fmt_spec.width_ > 1) { Char fill = ' '; out = w.grow_buffer(fmt_spec.width_); if (fmt_spec.align_ != ALIGN_LEFT) { std::fill_n(out, fmt_spec.width_ - 1, fill); out += fmt_spec.width_ - 1; } else { std::fill_n(out + 1, fmt_spec.width_ - 1, fill); } } else { out = w.grow_buffer(1); } *out = static_cast(value); } void visit_cstring(const char *value) { if (value) Base::visit_cstring(value); else if (this->spec().type_ == 'p') write_null_pointer(); else this->write("(null)"); } void visit_pointer(const void *value) { if (value) return Base::visit_pointer(value); this->spec().type_ = 0; write_null_pointer(); } void visit_custom(Arg::CustomValue c) { BasicFormatter formatter(ArgList(), this->writer()); const Char format_str[] = {'}', 0}; const Char *format = format_str; c.format(&formatter, c.value, &format); } }; } // namespace internal } // namespace fmt FMT_FUNC void fmt::SystemError::init( int err_code, CStringRef format_str, ArgList args) { error_code_ = err_code; MemoryWriter w; internal::format_system_error(w, err_code, format(format_str, args)); std::runtime_error &base = *this; base = std::runtime_error(w.str()); } template int fmt::internal::CharTraits::format_float( char *buffer, std::size_t size, const char *format, unsigned width, int precision, T value) { if (width == 0) { return precision < 0 ? FMT_SNPRINTF(buffer, size, format, value) : FMT_SNPRINTF(buffer, size, format, precision, value); } return precision < 0 ? FMT_SNPRINTF(buffer, size, format, width, value) : FMT_SNPRINTF(buffer, size, format, width, precision, value); } template int fmt::internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, T value) { if (width == 0) { return precision < 0 ? FMT_SWPRINTF(buffer, size, format, value) : FMT_SWPRINTF(buffer, size, format, precision, value); } return precision < 0 ? FMT_SWPRINTF(buffer, size, format, width, value) : FMT_SWPRINTF(buffer, size, format, width, precision, value); } template const char fmt::internal::BasicData::DIGITS[] = "0001020304050607080910111213141516171819" "2021222324252627282930313233343536373839" "4041424344454647484950515253545556575859" "6061626364656667686970717273747576777879" "8081828384858687888990919293949596979899"; #define FMT_POWERS_OF_10(factor) \ factor * 10, \ factor * 100, \ factor * 1000, \ factor * 10000, \ factor * 100000, \ factor * 1000000, \ factor * 10000000, \ factor * 100000000, \ factor * 1000000000 template const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { 0, FMT_POWERS_OF_10(1) }; template const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), // Multiply several constants instead of using a single long long constant // to avoid warnings about C++98 not supporting long long. fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 }; FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { (void)type; if (std::isprint(static_cast(code))) { FMT_THROW(fmt::FormatError( fmt::format("unknown format code '{}' for {}", code, type))); } FMT_THROW(fmt::FormatError( fmt::format("unknown format code '\\x{:02x}' for {}", static_cast(code), type))); } #if FMT_USE_WINDOWS_H FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) { static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; if (s.size() > INT_MAX) FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); int s_size = static_cast(s.size()); int length = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0); if (length == 0) FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); buffer_.resize(length + 1); length = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); if (length == 0) FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); buffer_[length] = 0; } FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { if (int error_code = convert(s)) { FMT_THROW(WindowsError(error_code, "cannot convert string from UTF-16 to UTF-8")); } } FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; int s_size = static_cast(s.size()); int length = WideCharToMultiByte( CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL); if (length == 0) return GetLastError(); buffer_.resize(length + 1); length = WideCharToMultiByte( CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL); if (length == 0) return GetLastError(); buffer_[length] = 0; return 0; } FMT_FUNC void fmt::WindowsError::init( int err_code, CStringRef format_str, ArgList args) { error_code_ = err_code; MemoryWriter w; internal::format_windows_error(w, err_code, format(format_str, args)); std::runtime_error &base = *this; base = std::runtime_error(w.str()); } FMT_FUNC void fmt::internal::format_windows_error( fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT { FMT_TRY { MemoryBuffer buffer; buffer.resize(INLINE_BUFFER_SIZE); for (;;) { wchar_t *system_message = &buffer[0]; int result = FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message, static_cast(buffer.size()), FMT_NULL); if (result != 0) { UTF16ToUTF8 utf8_message; if (utf8_message.convert(system_message) == ERROR_SUCCESS) { out << message << ": " << utf8_message; return; } break; } if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break; // Can't get error message, report error code instead. buffer.resize(buffer.size() * 2); } } FMT_CATCH(...) {} fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. } #endif // FMT_USE_WINDOWS_H FMT_FUNC void fmt::internal::format_system_error( fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT { FMT_TRY { MemoryBuffer buffer; buffer.resize(INLINE_BUFFER_SIZE); for (;;) { char *system_message = &buffer[0]; int result = safe_strerror(error_code, system_message, buffer.size()); if (result == 0) { out << message << ": " << system_message; return; } if (result != ERANGE) break; // Can't get error message, report error code instead. buffer.resize(buffer.size() * 2); } } FMT_CATCH(...) {} fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. } template void fmt::internal::ArgMap::init(const ArgList &args) { if (!map_.empty()) return; typedef internal::NamedArg NamedArg; const NamedArg *named_arg = FMT_NULL; bool use_values = args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; if (use_values) { for (unsigned i = 0;/*nothing*/; ++i) { internal::Arg::Type arg_type = args.type(i); switch (arg_type) { case internal::Arg::NONE: return; case internal::Arg::NAMED_ARG: named_arg = static_cast(args.values_[i].pointer); map_.push_back(Pair(named_arg->name, *named_arg)); break; default: /*nothing*/; } } return; } for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { internal::Arg::Type arg_type = args.type(i); if (arg_type == internal::Arg::NAMED_ARG) { named_arg = static_cast(args.args_[i].pointer); map_.push_back(Pair(named_arg->name, *named_arg)); } } for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { switch (args.args_[i].type) { case internal::Arg::NONE: return; case internal::Arg::NAMED_ARG: named_arg = static_cast(args.args_[i].pointer); map_.push_back(Pair(named_arg->name, *named_arg)); break; default: /*nothing*/; } } } template void fmt::internal::FixedBuffer::grow(std::size_t) { FMT_THROW(std::runtime_error("buffer overflow")); } FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( unsigned arg_index, const char *&error) { Arg arg = args_[arg_index]; switch (arg.type) { case Arg::NONE: error = "argument index out of range"; break; case Arg::NAMED_ARG: arg = *static_cast(arg.pointer); break; default: /*nothing*/; } return arg; } template void fmt::internal::PrintfFormatter::parse_flags( FormatSpec &spec, const Char *&s) { for (;;) { switch (*s++) { case '-': spec.align_ = ALIGN_LEFT; break; case '+': spec.flags_ |= SIGN_FLAG | PLUS_FLAG; break; case '0': spec.fill_ = '0'; break; case ' ': spec.flags_ |= SIGN_FLAG; break; case '#': spec.flags_ |= HASH_FLAG; break; default: --s; return; } } } template Arg fmt::internal::PrintfFormatter::get_arg( const Char *s, unsigned arg_index) { (void)s; const char *error = 0; Arg arg = arg_index == UINT_MAX ? next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); if (error) FMT_THROW(FormatError(!*s ? "invalid format string" : error)); return arg; } template unsigned fmt::internal::PrintfFormatter::parse_header( const Char *&s, FormatSpec &spec) { unsigned arg_index = UINT_MAX; Char c = *s; if (c >= '0' && c <= '9') { // Parse an argument index (if followed by '$') or a width possibly // preceded with '0' flag(s). unsigned value = parse_nonnegative_int(s); if (*s == '$') { // value is an argument index ++s; arg_index = value; } else { if (c == '0') spec.fill_ = '0'; if (value != 0) { // Nonzero value means that we parsed width and don't need to // parse it or flags again, so return now. spec.width_ = value; return arg_index; } } } parse_flags(spec, s); // Parse width. if (*s >= '0' && *s <= '9') { spec.width_ = parse_nonnegative_int(s); } else if (*s == '*') { ++s; spec.width_ = WidthHandler(spec).visit(get_arg(s)); } return arg_index; } template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, BasicCStringRef format_str) { const Char *start = format_str.c_str(); const Char *s = start; while (*s) { Char c = *s++; if (c != '%') continue; if (*s == c) { write(writer, start, s); start = ++s; continue; } write(writer, start, s - 1); FormatSpec spec; spec.align_ = ALIGN_RIGHT; // Parse argument index, flags and width. unsigned arg_index = parse_header(s, spec); // Parse precision. if (*s == '.') { ++s; if ('0' <= *s && *s <= '9') { spec.precision_ = static_cast(parse_nonnegative_int(s)); } else if (*s == '*') { ++s; spec.precision_ = PrecisionHandler().visit(get_arg(s)); } } Arg arg = get_arg(s, arg_index); if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) spec.flags_ &= ~to_unsigned(HASH_FLAG); if (spec.fill_ == '0') { if (arg.type <= Arg::LAST_NUMERIC_TYPE) spec.align_ = ALIGN_NUMERIC; else spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. } // Parse length and convert the argument to the required type. switch (*s++) { case 'h': if (*s == 'h') ArgConverter(arg, *++s).visit(arg); else ArgConverter(arg, *s).visit(arg); break; case 'l': if (*s == 'l') ArgConverter(arg, *++s).visit(arg); else ArgConverter(arg, *s).visit(arg); break; case 'j': ArgConverter(arg, *s).visit(arg); break; case 'z': ArgConverter(arg, *s).visit(arg); break; case 't': ArgConverter(arg, *s).visit(arg); break; case 'L': // printf produces garbage when 'L' is omitted for long double, no // need to do the same. break; default: --s; ArgConverter(arg, *s).visit(arg); } // Parse type. if (!*s) FMT_THROW(FormatError("invalid format string")); spec.type_ = static_cast(*s++); if (arg.type <= Arg::LAST_INTEGER_TYPE) { // Normalize type. switch (spec.type_) { case 'i': case 'u': spec.type_ = 'd'; break; case 'c': // TODO: handle wchar_t CharConverter(arg).visit(arg); break; } } start = s; // Format argument. internal::PrintfArgFormatter(writer, spec).visit(arg); } write(writer, start, s); } FMT_FUNC void fmt::report_system_error( int error_code, fmt::StringRef message) FMT_NOEXCEPT { // 'fmt::' is for bcc32. fmt::report_error(internal::format_system_error, error_code, message); } #if FMT_USE_WINDOWS_H FMT_FUNC void fmt::report_windows_error( int error_code, fmt::StringRef message) FMT_NOEXCEPT { // 'fmt::' is for bcc32. fmt::report_error(internal::format_windows_error, error_code, message); } #endif FMT_FUNC void fmt::print(std::FILE *f, CStringRef format_str, ArgList args) { MemoryWriter w; w.write(format_str, args); std::fwrite(w.data(), 1, w.size(), f); } FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) { print(stdout, format_str, args); } FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) { char escape[] = "\x1b[30m"; escape[3] = static_cast('0' + c); std::fputs(escape, stdout); print(format, args); std::fputs(RESET_COLOR, stdout); } FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) { MemoryWriter w; printf(w, format, args); std::size_t size = w.size(); return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); } #ifndef FMT_HEADER_ONLY template struct fmt::internal::BasicData; // Explicit instantiations for char. template void fmt::internal::FixedBuffer::grow(std::size_t); template void fmt::internal::ArgMap::init(const fmt::ArgList &args); template FMT_API void fmt::internal::PrintfFormatter::format( BasicWriter &writer, CStringRef format); template FMT_API int fmt::internal::CharTraits::format_float( char *buffer, std::size_t size, const char *format, unsigned width, int precision, double value); template FMT_API int fmt::internal::CharTraits::format_float( char *buffer, std::size_t size, const char *format, unsigned width, int precision, long double value); // Explicit instantiations for wchar_t. template void fmt::internal::FixedBuffer::grow(std::size_t); template void fmt::internal::ArgMap::init(const fmt::ArgList &args); template FMT_API void fmt::internal::PrintfFormatter::format( BasicWriter &writer, WCStringRef format); template FMT_API int fmt::internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, double value); template int FMT_API fmt::internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, long double value); #endif // FMT_HEADER_ONLY #ifdef _MSC_VER # pragma warning(pop) #endif DarkRadiant-2.5.0/libs/libfmt/fmt/format.h000066400000000000000000003517701321750546400203630ustar00rootroot00000000000000/* Formatting library for C++ Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ #include #include #include #include #include #include #include #include #include #include #include // for std::pair // The fmt library version in the form major * 10000 + minor * 100 + patch. #define FMT_VERSION 30002 #ifdef _SECURE_SCL # define FMT_SECURE_SCL _SECURE_SCL #else # define FMT_SECURE_SCL 0 #endif #if FMT_SECURE_SCL # include #endif #ifdef _MSC_VER # define FMT_MSC_VER _MSC_VER #else # define FMT_MSC_VER 0 #endif #if FMT_MSC_VER && FMT_MSC_VER <= 1500 typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; typedef __int64 intmax_t; #else #include #endif #if !defined(FMT_HEADER_ONLY) && defined(_WIN32) # ifdef FMT_EXPORT # define FMT_API __declspec(dllexport) # elif defined(FMT_SHARED) # define FMT_API __declspec(dllimport) # endif #endif #ifndef FMT_API # define FMT_API #endif #ifdef __GNUC__ # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # define FMT_GCC_EXTENSION __extension__ # if FMT_GCC_VERSION >= 406 # pragma GCC diagnostic push // Disable the warning about "long long" which is sometimes reported even // when using __extension__. # pragma GCC diagnostic ignored "-Wlong-long" // Disable the warning about declaration shadowing because it affects too // many valid cases. # pragma GCC diagnostic ignored "-Wshadow" // Disable the warning about implicit conversions that may change the sign of // an integer; silencing it otherwise would require many explicit casts. # pragma GCC diagnostic ignored "-Wsign-conversion" # endif # if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ # define FMT_HAS_GXX_CXX11 1 # endif #else # define FMT_GCC_EXTENSION #endif #if defined(__INTEL_COMPILER) # define FMT_ICC_VERSION __INTEL_COMPILER #elif defined(__ICL) # define FMT_ICC_VERSION __ICL #endif #if defined(__clang__) && !defined(FMT_ICC_VERSION) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdocumentation-unknown-command" # pragma clang diagnostic ignored "-Wpadded" #endif #ifdef __GNUC_LIBSTD__ # define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) #endif #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x) #else # define FMT_HAS_FEATURE(x) 0 #endif #ifdef __has_builtin # define FMT_HAS_BUILTIN(x) __has_builtin(x) #else # define FMT_HAS_BUILTIN(x) 0 #endif #ifdef __has_cpp_attribute # define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) #else # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif #ifndef FMT_USE_VARIADIC_TEMPLATES // Variadic templates are available in GCC since version 4.4 // (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ // since version 2013. # define FMT_USE_VARIADIC_TEMPLATES \ (FMT_HAS_FEATURE(cxx_variadic_templates) || \ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800) #endif #ifndef FMT_USE_RVALUE_REFERENCES // Don't use rvalue references when compiling with clang and an old libstdc++ // as the latter doesn't provide std::move. # if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 # define FMT_USE_RVALUE_REFERENCES 0 # else # define FMT_USE_RVALUE_REFERENCES \ (FMT_HAS_FEATURE(cxx_rvalue_references) || \ (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600) # endif #endif // Check if exceptions are disabled. #if defined(__GNUC__) && !defined(__EXCEPTIONS) # define FMT_EXCEPTIONS 0 #endif #if FMT_MSC_VER && !_HAS_EXCEPTIONS # define FMT_EXCEPTIONS 0 #endif #ifndef FMT_EXCEPTIONS # define FMT_EXCEPTIONS 1 #endif #ifndef FMT_THROW # if FMT_EXCEPTIONS # define FMT_THROW(x) throw x # else # define FMT_THROW(x) assert(false) # endif #endif // Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). #ifndef FMT_USE_NOEXCEPT # define FMT_USE_NOEXCEPT 0 #endif #if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ FMT_MSC_VER >= 1900 # define FMT_DETECTED_NOEXCEPT noexcept #else # define FMT_DETECTED_NOEXCEPT throw() #endif #ifndef FMT_NOEXCEPT # if FMT_EXCEPTIONS # define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT # else # define FMT_NOEXCEPT # endif #endif // This is needed because GCC still uses throw() in its headers when exceptions // are disabled. #if FMT_GCC_VERSION # define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT #else # define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT #endif #ifndef FMT_OVERRIDE # if (defined(FMT_USE_OVERRIDE) && FMT_USE_OVERRIDE) || FMT_HAS_FEATURE(cxx_override) || \ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ FMT_MSC_VER >= 1900 # define FMT_OVERRIDE override # else # define FMT_OVERRIDE # endif #endif #ifndef FMT_NULL # if FMT_HAS_FEATURE(cxx_nullptr) || \ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ FMT_MSC_VER >= 1600 # define FMT_NULL nullptr # else # define FMT_NULL NULL # endif #endif // A macro to disallow the copy constructor and operator= functions // This should be used in the private: declarations for a class #ifndef FMT_USE_DELETED_FUNCTIONS # define FMT_USE_DELETED_FUNCTIONS 0 #endif #if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800 # define FMT_DELETED_OR_UNDEFINED = delete # define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&) = delete; \ TypeName& operator=(const TypeName&) = delete #else # define FMT_DELETED_OR_UNDEFINED # define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ TypeName& operator=(const TypeName&) #endif #ifndef FMT_USE_USER_DEFINED_LITERALS // All compilers which support UDLs also support variadic templates. This // makes the fmt::literals implementation easier. However, an explicit check // for variadic templates is added here just in case. // For Intel's compiler both it and the system gcc/msc must support UDLs. # define FMT_USE_USER_DEFINED_LITERALS \ FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \ (FMT_HAS_FEATURE(cxx_user_literals) || \ (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \ (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) #endif #ifndef FMT_USE_EXTERN_TEMPLATES // Clang doesn't have a feature check for extern templates so we check // for variadic templates which were introduced in the same version. // For GCC according to cppreference.com they were introduced in 3.3. # define FMT_USE_EXTERN_TEMPLATES \ ((__clang__ && FMT_USE_VARIADIC_TEMPLATES) || \ (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) #endif #ifdef FMT_HEADER_ONLY // If header only do not use extern templates. # undef FMT_USE_EXTERN_TEMPLATES # define FMT_USE_EXTERN_TEMPLATES 0 #endif #ifndef FMT_ASSERT # define FMT_ASSERT(condition, message) assert((condition) && message) #endif #if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) # define FMT_BUILTIN_CLZ(n) __builtin_clz(n) #endif #if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) # define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) #endif // Some compilers masquerade as both MSVC and GCC-likes or // otherwise support __builtin_clz and __builtin_clzll, so // only define FMT_BUILTIN_CLZ using the MSVC intrinsics // if the clz and clzll builtins are not available. #if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED) # include // _BitScanReverse, _BitScanReverse64 namespace fmt { namespace internal { # pragma intrinsic(_BitScanReverse) inline uint32_t clz(uint32_t x) { unsigned long r = 0; _BitScanReverse(&r, x); assert(x != 0); // Static analysis complains about using uninitialized data // "r", but the only way that can happen is if "x" is 0, // which the callers guarantee to not happen. # pragma warning(suppress: 6102) return 31 - r; } # define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) # ifdef _WIN64 # pragma intrinsic(_BitScanReverse64) # endif inline uint32_t clzll(uint64_t x) { unsigned long r = 0; # ifdef _WIN64 _BitScanReverse64(&r, x); # else // Scan the high 32 bits. if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 - (r + 32); // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); # endif assert(x != 0); // Static analysis complains about using uninitialized data // "r", but the only way that can happen is if "x" is 0, // which the callers guarantee to not happen. # pragma warning(suppress: 6102) return 63 - r; } # define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) } } #endif namespace fmt { namespace internal { struct DummyInt { int data[2]; operator int() const { return 0; } }; typedef std::numeric_limits FPUtil; // Dummy implementations of system functions such as signbit and ecvt called // if the latter are not available. inline DummyInt signbit(...) { return DummyInt(); } inline DummyInt _ecvt_s(...) { return DummyInt(); } inline DummyInt isinf(...) { return DummyInt(); } inline DummyInt _finite(...) { return DummyInt(); } inline DummyInt isnan(...) { return DummyInt(); } inline DummyInt _isnan(...) { return DummyInt(); } // A helper function to suppress bogus "conditional expression is constant" // warnings. template inline T const_check(T value) { return value; } } } // namespace fmt namespace std { // Standard permits specialization of std::numeric_limits. This specialization // is used to resolve ambiguity between isinf and std::isinf in glibc: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891 // and the same for isnan and signbit. template <> class numeric_limits : public std::numeric_limits { public: // Portable version of isinf. template static bool isinfinity(T x) { using namespace fmt::internal; // The resolution "priority" is: // isinf macro > std::isinf > ::isinf > fmt::internal::isinf if (const_check(sizeof(isinf(x)) == sizeof(bool) || sizeof(isinf(x)) == sizeof(int))) { return isinf(x) != 0; } return !_finite(static_cast(x)); } // Portable version of isnan. template static bool isnotanumber(T x) { using namespace fmt::internal; if (const_check(sizeof(isnan(x)) == sizeof(bool) || sizeof(isnan(x)) == sizeof(int))) { return isnan(x) != 0; } return _isnan(static_cast(x)) != 0; } // Portable version of signbit. static bool isnegative(double x) { using namespace fmt::internal; if (const_check(sizeof(signbit(x)) == sizeof(bool) || sizeof(signbit(x)) == sizeof(int))) { return signbit(x) != 0; } if (x < 0) return true; if (!isnotanumber(x)) return false; int dec = 0, sign = 0; char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign); return sign != 0; } }; } // namespace std namespace fmt { // Fix the warning about long long on older versions of GCC // that don't support the diagnostic pragma. FMT_GCC_EXTENSION typedef long long LongLong; FMT_GCC_EXTENSION typedef unsigned long long ULongLong; #if FMT_USE_RVALUE_REFERENCES using std::move; #endif template class BasicWriter; typedef BasicWriter Writer; typedef BasicWriter WWriter; template class ArgFormatter; template > class BasicFormatter; /** \rst A string reference. It can be constructed from a C string or ``std::string``. You can use one of the following typedefs for common character types: +------------+-------------------------+ | Type | Definition | +============+=========================+ | StringRef | BasicStringRef | +------------+-------------------------+ | WStringRef | BasicStringRef | +------------+-------------------------+ This class is most useful as a parameter type to allow passing different types of strings to a function, for example:: template std::string format(StringRef format_str, const Args & ... args); format("{}", 42); format(std::string("{}"), 42); \endrst */ template class BasicStringRef { private: const Char *data_; std::size_t size_; public: /** Constructs a string reference object from a C string and a size. */ BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} /** \rst Constructs a string reference object from a C string computing the size with ``std::char_traits::length``. \endrst */ BasicStringRef(const Char *s) : data_(s), size_(std::char_traits::length(s)) {} /** \rst Constructs a string reference from an ``std::string`` object. \endrst */ BasicStringRef(const std::basic_string &s) : data_(s.c_str()), size_(s.size()) {} /** \rst Converts a string reference to an ``std::string`` object. \endrst */ std::basic_string to_string() const { return std::basic_string(data_, size_); } /** Returns a pointer to the string data. */ const Char *data() const { return data_; } /** Returns the string size. */ std::size_t size() const { return size_; } // Lexicographically compare this string reference to other. int compare(BasicStringRef other) const { std::size_t size = size_ < other.size_ ? size_ : other.size_; int result = std::char_traits::compare(data_, other.data_, size); if (result == 0) result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); return result; } friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) == 0; } friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) != 0; } friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) < 0; } friend bool operator<=(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) <= 0; } friend bool operator>(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) > 0; } friend bool operator>=(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) >= 0; } }; typedef BasicStringRef StringRef; typedef BasicStringRef WStringRef; /** \rst A reference to a null terminated string. It can be constructed from a C string or ``std::string``. You can use one of the following typedefs for common character types: +-------------+--------------------------+ | Type | Definition | +=============+==========================+ | CStringRef | BasicCStringRef | +-------------+--------------------------+ | WCStringRef | BasicCStringRef | +-------------+--------------------------+ This class is most useful as a parameter type to allow passing different types of strings to a function, for example:: template std::string format(CStringRef format_str, const Args & ... args); format("{}", 42); format(std::string("{}"), 42); \endrst */ template class BasicCStringRef { private: const Char *data_; public: /** Constructs a string reference object from a C string. */ BasicCStringRef(const Char *s) : data_(s) {} /** \rst Constructs a string reference from an ``std::string`` object. \endrst */ BasicCStringRef(const std::basic_string &s) : data_(s.c_str()) {} /** Returns the pointer to a C string. */ const Char *c_str() const { return data_; } }; typedef BasicCStringRef CStringRef; typedef BasicCStringRef WCStringRef; /** A formatting error such as invalid format string. */ class FormatError : public std::runtime_error { public: explicit FormatError(CStringRef message) : std::runtime_error(message.c_str()) {} FormatError(const FormatError &ferr) : std::runtime_error(ferr) {} FMT_API ~FormatError() FMT_DTOR_NOEXCEPT; }; namespace internal { // MakeUnsigned::Type gives an unsigned type corresponding to integer type T. template struct MakeUnsigned { typedef T Type; }; #define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ template <> \ struct MakeUnsigned { typedef U Type; } FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); // Casts nonnegative integer to unsigned. template inline typename MakeUnsigned::Type to_unsigned(Int value) { FMT_ASSERT(value >= 0, "negative value"); return static_cast::Type>(value); } // The number of characters to store in the MemoryBuffer object itself // to avoid dynamic memory allocation. enum { INLINE_BUFFER_SIZE = 500 }; #if FMT_SECURE_SCL // Use checked iterator to avoid warnings on MSVC. template inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { return stdext::checked_array_iterator(ptr, size); } #else template inline T *make_ptr(T *ptr, std::size_t) { return ptr; } #endif } // namespace internal /** \rst A buffer supporting a subset of ``std::vector``'s operations. \endrst */ template class Buffer { private: FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); protected: T *ptr_; std::size_t size_; std::size_t capacity_; Buffer(T *ptr = FMT_NULL, std::size_t capacity = 0) : ptr_(ptr), size_(0), capacity_(capacity) {} /** \rst Increases the buffer capacity to hold at least *size* elements updating ``ptr_`` and ``capacity_``. \endrst */ virtual void grow(std::size_t size) = 0; public: virtual ~Buffer() {} /** Returns the size of this buffer. */ std::size_t size() const { return size_; } /** Returns the capacity of this buffer. */ std::size_t capacity() const { return capacity_; } /** Resizes the buffer. If T is a POD type new elements may not be initialized. */ void resize(std::size_t new_size) { if (new_size > capacity_) grow(new_size); size_ = new_size; } /** \rst Reserves space to store at least *capacity* elements. \endrst */ void reserve(std::size_t capacity) { if (capacity > capacity_) grow(capacity); } void clear() FMT_NOEXCEPT { size_ = 0; } void push_back(const T &value) { if (size_ == capacity_) grow(size_ + 1); ptr_[size_++] = value; } /** Appends data to the end of the buffer. */ template void append(const U *begin, const U *end); T &operator[](std::size_t index) { return ptr_[index]; } const T &operator[](std::size_t index) const { return ptr_[index]; } }; template template void Buffer::append(const U *begin, const U *end) { FMT_ASSERT(end >= begin, "negative value"); std::size_t new_size = size_ + (end - begin); if (new_size > capacity_) grow(new_size); std::uninitialized_copy(begin, end, internal::make_ptr(ptr_, capacity_) + size_); size_ = new_size; } namespace internal { // A memory buffer for trivially copyable/constructible types with the first // SIZE elements stored in the object itself. template > class MemoryBuffer : private Allocator, public Buffer { private: T data_[SIZE]; // Deallocate memory allocated by the buffer. void deallocate() { if (this->ptr_ != data_) Allocator::deallocate(this->ptr_, this->capacity_); } protected: void grow(std::size_t size) FMT_OVERRIDE; public: explicit MemoryBuffer(const Allocator &alloc = Allocator()) : Allocator(alloc), Buffer(data_, SIZE) {} ~MemoryBuffer() { deallocate(); } #if FMT_USE_RVALUE_REFERENCES private: // Move data from other to this buffer. void move(MemoryBuffer &other) { Allocator &this_alloc = *this, &other_alloc = other; this_alloc = std::move(other_alloc); this->size_ = other.size_; this->capacity_ = other.capacity_; if (other.ptr_ == other.data_) { this->ptr_ = data_; std::uninitialized_copy(other.data_, other.data_ + this->size_, make_ptr(data_, this->capacity_)); } else { this->ptr_ = other.ptr_; // Set pointer to the inline array so that delete is not called // when deallocating. other.ptr_ = other.data_; } } public: MemoryBuffer(MemoryBuffer &&other) { move(other); } MemoryBuffer &operator=(MemoryBuffer &&other) { assert(this != &other); deallocate(); move(other); return *this; } #endif // Returns a copy of the allocator associated with this buffer. Allocator get_allocator() const { return *this; } }; template void MemoryBuffer::grow(std::size_t size) { std::size_t new_capacity = this->capacity_ + this->capacity_ / 2; if (size > new_capacity) new_capacity = size; T *new_ptr = this->allocate(new_capacity, FMT_NULL); // The following code doesn't throw, so the raw pointer above doesn't leak. std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); std::size_t old_capacity = this->capacity_; T *old_ptr = this->ptr_; this->capacity_ = new_capacity; this->ptr_ = new_ptr; // deallocate may throw (at least in principle), but it doesn't matter since // the buffer already uses the new storage and will deallocate it in case // of exception. if (old_ptr != data_) Allocator::deallocate(old_ptr, old_capacity); } // A fixed-size buffer. template class FixedBuffer : public fmt::Buffer { public: FixedBuffer(Char *array, std::size_t size) : fmt::Buffer(array, size) {} protected: FMT_API void grow(std::size_t size) FMT_OVERRIDE; }; template class BasicCharTraits { public: #if FMT_SECURE_SCL typedef stdext::checked_array_iterator CharPtr; #else typedef Char *CharPtr; #endif static Char cast(int value) { return static_cast(value); } }; template class CharTraits; template <> class CharTraits : public BasicCharTraits { private: // Conversion from wchar_t to char is not allowed. static char convert(wchar_t); public: static char convert(char value) { return value; } // Formats a floating-point number. template FMT_API static int format_float(char *buffer, std::size_t size, const char *format, unsigned width, int precision, T value); }; #if FMT_USE_EXTERN_TEMPLATES extern template int CharTraits::format_float (char *buffer, std::size_t size, const char* format, unsigned width, int precision, double value); extern template int CharTraits::format_float (char *buffer, std::size_t size, const char* format, unsigned width, int precision, long double value); #endif template <> class CharTraits : public BasicCharTraits { public: static wchar_t convert(char value) { return value; } static wchar_t convert(wchar_t value) { return value; } template FMT_API static int format_float(wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, T value); }; #if FMT_USE_EXTERN_TEMPLATES extern template int CharTraits::format_float (wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width, int precision, double value); extern template int CharTraits::format_float (wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width, int precision, long double value); #endif // Checks if a number is negative - used to avoid warnings. template struct SignChecker { template static bool is_negative(T value) { return value < 0; } }; template <> struct SignChecker { template static bool is_negative(T) { return false; } }; // Returns true if value is negative, false otherwise. // Same as (value < 0) but doesn't produce warnings if T is an unsigned type. template inline bool is_negative(T value) { return SignChecker::is_signed>::is_negative(value); } // Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. template struct TypeSelector { typedef uint32_t Type; }; template <> struct TypeSelector { typedef uint64_t Type; }; template struct IntTraits { // Smallest of uint32_t and uint64_t that is large enough to represent // all values of T. typedef typename TypeSelector::digits <= 32>::Type MainType; }; FMT_API void report_unknown_type(char code, const char *type); // Static data is placed in this class template to allow header-only // configuration. template struct FMT_API BasicData { static const uint32_t POWERS_OF_10_32[]; static const uint64_t POWERS_OF_10_64[]; static const char DIGITS[]; }; #if FMT_USE_EXTERN_TEMPLATES extern template struct BasicData; #endif typedef BasicData<> Data; #ifdef FMT_BUILTIN_CLZLL // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case count_digits returns 1. inline unsigned count_digits(uint64_t n) { // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1; } #else // Fallback version of count_digits used when __builtin_clz is not available. inline unsigned count_digits(uint64_t n) { unsigned count = 1; for (;;) { // Integer division is slow so do it for a group of four digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. if (n < 10) return count; if (n < 100) return count + 1; if (n < 1000) return count + 2; if (n < 10000) return count + 3; n /= 10000u; count += 4; } } #endif #ifdef FMT_BUILTIN_CLZ // Optional version of count_digits for better performance on 32-bit platforms. inline unsigned count_digits(uint32_t n) { int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1; } #endif // A functor that doesn't add a thousands separator. struct NoThousandsSep { template void operator()(Char *) {} }; // A functor that adds a thousands separator. class ThousandsSep { private: fmt::StringRef sep_; // Index of a decimal digit with the least significant digit having index 0. unsigned digit_index_; public: explicit ThousandsSep(fmt::StringRef sep) : sep_(sep), digit_index_(0) {} template void operator()(Char *&buffer) { if (++digit_index_ % 3 != 0) return; buffer -= sep_.size(); std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(), internal::make_ptr(buffer, sep_.size())); } }; // Formats a decimal unsigned integer value writing into buffer. // thousands_sep is a functor that is called after writing each char to // add a thousands separator if necessary. template inline void format_decimal(Char *buffer, UInt value, unsigned num_digits, ThousandsSep thousands_sep) { buffer += num_digits; while (value >= 100) { // Integer division is slow so do it for a group of two digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. unsigned index = static_cast((value % 100) * 2); value /= 100; *--buffer = Data::DIGITS[index + 1]; thousands_sep(buffer); *--buffer = Data::DIGITS[index]; thousands_sep(buffer); } if (value < 10) { *--buffer = static_cast('0' + value); return; } unsigned index = static_cast(value * 2); *--buffer = Data::DIGITS[index + 1]; thousands_sep(buffer); *--buffer = Data::DIGITS[index]; } template inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { format_decimal(buffer, value, num_digits, NoThousandsSep()); return; } #ifndef _WIN32 # define FMT_USE_WINDOWS_H 0 #elif !defined(FMT_USE_WINDOWS_H) # define FMT_USE_WINDOWS_H 1 #endif // Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h. // All the functionality that relies on it will be disabled too. #if FMT_USE_WINDOWS_H // A converter from UTF-8 to UTF-16. // It is only provided for Windows since other systems support UTF-8 natively. class UTF8ToUTF16 { private: MemoryBuffer buffer_; public: FMT_API explicit UTF8ToUTF16(StringRef s); operator WStringRef() const { return WStringRef(&buffer_[0], size()); } size_t size() const { return buffer_.size() - 1; } const wchar_t *c_str() const { return &buffer_[0]; } std::wstring str() const { return std::wstring(&buffer_[0], size()); } }; // A converter from UTF-16 to UTF-8. // It is only provided for Windows since other systems support UTF-8 natively. class UTF16ToUTF8 { private: MemoryBuffer buffer_; public: UTF16ToUTF8() {} FMT_API explicit UTF16ToUTF8(WStringRef s); operator StringRef() const { return StringRef(&buffer_[0], size()); } size_t size() const { return buffer_.size() - 1; } const char *c_str() const { return &buffer_[0]; } std::string str() const { return std::string(&buffer_[0], size()); } // Performs conversion returning a system error code instead of // throwing exception on conversion error. This method may still throw // in case of memory allocation error. FMT_API int convert(WStringRef s); }; FMT_API void format_windows_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; #endif FMT_API void format_system_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; // A formatting argument value. struct Value { template struct StringValue { const Char *value; std::size_t size; }; typedef void (*FormatFunc)( void *formatter, const void *arg, void *format_str_ptr); struct CustomValue { const void *value; FormatFunc format; }; union { int int_value; unsigned uint_value; LongLong long_long_value; ULongLong ulong_long_value; double double_value; long double long_double_value; const void *pointer; StringValue string; StringValue sstring; StringValue ustring; StringValue wstring; CustomValue custom; }; enum Type { NONE, NAMED_ARG, // Integer types should go first, INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, // followed by floating-point types. DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, CSTRING, STRING, WSTRING, POINTER, CUSTOM }; }; // A formatting argument. It is a trivially copyable/constructible type to // allow storage in internal::MemoryBuffer. struct Arg : Value { Type type; }; template struct NamedArg; template struct Null {}; // A helper class template to enable or disable overloads taking wide // characters and strings in MakeValue. template struct WCharHelper { typedef Null Supported; typedef T Unsupported; }; template struct WCharHelper { typedef T Supported; typedef Null Unsupported; }; typedef char Yes[1]; typedef char No[2]; template T &get(); // These are non-members to workaround an overload resolution bug in bcc32. Yes &convert(fmt::ULongLong); No &convert(...); template struct ConvertToIntImpl { enum { value = ENABLE_CONVERSION }; }; template struct ConvertToIntImpl2 { enum { value = false }; }; template struct ConvertToIntImpl2 { enum { // Don't convert numeric types. value = ConvertToIntImpl::is_specialized>::value }; }; template struct ConvertToInt { enum { enable_conversion = sizeof(fmt::internal::convert(get())) == sizeof(Yes) }; enum { value = ConvertToIntImpl2::value }; }; #define FMT_DISABLE_CONVERSION_TO_INT(Type) \ template <> \ struct ConvertToInt { enum { value = 0 }; } // Silence warnings about convering float to int. FMT_DISABLE_CONVERSION_TO_INT(float); FMT_DISABLE_CONVERSION_TO_INT(double); FMT_DISABLE_CONVERSION_TO_INT(long double); template struct EnableIf {}; template struct EnableIf { typedef T type; }; template struct Conditional { typedef T type; }; template struct Conditional { typedef F type; }; // For bcc32 which doesn't understand ! in template arguments. template struct Not { enum { value = 0 }; }; template<> struct Not { enum { value = 1 }; }; template struct LConvCheck { LConvCheck(int) {} }; // Returns the thousands separator for the current locale. // We check if ``lconv`` contains ``thousands_sep`` because on Android // ``lconv`` is stubbed as an empty struct. template inline StringRef thousands_sep( LConv *lc, LConvCheck = 0) { return lc->thousands_sep; } inline fmt::StringRef thousands_sep(...) { return ""; } // Makes an Arg object from any type. template class MakeValue : public Arg { public: typedef typename Formatter::Char Char; private: // The following two methods are private to disallow formatting of // arbitrary pointers. If you want to output a pointer cast it to // "void *" or "const void *". In particular, this forbids formatting // of "[const] volatile char *" which is printed as bool by iostreams. // Do not implement! template MakeValue(const T *value); template MakeValue(T *value); // The following methods are private to disallow formatting of wide // characters and strings into narrow strings as in // fmt::format("{}", L"test"); // To fix this, use a wide format string: fmt::format(L"{}", L"test"). #if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED) MakeValue(typename WCharHelper::Unsupported); #endif MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); void set_string(StringRef str) { string.value = str.data(); string.size = str.size(); } void set_string(WStringRef str) { wstring.value = str.data(); wstring.size = str.size(); } // Formats an argument of a custom type, such as a user-defined class. template static void format_custom_arg( void *formatter, const void *arg, void *format_str_ptr) { format(*static_cast(formatter), *static_cast(format_str_ptr), *static_cast(arg)); } public: MakeValue() {} #define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \ MakeValue(Type value) { field = rhs; } \ static uint64_t type(Type) { return Arg::TYPE; } #define FMT_MAKE_VALUE(Type, field, TYPE) \ FMT_MAKE_VALUE_(Type, field, TYPE, value) FMT_MAKE_VALUE(bool, int_value, BOOL) FMT_MAKE_VALUE(short, int_value, INT) FMT_MAKE_VALUE(unsigned short, uint_value, UINT) FMT_MAKE_VALUE(int, int_value, INT) FMT_MAKE_VALUE(unsigned, uint_value, UINT) MakeValue(long value) { // To minimize the number of types we need to deal with, long is // translated either to int or to long long depending on its size. if (const_check(sizeof(long) == sizeof(int))) int_value = static_cast(value); else long_long_value = value; } static uint64_t type(long) { return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; } MakeValue(unsigned long value) { if (const_check(sizeof(unsigned long) == sizeof(unsigned))) uint_value = static_cast(value); else ulong_long_value = value; } static uint64_t type(unsigned long) { return sizeof(unsigned long) == sizeof(unsigned) ? Arg::UINT : Arg::ULONG_LONG; } FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) FMT_MAKE_VALUE(float, double_value, DOUBLE) FMT_MAKE_VALUE(double, double_value, DOUBLE) FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) FMT_MAKE_VALUE(signed char, int_value, INT) FMT_MAKE_VALUE(unsigned char, uint_value, UINT) FMT_MAKE_VALUE(char, int_value, CHAR) #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) MakeValue(typename WCharHelper::Supported value) { int_value = value; } static uint64_t type(wchar_t) { return Arg::CHAR; } #endif #define FMT_MAKE_STR_VALUE(Type, TYPE) \ MakeValue(Type value) { set_string(value); } \ static uint64_t type(Type) { return Arg::TYPE; } FMT_MAKE_VALUE(char *, string.value, CSTRING) FMT_MAKE_VALUE(const char *, string.value, CSTRING) FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING) FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING) FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) FMT_MAKE_STR_VALUE(const std::string &, STRING) FMT_MAKE_STR_VALUE(StringRef, STRING) FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str()) #define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ MakeValue(typename WCharHelper::Supported value) { \ set_string(value); \ } \ static uint64_t type(Type) { return Arg::TYPE; } FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING) FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING) FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING) FMT_MAKE_VALUE(void *, pointer, POINTER) FMT_MAKE_VALUE(const void *, pointer, POINTER) template MakeValue(const T &value, typename EnableIf::value>::value, int>::type = 0) { custom.value = &value; custom.format = &format_custom_arg; } template static typename EnableIf::value>::value, uint64_t>::type type(const T &) { return Arg::CUSTOM; } // Additional template param `Char_` is needed here because make_type always // uses char. template MakeValue(const NamedArg &value) { pointer = &value; } template static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } }; template class MakeArg : public Arg { public: MakeArg() { type = Arg::NONE; } template MakeArg(const T &value) : Arg(MakeValue(value)) { type = static_cast(MakeValue::type(value)); } }; template struct NamedArg : Arg { BasicStringRef name; template NamedArg(BasicStringRef argname, const T &value) : Arg(MakeArg< BasicFormatter >(value)), name(argname) {} }; class RuntimeError : public std::runtime_error { protected: RuntimeError() : std::runtime_error("") {} RuntimeError(const RuntimeError &rerr) : std::runtime_error(rerr) {} FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT; }; template class PrintfArgFormatter; template class ArgMap; } // namespace internal /** An argument list. */ class ArgList { private: // To reduce compiled code size per formatting function call, types of first // MAX_PACKED_ARGS arguments are passed in the types_ field. uint64_t types_; union { // If the number of arguments is less than MAX_PACKED_ARGS, the argument // values are stored in values_, otherwise they are stored in args_. // This is done to reduce compiled code size as storing larger objects // may require more code (at least on x86-64) even if the same amount of // data is actually copied to stack. It saves ~10% on the bloat test. const internal::Value *values_; const internal::Arg *args_; }; internal::Arg::Type type(unsigned index) const { unsigned shift = index * 4; uint64_t mask = 0xf; return static_cast( (types_ & (mask << shift)) >> shift); } template friend class internal::ArgMap; public: // Maximum number of arguments with packed types. enum { MAX_PACKED_ARGS = 16 }; ArgList() : types_(0) {} ArgList(ULongLong types, const internal::Value *values) : types_(types), values_(values) {} ArgList(ULongLong types, const internal::Arg *args) : types_(types), args_(args) {} /** Returns the argument at specified index. */ internal::Arg operator[](unsigned index) const { using internal::Arg; Arg arg; bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE; if (index < MAX_PACKED_ARGS) { Arg::Type arg_type = type(index); internal::Value &val = arg; if (arg_type != Arg::NONE) val = use_values ? values_[index] : args_[index]; arg.type = arg_type; return arg; } if (use_values) { // The index is greater than the number of arguments that can be stored // in values, so return a "none" argument. arg.type = Arg::NONE; return arg; } for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) { if (args_[i].type == Arg::NONE) return args_[i]; } return args_[index]; } }; #define FMT_DISPATCH(call) static_cast(this)->call /** \rst An argument visitor based on the `curiously recurring template pattern `_. To use `~fmt::ArgVisitor` define a subclass that implements some or all of the visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. Pass the subclass as the *Impl* template parameter. Then calling `~fmt::ArgVisitor::visit` for some argument will dispatch to a visit method specific to the argument type. For example, if the argument type is ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass will be called. If the subclass doesn't contain a method with this signature, then a corresponding method of `~fmt::ArgVisitor` will be called. **Example**:: class MyArgVisitor : public fmt::ArgVisitor { public: void visit_int(int value) { fmt::print("{}", value); } void visit_double(double value) { fmt::print("{}", value ); } }; \endrst */ template class ArgVisitor { private: typedef internal::Arg Arg; public: void report_unhandled_arg() {} Result visit_unhandled_arg() { FMT_DISPATCH(report_unhandled_arg()); return Result(); } /** Visits an ``int`` argument. **/ Result visit_int(int value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits a ``long long`` argument. **/ Result visit_long_long(LongLong value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits an ``unsigned`` argument. **/ Result visit_uint(unsigned value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits an ``unsigned long long`` argument. **/ Result visit_ulong_long(ULongLong value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits a ``bool`` argument. **/ Result visit_bool(bool value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits a ``char`` or ``wchar_t`` argument. **/ Result visit_char(int value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits an argument of any integral type. **/ template Result visit_any_int(T) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a ``double`` argument. **/ Result visit_double(double value) { return FMT_DISPATCH(visit_any_double(value)); } /** Visits a ``long double`` argument. **/ Result visit_long_double(long double value) { return FMT_DISPATCH(visit_any_double(value)); } /** Visits a ``double`` or ``long double`` argument. **/ template Result visit_any_double(T) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a null-terminated C string (``const char *``) argument. **/ Result visit_cstring(const char *) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a string argument. **/ Result visit_string(Arg::StringValue) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a wide string argument. **/ Result visit_wstring(Arg::StringValue) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a pointer argument. **/ Result visit_pointer(const void *) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits an argument of a custom (user-defined) type. **/ Result visit_custom(Arg::CustomValue) { return FMT_DISPATCH(visit_unhandled_arg()); } /** \rst Visits an argument dispatching to the appropriate visit method based on the argument type. For example, if the argument type is ``double`` then the `~fmt::ArgVisitor::visit_double()` method of the *Impl* class will be called. \endrst */ Result visit(const Arg &arg) { switch (arg.type) { case Arg::NONE: case Arg::NAMED_ARG: FMT_ASSERT(false, "invalid argument type"); break; case Arg::INT: return FMT_DISPATCH(visit_int(arg.int_value)); case Arg::UINT: return FMT_DISPATCH(visit_uint(arg.uint_value)); case Arg::LONG_LONG: return FMT_DISPATCH(visit_long_long(arg.long_long_value)); case Arg::ULONG_LONG: return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); case Arg::BOOL: return FMT_DISPATCH(visit_bool(arg.int_value != 0)); case Arg::CHAR: return FMT_DISPATCH(visit_char(arg.int_value)); case Arg::DOUBLE: return FMT_DISPATCH(visit_double(arg.double_value)); case Arg::LONG_DOUBLE: return FMT_DISPATCH(visit_long_double(arg.long_double_value)); case Arg::CSTRING: return FMT_DISPATCH(visit_cstring(arg.string.value)); case Arg::STRING: return FMT_DISPATCH(visit_string(arg.string)); case Arg::WSTRING: return FMT_DISPATCH(visit_wstring(arg.wstring)); case Arg::POINTER: return FMT_DISPATCH(visit_pointer(arg.pointer)); case Arg::CUSTOM: return FMT_DISPATCH(visit_custom(arg.custom)); } return Result(); } }; enum Alignment { ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC }; // Flags. enum { SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. }; // An empty format specifier. struct EmptySpec {}; // A type specifier. template struct TypeSpec : EmptySpec { Alignment align() const { return ALIGN_DEFAULT; } unsigned width() const { return 0; } int precision() const { return -1; } bool flag(unsigned) const { return false; } char type() const { return TYPE; } char fill() const { return ' '; } }; // A width specifier. struct WidthSpec { unsigned width_; // Fill is always wchar_t and cast to char if necessary to avoid having // two specialization of WidthSpec and its subclasses. wchar_t fill_; WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} unsigned width() const { return width_; } wchar_t fill() const { return fill_; } }; // An alignment specifier. struct AlignSpec : WidthSpec { Alignment align_; AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) : WidthSpec(width, fill), align_(align) {} Alignment align() const { return align_; } int precision() const { return -1; } }; // An alignment and type specifier. template struct AlignTypeSpec : AlignSpec { AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} bool flag(unsigned) const { return false; } char type() const { return TYPE; } }; // A full format specifier. struct FormatSpec : AlignSpec { unsigned flags_; int precision_; char type_; FormatSpec( unsigned width = 0, char type = 0, wchar_t fill = ' ') : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} bool flag(unsigned f) const { return (flags_ & f) != 0; } int precision() const { return precision_; } char type() const { return type_; } }; // An integer format specifier. template , typename Char = char> class IntFormatSpec : public SpecT { private: T value_; public: IntFormatSpec(T val, const SpecT &spec = SpecT()) : SpecT(spec), value_(val) {} T value() const { return value_; } }; // A string format specifier. template class StrFormatSpec : public AlignSpec { private: const Char *str_; public: template StrFormatSpec(const Char *str, unsigned width, FillChar fill) : AlignSpec(width, fill), str_(str) { internal::CharTraits::convert(FillChar()); } const Char *str() const { return str_; } }; /** Returns an integer format specifier to format the value in base 2. */ IntFormatSpec > bin(int value); /** Returns an integer format specifier to format the value in base 8. */ IntFormatSpec > oct(int value); /** Returns an integer format specifier to format the value in base 16 using lower-case letters for the digits above 9. */ IntFormatSpec > hex(int value); /** Returns an integer formatter format specifier to format in base 16 using upper-case letters for the digits above 9. */ IntFormatSpec > hexu(int value); /** \rst Returns an integer format specifier to pad the formatted argument with the fill character to the specified width using the default (right) numeric alignment. **Example**:: MemoryWriter out; out << pad(hex(0xcafe), 8, '0'); // out.str() == "0000cafe" \endrst */ template IntFormatSpec, Char> pad( int value, unsigned width, Char fill = ' '); #define FMT_DEFINE_INT_FORMATTERS(TYPE) \ inline IntFormatSpec > bin(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'b'>()); \ } \ \ inline IntFormatSpec > oct(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'o'>()); \ } \ \ inline IntFormatSpec > hex(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'x'>()); \ } \ \ inline IntFormatSpec > hexu(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'X'>()); \ } \ \ template \ inline IntFormatSpec > pad( \ IntFormatSpec > f, unsigned width) { \ return IntFormatSpec >( \ f.value(), AlignTypeSpec(width, ' ')); \ } \ \ /* For compatibility with older compilers we provide two overloads for pad, */ \ /* one that takes a fill character and one that doesn't. In the future this */ \ /* can be replaced with one overload making the template argument Char */ \ /* default to char (C++11). */ \ template \ inline IntFormatSpec, Char> pad( \ IntFormatSpec, Char> f, \ unsigned width, Char fill) { \ return IntFormatSpec, Char>( \ f.value(), AlignTypeSpec(width, fill)); \ } \ \ inline IntFormatSpec > pad( \ TYPE value, unsigned width) { \ return IntFormatSpec >( \ value, AlignTypeSpec<0>(width, ' ')); \ } \ \ template \ inline IntFormatSpec, Char> pad( \ TYPE value, unsigned width, Char fill) { \ return IntFormatSpec, Char>( \ value, AlignTypeSpec<0>(width, fill)); \ } FMT_DEFINE_INT_FORMATTERS(int) FMT_DEFINE_INT_FORMATTERS(long) FMT_DEFINE_INT_FORMATTERS(unsigned) FMT_DEFINE_INT_FORMATTERS(unsigned long) FMT_DEFINE_INT_FORMATTERS(LongLong) FMT_DEFINE_INT_FORMATTERS(ULongLong) /** \rst Returns a string formatter that pads the formatted argument with the fill character to the specified width using the default (left) string alignment. **Example**:: std::string s = str(MemoryWriter() << pad("abc", 8)); // s == "abc " \endrst */ template inline StrFormatSpec pad( const Char *str, unsigned width, Char fill = ' ') { return StrFormatSpec(str, width, fill); } inline StrFormatSpec pad( const wchar_t *str, unsigned width, char fill = ' ') { return StrFormatSpec(str, width, fill); } namespace internal { template class ArgMap { private: typedef std::vector< std::pair, internal::Arg> > MapType; typedef typename MapType::value_type Pair; MapType map_; public: FMT_API void init(const ArgList &args); const internal::Arg *find(const fmt::BasicStringRef &name) const { // The list is unsorted, so just return the first matching name. for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); it != end; ++it) { if (it->first == name) return &it->second; } return FMT_NULL; } }; template class ArgFormatterBase : public ArgVisitor { private: BasicWriter &writer_; FormatSpec &spec_; FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase); void write_pointer(const void *p) { spec_.flags_ = HASH_FLAG; spec_.type_ = 'x'; writer_.write_int(reinterpret_cast(p), spec_); } // workaround MSVC two-phase lookup issue typedef internal::Arg Arg; protected: BasicWriter &writer() { return writer_; } FormatSpec &spec() { return spec_; } void write(bool value) { const char *str_value = value ? "true" : "false"; Arg::StringValue str = { str_value, std::strlen(str_value) }; writer_.write_str(str, spec_); } void write(const char *value) { Arg::StringValue str = {value, value ? std::strlen(value) : 0}; writer_.write_str(str, spec_); } public: ArgFormatterBase(BasicWriter &w, FormatSpec &s) : writer_(w), spec_(s) {} template void visit_any_int(T value) { writer_.write_int(value, spec_); } template void visit_any_double(T value) { writer_.write_double(value, spec_); } void visit_bool(bool value) { if (spec_.type_) { visit_any_int(value); return; } write(value); } void visit_char(int value) { if (spec_.type_ && spec_.type_ != 'c') { spec_.flags_ |= CHAR_FLAG; writer_.write_int(value, spec_); return; } if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) FMT_THROW(FormatError("invalid format specifier for char")); typedef typename BasicWriter::CharPtr CharPtr; Char fill = internal::CharTraits::cast(spec_.fill()); CharPtr out = CharPtr(); const unsigned CHAR_SIZE = 1; if (spec_.width_ > CHAR_SIZE) { out = writer_.grow_buffer(spec_.width_); if (spec_.align_ == ALIGN_RIGHT) { std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill); out += spec_.width_ - CHAR_SIZE; } else if (spec_.align_ == ALIGN_CENTER) { out = writer_.fill_padding(out, spec_.width_, internal::const_check(CHAR_SIZE), fill); } else { std::uninitialized_fill_n(out + CHAR_SIZE, spec_.width_ - CHAR_SIZE, fill); } } else { out = writer_.grow_buffer(CHAR_SIZE); } *out = internal::CharTraits::cast(value); } void visit_cstring(const char *value) { if (spec_.type_ == 'p') return write_pointer(value); write(value); } // Qualification with "internal" here and below is a workaround for nvcc. void visit_string(internal::Arg::StringValue value) { writer_.write_str(value, spec_); } using ArgVisitor::visit_wstring; void visit_wstring(internal::Arg::StringValue value) { writer_.write_str(value, spec_); } void visit_pointer(const void *value) { if (spec_.type_ && spec_.type_ != 'p') report_unknown_type(spec_.type_, "pointer"); write_pointer(value); } }; class FormatterBase { private: ArgList args_; int next_arg_index_; // Returns the argument with specified index. FMT_API Arg do_get_arg(unsigned arg_index, const char *&error); protected: const ArgList &args() const { return args_; } explicit FormatterBase(const ArgList &args) { args_ = args; next_arg_index_ = 0; } // Returns the next argument. Arg next_arg(const char *&error) { if (next_arg_index_ >= 0) return do_get_arg(internal::to_unsigned(next_arg_index_++), error); error = "cannot switch from manual to automatic argument indexing"; return Arg(); } // Checks if manual indexing is used and returns the argument with // specified index. Arg get_arg(unsigned arg_index, const char *&error) { return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg(); } bool check_no_auto_index(const char *&error) { if (next_arg_index_ > 0) { error = "cannot switch from automatic to manual argument indexing"; return false; } next_arg_index_ = -1; return true; } template void write(BasicWriter &w, const Char *start, const Char *end) { if (start != end) w << BasicStringRef(start, internal::to_unsigned(end - start)); } }; // A printf formatter. template class PrintfFormatter : private FormatterBase { private: void parse_flags(FormatSpec &spec, const Char *&s); // Returns the argument with specified index or, if arg_index is equal // to the maximum unsigned value, the next argument. Arg get_arg(const Char *s, unsigned arg_index = (std::numeric_limits::max)()); // Parses argument index, flags and width and returns the argument index. unsigned parse_header(const Char *&s, FormatSpec &spec); public: explicit PrintfFormatter(const ArgList &args) : FormatterBase(args) {} FMT_API void format(BasicWriter &writer, BasicCStringRef format_str); }; } // namespace internal /** \rst An argument formatter based on the `curiously recurring template pattern `_. To use `~fmt::BasicArgFormatter` define a subclass that implements some or all of the visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. Pass the subclass as the *Impl* template parameter. When a formatting function processes an argument, it will dispatch to a visit method specific to the argument type. For example, if the argument type is ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass will be called. If the subclass doesn't contain a method with this signature, then a corresponding method of `~fmt::BasicArgFormatter` or its superclass will be called. \endrst */ template class BasicArgFormatter : public internal::ArgFormatterBase { private: BasicFormatter &formatter_; const Char *format_; public: /** \rst Constructs an argument formatter object. *formatter* is a reference to the main formatter object, *spec* contains format specifier information for standard argument types, and *fmt* points to the part of the format string being parsed for custom argument types. \endrst */ BasicArgFormatter(BasicFormatter &formatter, FormatSpec &spec, const Char *fmt) : internal::ArgFormatterBase(formatter.writer(), spec), formatter_(formatter), format_(fmt) {} /** Formats argument of a custom (user-defined) type. */ void visit_custom(internal::Arg::CustomValue c) { c.format(&formatter_, c.value, &format_); } }; /** The default argument formatter. */ template class ArgFormatter : public BasicArgFormatter, Char> { public: /** Constructs an argument formatter object. */ ArgFormatter(BasicFormatter &formatter, FormatSpec &spec, const Char *fmt) : BasicArgFormatter, Char>(formatter, spec, fmt) {} }; /** This template formats data and writes the output to a writer. */ template class BasicFormatter : private internal::FormatterBase { public: /** The character type for the output. */ typedef CharType Char; private: BasicWriter &writer_; internal::ArgMap map_; FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); using internal::FormatterBase::get_arg; // Checks if manual indexing is used and returns the argument with // specified name. internal::Arg get_arg(BasicStringRef arg_name, const char *&error); // Parses argument index and returns corresponding argument. internal::Arg parse_arg_index(const Char *&s); // Parses argument name and returns corresponding argument. internal::Arg parse_arg_name(const Char *&s); public: /** \rst Constructs a ``BasicFormatter`` object. References to the arguments and the writer are stored in the formatter object so make sure they have appropriate lifetimes. \endrst */ BasicFormatter(const ArgList &args, BasicWriter &w) : internal::FormatterBase(args), writer_(w) {} /** Returns a reference to the writer associated with this formatter. */ BasicWriter &writer() { return writer_; } /** Formats stored arguments and writes the output to the writer. */ void format(BasicCStringRef format_str); // Formats a single argument and advances format_str, a format string pointer. const Char *format(const Char *&format_str, const internal::Arg &arg); }; // Generates a comma-separated list with results of applying f to // numbers 0..n-1. # define FMT_GEN(n, f) FMT_GEN##n(f) # define FMT_GEN1(f) f(0) # define FMT_GEN2(f) FMT_GEN1(f), f(1) # define FMT_GEN3(f) FMT_GEN2(f), f(2) # define FMT_GEN4(f) FMT_GEN3(f), f(3) # define FMT_GEN5(f) FMT_GEN4(f), f(4) # define FMT_GEN6(f) FMT_GEN5(f), f(5) # define FMT_GEN7(f) FMT_GEN6(f), f(6) # define FMT_GEN8(f) FMT_GEN7(f), f(7) # define FMT_GEN9(f) FMT_GEN8(f), f(8) # define FMT_GEN10(f) FMT_GEN9(f), f(9) # define FMT_GEN11(f) FMT_GEN10(f), f(10) # define FMT_GEN12(f) FMT_GEN11(f), f(11) # define FMT_GEN13(f) FMT_GEN12(f), f(12) # define FMT_GEN14(f) FMT_GEN13(f), f(13) # define FMT_GEN15(f) FMT_GEN14(f), f(14) namespace internal { inline uint64_t make_type() { return 0; } template inline uint64_t make_type(const T &arg) { return MakeValue< BasicFormatter >::type(arg); } template struct ArgArray; template struct ArgArray { typedef Value Type[N > 0 ? N : 1]; template static Value make(const T &value) { #ifdef __clang__ Value result = MakeValue(value); // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang: // https://github.com/fmtlib/fmt/issues/276 (void)result.custom.format; return result; #else return MakeValue(value); #endif } }; template struct ArgArray { typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE template static Arg make(const T &value) { return MakeArg(value); } }; #if FMT_USE_VARIADIC_TEMPLATES template inline uint64_t make_type(const Arg &first, const Args & ... tail) { return make_type(first) | (make_type(tail...) << 4); } #else struct ArgType { uint64_t type; ArgType() : type(0) {} template ArgType(const T &arg) : type(make_type(arg)) {} }; # define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | (t12.type << 48) | (t13.type << 52) | (t14.type << 56); } #endif } // namespace internal # define FMT_MAKE_TEMPLATE_ARG(n) typename T##n # define FMT_MAKE_ARG_TYPE(n) T##n # define FMT_MAKE_ARG(n) const T##n &v##n # define FMT_ASSIGN_char(n) \ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) # define FMT_ASSIGN_wchar_t(n) \ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) #if FMT_USE_VARIADIC_TEMPLATES // Defines a variadic function returning void. # define FMT_VARIADIC_VOID(func, arg_type) \ template \ void func(arg_type arg0, const Args & ... args) { \ typedef fmt::internal::ArgArray ArgArray; \ typename ArgArray::Type array{ \ ArgArray::template make >(args)...}; \ func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \ } // Defines a variadic constructor. # define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ template \ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ typedef fmt::internal::ArgArray ArgArray; \ typename ArgArray::Type array{ \ ArgArray::template make >(args)...}; \ func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \ } #else # define FMT_MAKE_REF(n) \ fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) # define FMT_MAKE_REF2(n) v##n // Defines a wrapper for a function taking one argument of type arg_type // and n additional arguments of arbitrary types. # define FMT_WRAP1(func, arg_type, n) \ template \ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ func(arg1, fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ } // Emulates a variadic function returning void on a pre-C++11 compiler. # define FMT_VARIADIC_VOID(func, arg_type) \ inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) # define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ template \ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ func(arg0, arg1, fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ } // Emulates a variadic constructor on a pre-C++11 compiler. # define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) #endif // Generates a comma-separated list with results of applying f to pairs // (argument, index). #define FMT_FOR_EACH1(f, x0) f(x0, 0) #define FMT_FOR_EACH2(f, x0, x1) \ FMT_FOR_EACH1(f, x0), f(x1, 1) #define FMT_FOR_EACH3(f, x0, x1, x2) \ FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) #define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) #define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) #define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) #define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) #define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) #define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) #define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) /** An error returned by an operating system or a language runtime, for example a file opening error. */ class SystemError : public internal::RuntimeError { private: FMT_API void init(int err_code, CStringRef format_str, ArgList args); protected: int error_code_; typedef char Char; // For FMT_VARIADIC_CTOR. SystemError() {} public: /** \rst Constructs a :class:`fmt::SystemError` object with the description of the form .. parsed-literal:: **: ** where ** is the formatted message and ** is the system message corresponding to the error code. *error_code* is a system error code as given by ``errno``. If *error_code* is not a valid error code such as -1, the system message may look like "Unknown error -1" and is platform-dependent. **Example**:: // This throws a SystemError with the description // cannot open file 'madeup': No such file or directory // or similar (system message may vary). const char *filename = "madeup"; std::FILE *file = std::fopen(filename, "r"); if (!file) throw fmt::SystemError(errno, "cannot open file '{}'", filename); \endrst */ SystemError(int error_code, CStringRef message) { init(error_code, message, ArgList()); } FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) FMT_API ~SystemError() FMT_DTOR_NOEXCEPT; int error_code() const { return error_code_; } }; /** \rst This template provides operations for formatting and writing data into a character stream. The output is stored in a buffer provided by a subclass such as :class:`fmt::BasicMemoryWriter`. You can use one of the following typedefs for common character types: +---------+----------------------+ | Type | Definition | +=========+======================+ | Writer | BasicWriter | +---------+----------------------+ | WWriter | BasicWriter | +---------+----------------------+ \endrst */ template class BasicWriter { private: // Output buffer. Buffer &buffer_; FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); typedef typename internal::CharTraits::CharPtr CharPtr; #if FMT_SECURE_SCL // Returns pointer value. static Char *get(CharPtr p) { return p.base(); } #else static Char *get(Char *p) { return p; } #endif // Fills the padding around the content and returns the pointer to the // content area. static CharPtr fill_padding(CharPtr buffer, unsigned total_size, std::size_t content_size, wchar_t fill); // Grows the buffer by n characters and returns a pointer to the newly // allocated area. CharPtr grow_buffer(std::size_t n) { std::size_t size = buffer_.size(); buffer_.resize(size + n); return internal::make_ptr(&buffer_[size], n); } // Writes an unsigned decimal integer. template Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) { unsigned num_digits = internal::count_digits(value); Char *ptr = get(grow_buffer(prefix_size + num_digits)); internal::format_decimal(ptr + prefix_size, value, num_digits); return ptr; } // Writes a decimal integer. template void write_decimal(Int value) { typedef typename internal::IntTraits::MainType MainType; MainType abs_value = static_cast(value); if (internal::is_negative(value)) { abs_value = 0 - abs_value; *write_unsigned_decimal(abs_value, 1) = '-'; } else { write_unsigned_decimal(abs_value, 0); } } // Prepare a buffer for integer formatting. CharPtr prepare_int_buffer(unsigned num_digits, const EmptySpec &, const char *prefix, unsigned prefix_size) { unsigned size = prefix_size + num_digits; CharPtr p = grow_buffer(size); std::uninitialized_copy(prefix, prefix + prefix_size, p); return p + size - 1; } template CharPtr prepare_int_buffer(unsigned num_digits, const Spec &spec, const char *prefix, unsigned prefix_size); // Formats an integer. template void write_int(T value, Spec spec); // Formats a floating-point number (double or long double). template void write_double(T value, const FormatSpec &spec); // Writes a formatted string. template CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec); template void write_str(const internal::Arg::StringValue &str, const FormatSpec &spec); // This following methods are private to disallow writing wide characters // and strings to a char stream. If you want to print a wide string as a // pointer as std::ostream does, cast it to const void*. // Do not implement! void operator<<(typename internal::WCharHelper::Unsupported); void operator<<( typename internal::WCharHelper::Unsupported); // Appends floating-point length specifier to the format string. // The second argument is only used for overload resolution. void append_float_length(Char *&format_ptr, long double) { *format_ptr++ = 'L'; } template void append_float_length(Char *&, T) {} template friend class internal::ArgFormatterBase; friend class internal::PrintfArgFormatter; protected: /** Constructs a ``BasicWriter`` object. */ explicit BasicWriter(Buffer &b) : buffer_(b) {} public: /** \rst Destroys a ``BasicWriter`` object. \endrst */ virtual ~BasicWriter() {} /** Returns the total number of characters written. */ std::size_t size() const { return buffer_.size(); } /** Returns a pointer to the output buffer content. No terminating null character is appended. */ const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } /** Returns a pointer to the output buffer content with terminating null character appended. */ const Char *c_str() const { std::size_t size = buffer_.size(); buffer_.reserve(size + 1); buffer_[size] = '\0'; return &buffer_[0]; } /** \rst Returns the content of the output buffer as an `std::string`. \endrst */ std::basic_string str() const { return std::basic_string(&buffer_[0], buffer_.size()); } /** \rst Writes formatted data. *args* is an argument list representing arbitrary arguments. **Example**:: MemoryWriter out; out.write("Current point:\n"); out.write("({:+f}, {:+f})", -3.14, 3.14); This will write the following output to the ``out`` object: .. code-block:: none Current point: (-3.140000, +3.140000) The output can be accessed using :func:`data()`, :func:`c_str` or :func:`str` methods. See also :ref:`syntax`. \endrst */ void write(BasicCStringRef format, ArgList args) { BasicFormatter(args, *this).format(format); } FMT_VARIADIC_VOID(write, BasicCStringRef) BasicWriter &operator<<(int value) { write_decimal(value); return *this; } BasicWriter &operator<<(unsigned value) { return *this << IntFormatSpec(value); } BasicWriter &operator<<(long value) { write_decimal(value); return *this; } BasicWriter &operator<<(unsigned long value) { return *this << IntFormatSpec(value); } BasicWriter &operator<<(LongLong value) { write_decimal(value); return *this; } /** \rst Formats *value* and writes it to the stream. \endrst */ BasicWriter &operator<<(ULongLong value) { return *this << IntFormatSpec(value); } BasicWriter &operator<<(double value) { write_double(value, FormatSpec()); return *this; } /** \rst Formats *value* using the general format for floating-point numbers (``'g'``) and writes it to the stream. \endrst */ BasicWriter &operator<<(long double value) { write_double(value, FormatSpec()); return *this; } /** Writes a character to the stream. */ BasicWriter &operator<<(char value) { buffer_.push_back(value); return *this; } BasicWriter &operator<<( typename internal::WCharHelper::Supported value) { buffer_.push_back(value); return *this; } /** \rst Writes *value* to the stream. \endrst */ BasicWriter &operator<<(fmt::BasicStringRef value) { const Char *str = value.data(); buffer_.append(str, str + value.size()); return *this; } BasicWriter &operator<<( typename internal::WCharHelper::Supported value) { const char *str = value.data(); buffer_.append(str, str + value.size()); return *this; } template BasicWriter &operator<<(IntFormatSpec spec) { internal::CharTraits::convert(FillChar()); write_int(spec.value(), spec); return *this; } template BasicWriter &operator<<(const StrFormatSpec &spec) { const StrChar *s = spec.str(); write_str(s, std::char_traits::length(s), spec); return *this; } void clear() FMT_NOEXCEPT { buffer_.clear(); } Buffer &buffer() FMT_NOEXCEPT { return buffer_; } }; template template typename BasicWriter::CharPtr BasicWriter::write_str( const StrChar *s, std::size_t size, const AlignSpec &spec) { CharPtr out = CharPtr(); if (spec.width() > size) { out = grow_buffer(spec.width()); Char fill = internal::CharTraits::cast(spec.fill()); if (spec.align() == ALIGN_RIGHT) { std::uninitialized_fill_n(out, spec.width() - size, fill); out += spec.width() - size; } else if (spec.align() == ALIGN_CENTER) { out = fill_padding(out, spec.width(), size, fill); } else { std::uninitialized_fill_n(out + size, spec.width() - size, fill); } } else { out = grow_buffer(size); } std::uninitialized_copy(s, s + size, out); return out; } template template void BasicWriter::write_str( const internal::Arg::StringValue &s, const FormatSpec &spec) { // Check if StrChar is convertible to Char. internal::CharTraits::convert(StrChar()); if (spec.type_ && spec.type_ != 's') internal::report_unknown_type(spec.type_, "string"); const StrChar *str_value = s.value; std::size_t str_size = s.size; if (str_size == 0) { if (!str_value) { FMT_THROW(FormatError("string pointer is null")); } } std::size_t precision = static_cast(spec.precision_); if (spec.precision_ >= 0 && precision < str_size) str_size = precision; write_str(str_value, str_size, spec); } template typename BasicWriter::CharPtr BasicWriter::fill_padding( CharPtr buffer, unsigned total_size, std::size_t content_size, wchar_t fill) { std::size_t padding = total_size - content_size; std::size_t left_padding = padding / 2; Char fill_char = internal::CharTraits::cast(fill); std::uninitialized_fill_n(buffer, left_padding, fill_char); buffer += left_padding; CharPtr content = buffer; std::uninitialized_fill_n(buffer + content_size, padding - left_padding, fill_char); return content; } template template typename BasicWriter::CharPtr BasicWriter::prepare_int_buffer( unsigned num_digits, const Spec &spec, const char *prefix, unsigned prefix_size) { unsigned width = spec.width(); Alignment align = spec.align(); Char fill = internal::CharTraits::cast(spec.fill()); if (spec.precision() > static_cast(num_digits)) { // Octal prefix '0' is counted as a digit, so ignore it if precision // is specified. if (prefix_size > 0 && prefix[prefix_size - 1] == '0') --prefix_size; unsigned number_size = prefix_size + internal::to_unsigned(spec.precision()); AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); if (number_size >= width) return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); buffer_.reserve(width); unsigned fill_size = width - number_size; if (align != ALIGN_LEFT) { CharPtr p = grow_buffer(fill_size); std::uninitialized_fill(p, p + fill_size, fill); } CharPtr result = prepare_int_buffer( num_digits, subspec, prefix, prefix_size); if (align == ALIGN_LEFT) { CharPtr p = grow_buffer(fill_size); std::uninitialized_fill(p, p + fill_size, fill); } return result; } unsigned size = prefix_size + num_digits; if (width <= size) { CharPtr p = grow_buffer(size); std::uninitialized_copy(prefix, prefix + prefix_size, p); return p + size - 1; } CharPtr p = grow_buffer(width); CharPtr end = p + width; if (align == ALIGN_LEFT) { std::uninitialized_copy(prefix, prefix + prefix_size, p); p += size; std::uninitialized_fill(p, end, fill); } else if (align == ALIGN_CENTER) { p = fill_padding(p, width, size, fill); std::uninitialized_copy(prefix, prefix + prefix_size, p); p += size; } else { if (align == ALIGN_NUMERIC) { if (prefix_size != 0) { p = std::uninitialized_copy(prefix, prefix + prefix_size, p); size -= prefix_size; } } else { std::uninitialized_copy(prefix, prefix + prefix_size, end - size); } std::uninitialized_fill(p, end - size, fill); p = end; } return p - 1; } template template void BasicWriter::write_int(T value, Spec spec) { unsigned prefix_size = 0; typedef typename internal::IntTraits::MainType UnsignedType; UnsignedType abs_value = static_cast(value); char prefix[4] = ""; if (internal::is_negative(value)) { prefix[0] = '-'; ++prefix_size; abs_value = 0 - abs_value; } else if (spec.flag(SIGN_FLAG)) { prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; ++prefix_size; } switch (spec.type()) { case 0: case 'd': { unsigned num_digits = internal::count_digits(abs_value); CharPtr p = prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1; internal::format_decimal(get(p), abs_value, 0); break; } case 'x': case 'X': { UnsignedType n = abs_value; if (spec.flag(HASH_FLAG)) { prefix[prefix_size++] = '0'; prefix[prefix_size++] = spec.type(); } unsigned num_digits = 0; do { ++num_digits; } while ((n >>= 4) != 0); Char *p = get(prepare_int_buffer( num_digits, spec, prefix, prefix_size)); n = abs_value; const char *digits = spec.type() == 'x' ? "0123456789abcdef" : "0123456789ABCDEF"; do { *p-- = digits[n & 0xf]; } while ((n >>= 4) != 0); break; } case 'b': case 'B': { UnsignedType n = abs_value; if (spec.flag(HASH_FLAG)) { prefix[prefix_size++] = '0'; prefix[prefix_size++] = spec.type(); } unsigned num_digits = 0; do { ++num_digits; } while ((n >>= 1) != 0); Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); n = abs_value; do { *p-- = static_cast('0' + (n & 1)); } while ((n >>= 1) != 0); break; } case 'o': { UnsignedType n = abs_value; if (spec.flag(HASH_FLAG)) prefix[prefix_size++] = '0'; unsigned num_digits = 0; do { ++num_digits; } while ((n >>= 3) != 0); Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); n = abs_value; do { *p-- = static_cast('0' + (n & 7)); } while ((n >>= 3) != 0); break; } case 'n': { unsigned num_digits = internal::count_digits(abs_value); fmt::StringRef sep = ""; #if !(defined(ANDROID) || defined(__ANDROID__)) sep = internal::thousands_sep(std::localeconv()); #endif unsigned size = static_cast( num_digits + sep.size() * ((num_digits - 1) / 3)); CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1; internal::format_decimal(get(p), abs_value, 0, internal::ThousandsSep(sep)); break; } default: internal::report_unknown_type( spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); break; } } template template void BasicWriter::write_double(T value, const FormatSpec &spec) { // Check type. char type = spec.type(); bool upper = false; switch (type) { case 0: type = 'g'; break; case 'e': case 'f': case 'g': case 'a': break; case 'F': #if FMT_MSC_VER // MSVC's printf doesn't support 'F'. type = 'f'; #endif // Fall through. case 'E': case 'G': case 'A': upper = true; break; default: internal::report_unknown_type(type, "double"); break; } char sign = 0; // Use isnegative instead of value < 0 because the latter is always // false for NaN. if (internal::FPUtil::isnegative(static_cast(value))) { sign = '-'; value = -value; } else if (spec.flag(SIGN_FLAG)) { sign = spec.flag(PLUS_FLAG) ? '+' : ' '; } if (internal::FPUtil::isnotanumber(value)) { // Format NaN ourselves because sprintf's output is not consistent // across platforms. std::size_t nan_size = 4; const char *nan = upper ? " NAN" : " nan"; if (!sign) { --nan_size; ++nan; } CharPtr out = write_str(nan, nan_size, spec); if (sign) *out = sign; return; } if (internal::FPUtil::isinfinity(value)) { // Format infinity ourselves because sprintf's output is not consistent // across platforms. std::size_t inf_size = 4; const char *inf = upper ? " INF" : " inf"; if (!sign) { --inf_size; ++inf; } CharPtr out = write_str(inf, inf_size, spec); if (sign) *out = sign; return; } std::size_t offset = buffer_.size(); unsigned width = spec.width(); if (sign) { buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u)); if (width > 0) --width; ++offset; } // Build format string. enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg Char format[MAX_FORMAT_SIZE]; Char *format_ptr = format; *format_ptr++ = '%'; unsigned width_for_sprintf = width; if (spec.flag(HASH_FLAG)) *format_ptr++ = '#'; if (spec.align() == ALIGN_CENTER) { width_for_sprintf = 0; } else { if (spec.align() == ALIGN_LEFT) *format_ptr++ = '-'; if (width != 0) *format_ptr++ = '*'; } if (spec.precision() >= 0) { *format_ptr++ = '.'; *format_ptr++ = '*'; } append_float_length(format_ptr, value); *format_ptr++ = type; *format_ptr = '\0'; // Format using snprintf. Char fill = internal::CharTraits::cast(spec.fill()); unsigned n = 0; Char *start = FMT_NULL; for (;;) { std::size_t buffer_size = buffer_.capacity() - offset; #if FMT_MSC_VER // MSVC's vsnprintf_s doesn't work with zero size, so reserve // space for at least one extra character to make the size non-zero. // Note that the buffer's capacity will increase by more than 1. if (buffer_size == 0) { buffer_.reserve(offset + 1); buffer_size = buffer_.capacity() - offset; } #endif start = &buffer_[offset]; int result = internal::CharTraits::format_float( start, buffer_size, format, width_for_sprintf, spec.precision(), value); if (result >= 0) { n = internal::to_unsigned(result); if (offset + n < buffer_.capacity()) break; // The buffer is large enough - continue with formatting. buffer_.reserve(offset + n + 1); } else { // If result is negative we ask to increase the capacity by at least 1, // but as std::vector, the buffer grows exponentially. buffer_.reserve(buffer_.capacity() + 1); } } if (sign) { if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || *start != ' ') { *(start - 1) = sign; sign = 0; } else { *(start - 1) = fill; } ++n; } if (spec.align() == ALIGN_CENTER && spec.width() > n) { width = spec.width(); CharPtr p = grow_buffer(width); std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char)); fill_padding(p, spec.width(), n, fill); return; } if (spec.fill() != ' ' || sign) { while (*start == ' ') *start++ = fill; if (sign) *(start - 1) = sign; } grow_buffer(n); } /** \rst This class template provides operations for formatting and writing data into a character stream. The output is stored in a memory buffer that grows dynamically. You can use one of the following typedefs for common character types and the standard allocator: +---------------+-----------------------------------------------------+ | Type | Definition | +===============+=====================================================+ | MemoryWriter | BasicMemoryWriter> | +---------------+-----------------------------------------------------+ | WMemoryWriter | BasicMemoryWriter> | +---------------+-----------------------------------------------------+ **Example**:: MemoryWriter out; out << "The answer is " << 42 << "\n"; out.write("({:+f}, {:+f})", -3.14, 3.14); This will write the following output to the ``out`` object: .. code-block:: none The answer is 42 (-3.140000, +3.140000) The output can be converted to an ``std::string`` with ``out.str()`` or accessed as a C string with ``out.c_str()``. \endrst */ template > class BasicMemoryWriter : public BasicWriter { private: internal::MemoryBuffer buffer_; public: explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) : BasicWriter(buffer_), buffer_(alloc) {} #if FMT_USE_RVALUE_REFERENCES /** \rst Constructs a :class:`fmt::BasicMemoryWriter` object moving the content of the other object to it. \endrst */ BasicMemoryWriter(BasicMemoryWriter &&other) : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { } /** \rst Moves the content of the other ``BasicMemoryWriter`` object to this one. \endrst */ BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { buffer_ = std::move(other.buffer_); return *this; } #endif }; typedef BasicMemoryWriter MemoryWriter; typedef BasicMemoryWriter WMemoryWriter; /** \rst This class template provides operations for formatting and writing data into a fixed-size array. For writing into a dynamically growing buffer use :class:`fmt::BasicMemoryWriter`. Any write method will throw ``std::runtime_error`` if the output doesn't fit into the array. You can use one of the following typedefs for common character types: +--------------+---------------------------+ | Type | Definition | +==============+===========================+ | ArrayWriter | BasicArrayWriter | +--------------+---------------------------+ | WArrayWriter | BasicArrayWriter | +--------------+---------------------------+ \endrst */ template class BasicArrayWriter : public BasicWriter { private: internal::FixedBuffer buffer_; public: /** \rst Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the given size. \endrst */ BasicArrayWriter(Char *array, std::size_t size) : BasicWriter(buffer_), buffer_(array, size) {} /** \rst Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the size known at compile time. \endrst */ template explicit BasicArrayWriter(Char (&array)[SIZE]) : BasicWriter(buffer_), buffer_(array, SIZE) {} }; typedef BasicArrayWriter ArrayWriter; typedef BasicArrayWriter WArrayWriter; // Reports a system error without throwing an exception. // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; #if FMT_USE_WINDOWS_H /** A Windows error. */ class WindowsError : public SystemError { private: FMT_API void init(int error_code, CStringRef format_str, ArgList args); public: /** \rst Constructs a :class:`fmt::WindowsError` object with the description of the form .. parsed-literal:: **: ** where ** is the formatted message and ** is the system message corresponding to the error code. *error_code* is a Windows error code as given by ``GetLastError``. If *error_code* is not a valid error code such as -1, the system message will look like "error -1". **Example**:: // This throws a WindowsError with the description // cannot open file 'madeup': The system cannot find the file specified. // or similar (system message may vary). const char *filename = "madeup"; LPOFSTRUCT of = LPOFSTRUCT(); HFILE file = OpenFile(filename, &of, OF_READ); if (file == HFILE_ERROR) { throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename); } \endrst */ WindowsError(int error_code, CStringRef message) { init(error_code, message, ArgList()); } FMT_VARIADIC_CTOR(WindowsError, init, int, CStringRef) }; // Reports a Windows error without throwing an exception. // Can be used to report errors from destructors. FMT_API void report_windows_error(int error_code, StringRef message) FMT_NOEXCEPT; #endif enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; /** Formats a string and prints it to stdout using ANSI escape sequences to specify color (experimental). Example: print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); */ FMT_API void print_colored(Color c, CStringRef format, ArgList args); /** \rst Formats arguments and returns the result as a string. **Example**:: std::string message = format("The answer is {}", 42); \endrst */ inline std::string format(CStringRef format_str, ArgList args) { MemoryWriter w; w.write(format_str, args); return w.str(); } inline std::wstring format(WCStringRef format_str, ArgList args) { WMemoryWriter w; w.write(format_str, args); return w.str(); } /** \rst Prints formatted data to the file *f*. **Example**:: print(stderr, "Don't {}!", "panic"); \endrst */ FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args); /** \rst Prints formatted data to ``stdout``. **Example**:: print("Elapsed time: {0:.2f} seconds", 1.23); \endrst */ FMT_API void print(CStringRef format_str, ArgList args); template void printf(BasicWriter &w, BasicCStringRef format, ArgList args) { internal::PrintfFormatter(args).format(w, format); } /** \rst Formats arguments and returns the result as a string. **Example**:: std::string message = fmt::sprintf("The answer is %d", 42); \endrst */ inline std::string sprintf(CStringRef format, ArgList args) { MemoryWriter w; printf(w, format, args); return w.str(); } inline std::wstring sprintf(WCStringRef format, ArgList args) { WMemoryWriter w; printf(w, format, args); return w.str(); } /** \rst Prints formatted data to the file *f*. **Example**:: fmt::fprintf(stderr, "Don't %s!", "panic"); \endrst */ FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args); /** \rst Prints formatted data to ``stdout``. **Example**:: fmt::printf("Elapsed time: %.2f seconds", 1.23); \endrst */ inline int printf(CStringRef format, ArgList args) { return fprintf(stdout, format, args); } /** Fast integer formatter. */ class FormatInt { private: // Buffer should be large enough to hold all digits (digits10 + 1), // a sign and a null character. enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; mutable char buffer_[BUFFER_SIZE]; char *str_; // Formats value in reverse and returns the number of digits. char *format_decimal(ULongLong value) { char *buffer_end = buffer_ + BUFFER_SIZE - 1; while (value >= 100) { // Integer division is slow so do it for a group of two digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. unsigned index = static_cast((value % 100) * 2); value /= 100; *--buffer_end = internal::Data::DIGITS[index + 1]; *--buffer_end = internal::Data::DIGITS[index]; } if (value < 10) { *--buffer_end = static_cast('0' + value); return buffer_end; } unsigned index = static_cast(value * 2); *--buffer_end = internal::Data::DIGITS[index + 1]; *--buffer_end = internal::Data::DIGITS[index]; return buffer_end; } void FormatSigned(LongLong value) { ULongLong abs_value = static_cast(value); bool negative = value < 0; if (negative) abs_value = 0 - abs_value; str_ = format_decimal(abs_value); if (negative) *--str_ = '-'; } public: explicit FormatInt(int value) { FormatSigned(value); } explicit FormatInt(long value) { FormatSigned(value); } explicit FormatInt(LongLong value) { FormatSigned(value); } explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} /** Returns the number of characters written to the output buffer. */ std::size_t size() const { return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1); } /** Returns a pointer to the output buffer content. No terminating null character is appended. */ const char *data() const { return str_; } /** Returns a pointer to the output buffer content with terminating null character appended. */ const char *c_str() const { buffer_[BUFFER_SIZE - 1] = '\0'; return str_; } /** \rst Returns the content of the output buffer as an ``std::string``. \endrst */ std::string str() const { return std::string(str_, size()); } }; // Formats a decimal integer value writing into buffer and returns // a pointer to the end of the formatted string. This function doesn't // write a terminating null character. template inline void format_decimal(char *&buffer, T value) { typedef typename internal::IntTraits::MainType MainType; MainType abs_value = static_cast(value); if (internal::is_negative(value)) { *buffer++ = '-'; abs_value = 0 - abs_value; } if (abs_value < 100) { if (abs_value < 10) { *buffer++ = static_cast('0' + abs_value); return; } unsigned index = static_cast(abs_value * 2); *buffer++ = internal::Data::DIGITS[index]; *buffer++ = internal::Data::DIGITS[index + 1]; return; } unsigned num_digits = internal::count_digits(abs_value); internal::format_decimal(buffer, abs_value, num_digits); buffer += num_digits; } /** \rst Returns a named argument for formatting functions. **Example**:: print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); \endrst */ template inline internal::NamedArg arg(StringRef name, const T &arg) { return internal::NamedArg(name, arg); } template inline internal::NamedArg arg(WStringRef name, const T &arg) { return internal::NamedArg(name, arg); } // The following two functions are deleted intentionally to disable // nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``. template void arg(StringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; template void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; } #if FMT_GCC_VERSION // Use the system_header pragma to suppress warnings about variadic macros // because suppressing -Wvariadic-macros with the diagnostic pragma doesn't // work. It is used at the end because we want to suppress as little warnings // as possible. # pragma GCC system_header #endif // This is used to work around VC++ bugs in handling variadic macros. #define FMT_EXPAND(args) args // Returns the number of arguments. // Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. #define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) #define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) #define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N #define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 #define FMT_CONCAT(a, b) a##b #define FMT_FOR_EACH_(N, f, ...) \ FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) #define FMT_FOR_EACH(f, ...) \ FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) #define FMT_ADD_ARG_NAME(type, index) type arg##index #define FMT_GET_ARG_NAME(type, index) arg##index #if FMT_USE_VARIADIC_TEMPLATES # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ template \ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ const Args & ... args) { \ typedef fmt::internal::ArgArray ArgArray; \ typename ArgArray::Type array{ \ ArgArray::template make >(args)...}; \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ fmt::ArgList(fmt::internal::make_type(args...), array)); \ } #else // Defines a wrapper for a function taking __VA_ARGS__ arguments // and n additional arguments of arbitrary types. # define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ template \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ FMT_GEN(n, FMT_MAKE_ARG)) { \ fmt::internal::ArgArray::Type arr; \ FMT_GEN(n, FMT_ASSIGN_##Char); \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \ } # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ } \ FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) #endif // FMT_USE_VARIADIC_TEMPLATES /** \rst Defines a variadic function with the specified return type, function name and argument types passed as variable arguments to this macro. **Example**:: void print_error(const char *file, int line, const char *format, fmt::ArgList args) { fmt::print("{}: {}: ", file, line); fmt::print(format, args); } FMT_VARIADIC(void, print_error, const char *, int, const char *) ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that don't implement variadic templates. You don't have to use this macro if you don't need legacy compiler support and can use variadic templates directly:: template void print_error(const char *file, int line, const char *format, const Args & ... args) { fmt::print("{}: {}: ", file, line); fmt::print(format, args...); } \endrst */ #define FMT_VARIADIC(ReturnType, func, ...) \ FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) #define FMT_VARIADIC_W(ReturnType, func, ...) \ FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) #define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) #define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L###id, id) /** \rst Convenient macro to capture the arguments' names and values into several ``fmt::arg(name, value)``. **Example**:: int x = 1, y = 2; print("point: ({x}, {y})", FMT_CAPTURE(x, y)); // same as: // print("point: ({x}, {y})", arg("x", x), arg("y", y)); \endrst */ #define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__) #define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__) namespace fmt { FMT_VARIADIC(std::string, format, CStringRef) FMT_VARIADIC_W(std::wstring, format, WCStringRef) FMT_VARIADIC(void, print, CStringRef) FMT_VARIADIC(void, print, std::FILE *, CStringRef) FMT_VARIADIC(void, print_colored, Color, CStringRef) FMT_VARIADIC(std::string, sprintf, CStringRef) FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) FMT_VARIADIC(int, printf, CStringRef) FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) namespace internal { template inline bool is_name_start(Char c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; } // Parses an unsigned integer advancing s to the end of the parsed input. // This function assumes that the first character of s is a digit. template unsigned parse_nonnegative_int(const Char *&s) { assert('0' <= *s && *s <= '9'); unsigned value = 0; do { unsigned new_value = value * 10 + (*s++ - '0'); // Check if value wrapped around. if (new_value < value) { value = (std::numeric_limits::max)(); break; } value = new_value; } while ('0' <= *s && *s <= '9'); // Convert to unsigned to prevent a warning. unsigned max_int = (std::numeric_limits::max)(); if (value > max_int) FMT_THROW(FormatError("number is too big")); return value; } inline void require_numeric_argument(const Arg &arg, char spec) { if (arg.type > Arg::LAST_NUMERIC_TYPE) { std::string message = fmt::format("format specifier '{}' requires numeric argument", spec); FMT_THROW(fmt::FormatError(message)); } } template void check_sign(const Char *&s, const Arg &arg) { char sign = static_cast(*s); require_numeric_argument(arg, sign); if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { FMT_THROW(FormatError(fmt::format( "format specifier '{}' requires signed argument", sign))); } ++s; } } // namespace internal template inline internal::Arg BasicFormatter::get_arg( BasicStringRef arg_name, const char *&error) { if (check_no_auto_index(error)) { map_.init(args()); const internal::Arg *arg = map_.find(arg_name); if (arg) return *arg; error = "argument not found"; } return internal::Arg(); } template inline internal::Arg BasicFormatter::parse_arg_index(const Char *&s) { const char *error = FMT_NULL; internal::Arg arg = *s < '0' || *s > '9' ? next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error); if (error) { FMT_THROW(FormatError( *s != '}' && *s != ':' ? "invalid format string" : error)); } return arg; } template inline internal::Arg BasicFormatter::parse_arg_name(const Char *&s) { assert(internal::is_name_start(*s)); const Char *start = s; Char c; do { c = *++s; } while (internal::is_name_start(c) || ('0' <= c && c <= '9')); const char *error = FMT_NULL; internal::Arg arg = get_arg(BasicStringRef(start, s - start), error); if (error) FMT_THROW(FormatError(error)); return arg; } template const Char *BasicFormatter::format( const Char *&format_str, const internal::Arg &arg) { using internal::Arg; const Char *s = format_str; FormatSpec spec; if (*s == ':') { if (arg.type == Arg::CUSTOM) { arg.custom.format(this, arg.custom.value, &s); return s; } ++s; // Parse fill and alignment. if (Char c = *s) { const Char *p = s + 1; spec.align_ = ALIGN_DEFAULT; do { switch (*p) { case '<': spec.align_ = ALIGN_LEFT; break; case '>': spec.align_ = ALIGN_RIGHT; break; case '=': spec.align_ = ALIGN_NUMERIC; break; case '^': spec.align_ = ALIGN_CENTER; break; } if (spec.align_ != ALIGN_DEFAULT) { if (p != s) { if (c == '}') break; if (c == '{') FMT_THROW(FormatError("invalid fill character '{'")); s += 2; spec.fill_ = c; } else ++s; if (spec.align_ == ALIGN_NUMERIC) require_numeric_argument(arg, '='); break; } } while (--p >= s); } // Parse sign. switch (*s) { case '+': check_sign(s, arg); spec.flags_ |= SIGN_FLAG | PLUS_FLAG; break; case '-': check_sign(s, arg); spec.flags_ |= MINUS_FLAG; break; case ' ': check_sign(s, arg); spec.flags_ |= SIGN_FLAG; break; } if (*s == '#') { require_numeric_argument(arg, '#'); spec.flags_ |= HASH_FLAG; ++s; } // Parse zero flag. if (*s == '0') { require_numeric_argument(arg, '0'); spec.align_ = ALIGN_NUMERIC; spec.fill_ = '0'; ++s; } // Parse width. if ('0' <= *s && *s <= '9') { spec.width_ = internal::parse_nonnegative_int(s); } else if (*s == '{') { ++s; Arg width_arg = internal::is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); if (*s++ != '}') FMT_THROW(FormatError("invalid format string")); ULongLong value = 0; switch (width_arg.type) { case Arg::INT: if (width_arg.int_value < 0) FMT_THROW(FormatError("negative width")); value = width_arg.int_value; break; case Arg::UINT: value = width_arg.uint_value; break; case Arg::LONG_LONG: if (width_arg.long_long_value < 0) FMT_THROW(FormatError("negative width")); value = width_arg.long_long_value; break; case Arg::ULONG_LONG: value = width_arg.ulong_long_value; break; default: FMT_THROW(FormatError("width is not integer")); } if (value > (std::numeric_limits::max)()) FMT_THROW(FormatError("number is too big")); spec.width_ = static_cast(value); } // Parse precision. if (*s == '.') { ++s; spec.precision_ = 0; if ('0' <= *s && *s <= '9') { spec.precision_ = internal::parse_nonnegative_int(s); } else if (*s == '{') { ++s; Arg precision_arg = internal::is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); if (*s++ != '}') FMT_THROW(FormatError("invalid format string")); ULongLong value = 0; switch (precision_arg.type) { case Arg::INT: if (precision_arg.int_value < 0) FMT_THROW(FormatError("negative precision")); value = precision_arg.int_value; break; case Arg::UINT: value = precision_arg.uint_value; break; case Arg::LONG_LONG: if (precision_arg.long_long_value < 0) FMT_THROW(FormatError("negative precision")); value = precision_arg.long_long_value; break; case Arg::ULONG_LONG: value = precision_arg.ulong_long_value; break; default: FMT_THROW(FormatError("precision is not integer")); } if (value > (std::numeric_limits::max)()) FMT_THROW(FormatError("number is too big")); spec.precision_ = static_cast(value); } else { FMT_THROW(FormatError("missing precision specifier")); } if (arg.type <= Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { FMT_THROW(FormatError( fmt::format("precision not allowed in {} format specifier", arg.type == Arg::POINTER ? "pointer" : "integer"))); } } // Parse type. if (*s != '}' && *s) spec.type_ = static_cast(*s++); } if (*s++ != '}') FMT_THROW(FormatError("missing '}' in format string")); // Format argument. ArgFormatter(*this, spec, s - 1).visit(arg); return s; } template void BasicFormatter::format(BasicCStringRef format_str) { const Char *s = format_str.c_str(); const Char *start = s; while (*s) { Char c = *s++; if (c != '{' && c != '}') continue; if (*s == c) { write(writer_, start, s); start = ++s; continue; } if (c == '}') FMT_THROW(FormatError("unmatched '}' in format string")); write(writer_, start, s - 1); internal::Arg arg = internal::is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); start = s = format(s, arg); } write(writer_, start, s); } } // namespace fmt #if FMT_USE_USER_DEFINED_LITERALS namespace fmt { namespace internal { template struct UdlFormat { const Char *str; template auto operator()(Args && ... args) const -> decltype(format(str, std::forward(args)...)) { return format(str, std::forward(args)...); } }; template struct UdlArg { const Char *str; template NamedArg operator=(T &&value) const { return {str, std::forward(value)}; } }; } // namespace internal inline namespace literals { /** \rst C++11 literal equivalent of :func:`fmt::format`. **Example**:: using namespace fmt::literals; std::string message = "The answer is {}"_format(42); \endrst */ inline internal::UdlFormat operator"" _format(const char *s, std::size_t) { return {s}; } inline internal::UdlFormat operator"" _format(const wchar_t *s, std::size_t) { return {s}; } /** \rst C++11 literal equivalent of :func:`fmt::arg`. **Example**:: using namespace fmt::literals; print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); \endrst */ inline internal::UdlArg operator"" _a(const char *s, std::size_t) { return {s}; } inline internal::UdlArg operator"" _a(const wchar_t *s, std::size_t) { return {s}; } } // inline namespace literals } // namespace fmt #endif // FMT_USE_USER_DEFINED_LITERALS // Restore warnings. #if FMT_GCC_VERSION >= 406 # pragma GCC diagnostic pop #endif #if defined(__clang__) && !defined(FMT_ICC_VERSION) # pragma clang diagnostic pop #endif #ifdef FMT_HEADER_ONLY # define FMT_FUNC inline # include "format.cc" #else # define FMT_FUNC #endif #endif // FMT_FORMAT_H_ DarkRadiant-2.5.0/libs/libfmt/fmt/ostream.cc000066400000000000000000000020611321750546400206650ustar00rootroot00000000000000/* Formatting library for C++ - std::ostream support Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. For the license information refer to format.h. */ #include "ostream.h" namespace fmt { namespace { // Write the content of w to os. void write(std::ostream &os, Writer &w) { const char *data = w.data(); typedef internal::MakeUnsigned::Type UnsignedStreamSize; UnsignedStreamSize size = w.size(); UnsignedStreamSize max_size = internal::to_unsigned((std::numeric_limits::max)()); do { UnsignedStreamSize n = size <= max_size ? size : max_size; os.write(data, static_cast(n)); data += n; size -= n; } while (size != 0); } } FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) { MemoryWriter w; w.write(format_str, args); write(os, w); } FMT_FUNC int fprintf(std::ostream &os, CStringRef format, ArgList args) { MemoryWriter w; printf(w, format, args); write(os, w); return static_cast(w.size()); } } // namespace fmt DarkRadiant-2.5.0/libs/libfmt/fmt/ostream.h000066400000000000000000000054161321750546400205360ustar00rootroot00000000000000/* Formatting library for C++ - std::ostream support Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. For the license information refer to format.h. */ #ifndef FMT_OSTREAM_H_ #define FMT_OSTREAM_H_ #include "format.h" #include namespace fmt { namespace internal { template class FormatBuf : public std::basic_streambuf { private: typedef typename std::basic_streambuf::int_type int_type; typedef typename std::basic_streambuf::traits_type traits_type; Buffer &buffer_; Char *start_; public: FormatBuf(Buffer &buffer) : buffer_(buffer), start_(&buffer[0]) { this->setp(start_, start_ + buffer_.capacity()); } int_type overflow(int_type ch = traits_type::eof()) { if (!traits_type::eq_int_type(ch, traits_type::eof())) { size_t buf_size = size(); buffer_.resize(buf_size); buffer_.reserve(buf_size * 2); start_ = &buffer_[0]; start_[buf_size] = traits_type::to_char_type(ch); this->setp(start_+ buf_size + 1, start_ + buf_size * 2); } return ch; } size_t size() const { return to_unsigned(this->pptr() - start_); } }; Yes &convert(std::ostream &); struct DummyStream : std::ostream { DummyStream(); // Suppress a bogus warning in MSVC. // Hide all operator<< overloads from std::ostream. void operator<<(Null<>); }; No &operator<<(std::ostream &, int); template struct ConvertToIntImpl { // Convert to int only if T doesn't have an overloaded operator<<. enum { value = sizeof(convert(get() << get())) == sizeof(No) }; }; } // namespace internal // Formats a value. template void format(BasicFormatter &f, const Char *&format_str, const T &value) { internal::MemoryBuffer buffer; internal::FormatBuf format_buf(buffer); std::basic_ostream output(&format_buf); output << value; BasicStringRef str(&buffer[0], format_buf.size()); typedef internal::MakeArg< BasicFormatter > MakeArg; format_str = f.format(format_str, MakeArg(str)); } /** \rst Prints formatted data to the stream *os*. **Example**:: print(cerr, "Don't {}!", "panic"); \endrst */ FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); FMT_VARIADIC(void, print, std::ostream &, CStringRef) /** \rst Prints formatted data to the stream *os*. **Example**:: fprintf(cerr, "Don't %s!", "panic"); \endrst */ FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args); FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) } // namespace fmt #ifdef FMT_HEADER_ONLY # include "ostream.cc" #endif #endif // FMT_OSTREAM_H_ DarkRadiant-2.5.0/libs/libfmt/fmt/posix.cc000066400000000000000000000152421321750546400203620ustar00rootroot00000000000000/* A C++ interface to POSIX functions. Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. For the license information refer to format.h. */ // Disable bogus MSVC warnings. #ifndef _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS #endif #include "posix.h" #include #include #include #ifndef _WIN32 # include #else # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include # include # define O_CREAT _O_CREAT # define O_TRUNC _O_TRUNC # ifndef S_IRUSR # define S_IRUSR _S_IREAD # endif # ifndef S_IWUSR # define S_IWUSR _S_IWRITE # endif # ifdef __MINGW32__ # define _SH_DENYNO 0x40 # endif #endif // _WIN32 #ifdef fileno # undef fileno #endif namespace { #ifdef _WIN32 // Return type of read and write functions. typedef int RWResult; // On Windows the count argument to read and write is unsigned, so convert // it from size_t preventing integer overflow. inline unsigned convert_rwcount(std::size_t count) { return count <= UINT_MAX ? static_cast(count) : UINT_MAX; } #else // Return type of read and write functions. typedef ssize_t RWResult; inline std::size_t convert_rwcount(std::size_t count) { return count; } #endif } fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT { if (file_ && FMT_SYSTEM(fclose(file_)) != 0) fmt::report_system_error(errno, "cannot close file"); } fmt::BufferedFile::BufferedFile( fmt::CStringRef filename, fmt::CStringRef mode) { FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0); if (!file_) FMT_THROW(SystemError(errno, "cannot open file {}", filename)); } void fmt::BufferedFile::close() { if (!file_) return; int result = FMT_SYSTEM(fclose(file_)); file_ = FMT_NULL; if (result != 0) FMT_THROW(SystemError(errno, "cannot close file")); } // A macro used to prevent expansion of fileno on broken versions of MinGW. #define FMT_ARGS int fmt::BufferedFile::fileno() const { int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); if (fd == -1) FMT_THROW(SystemError(errno, "cannot get file descriptor")); return fd; } fmt::File::File(fmt::CStringRef path, int oflag) { int mode = S_IRUSR | S_IWUSR; #if defined(_WIN32) && !defined(__MINGW32__) fd_ = -1; FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); #else FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); #endif if (fd_ == -1) FMT_THROW(SystemError(errno, "cannot open file {}", path)); } fmt::File::~File() FMT_NOEXCEPT { // Don't retry close in case of EINTR! // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) fmt::report_system_error(errno, "cannot close file"); } void fmt::File::close() { if (fd_ == -1) return; // Don't retry close in case of EINTR! // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html int result = FMT_POSIX_CALL(close(fd_)); fd_ = -1; if (result != 0) FMT_THROW(SystemError(errno, "cannot close file")); } fmt::LongLong fmt::File::size() const { #ifdef _WIN32 // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT // is less than 0x0500 as is the case with some default MinGW builds. // Both functions support large file sizes. DWORD size_upper = 0; HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); if (size_lower == INVALID_FILE_SIZE) { DWORD error = GetLastError(); if (error != NO_ERROR) FMT_THROW(WindowsError(GetLastError(), "cannot get file size")); } fmt::ULongLong long_size = size_upper; return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; #else typedef struct stat Stat; Stat file_stat = Stat(); if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) FMT_THROW(SystemError(errno, "cannot get file attributes")); FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size), "return type of File::size is not large enough"); return file_stat.st_size; #endif } std::size_t fmt::File::read(void *buffer, std::size_t count) { RWResult result = 0; FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); if (result < 0) FMT_THROW(SystemError(errno, "cannot read from file")); return internal::to_unsigned(result); } std::size_t fmt::File::write(const void *buffer, std::size_t count) { RWResult result = 0; FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); if (result < 0) FMT_THROW(SystemError(errno, "cannot write to file")); return internal::to_unsigned(result); } fmt::File fmt::File::dup(int fd) { // Don't retry as dup doesn't return EINTR. // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html int new_fd = FMT_POSIX_CALL(dup(fd)); if (new_fd == -1) FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd)); return File(new_fd); } void fmt::File::dup2(int fd) { int result = 0; FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); if (result == -1) { FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {} to {}", fd_, fd)); } } void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT { int result = 0; FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); if (result == -1) ec = ErrorCode(errno); } void fmt::File::pipe(File &read_end, File &write_end) { // Close the descriptors first to make sure that assignments don't throw // and there are no leaks. read_end.close(); write_end.close(); int fds[2] = {}; #ifdef _WIN32 // Make the default pipe capacity same as on Linux 2.6.11+. enum { DEFAULT_CAPACITY = 65536 }; int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); #else // Don't retry as the pipe function doesn't return EINTR. // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html int result = FMT_POSIX_CALL(pipe(fds)); #endif if (result != 0) FMT_THROW(SystemError(errno, "cannot create pipe")); // The following assignments don't throw because read_fd and write_fd // are closed. read_end = File(fds[0]); write_end = File(fds[1]); } fmt::BufferedFile fmt::File::fdopen(const char *mode) { // Don't retry as fdopen doesn't return EINTR. FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); if (!f) FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor")); BufferedFile file(f); fd_ = -1; return file; } long fmt::getpagesize() { #ifdef _WIN32 SYSTEM_INFO si; GetSystemInfo(&si); return si.dwPageSize; #else long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); if (size < 0) FMT_THROW(SystemError(errno, "cannot get memory page size")); return size; #endif } DarkRadiant-2.5.0/libs/libfmt/fmt/posix.h000066400000000000000000000234711321750546400202270ustar00rootroot00000000000000/* A C++ interface to POSIX functions. Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. For the license information refer to format.h. */ #ifndef FMT_POSIX_H_ #define FMT_POSIX_H_ #if defined(__MINGW32__) || defined(__CYGWIN__) // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. # undef __STRICT_ANSI__ #endif #include #include // for O_RDONLY #include // for locale_t #include #include // for strtod_l #include #if defined __APPLE__ || defined(__FreeBSD__) # include // for LC_NUMERIC_MASK on OS X #endif #include "format.h" #ifndef FMT_POSIX # if defined(_WIN32) && !defined(__MINGW32__) // Fix warnings about deprecated symbols. # define FMT_POSIX(call) _##call # else # define FMT_POSIX(call) call # endif #endif // Calls to system functions are wrapped in FMT_SYSTEM for testability. #ifdef FMT_SYSTEM # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) #else # define FMT_SYSTEM(call) call # ifdef _WIN32 // Fix warnings about deprecated symbols. # define FMT_POSIX_CALL(call) ::_##call # else # define FMT_POSIX_CALL(call) ::call # endif #endif #if FMT_GCC_VERSION >= 407 # define FMT_UNUSED __attribute__((unused)) #else # define FMT_UNUSED #endif #ifndef FMT_USE_STATIC_ASSERT # define FMT_USE_STATIC_ASSERT 0 #endif #if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \ (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600 # define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message) #else # define FMT_CONCAT_(a, b) FMT_CONCAT(a, b) # define FMT_STATIC_ASSERT(cond, message) \ typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED #endif // Retries the expression while it evaluates to error_result and errno // equals to EINTR. #ifndef _WIN32 # define FMT_RETRY_VAL(result, expression, error_result) \ do { \ result = (expression); \ } while (result == error_result && errno == EINTR) #else # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) #endif #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) namespace fmt { // An error code. class ErrorCode { private: int value_; public: explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {} int get() const FMT_NOEXCEPT { return value_; } }; // A buffered file. class BufferedFile { private: FILE *file_; friend class File; explicit BufferedFile(FILE *f) : file_(f) {} public: // Constructs a BufferedFile object which doesn't represent any file. BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {} // Destroys the object closing the file it represents if any. FMT_API ~BufferedFile() FMT_NOEXCEPT; #if !FMT_USE_RVALUE_REFERENCES // Emulate a move constructor and a move assignment operator if rvalue // references are not supported. private: // A proxy object to emulate a move constructor. // It is private to make it impossible call operator Proxy directly. struct Proxy { FILE *file; }; public: // A "move constructor" for moving from a temporary. BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {} // A "move constructor" for moving from an lvalue. BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) { f.file_ = FMT_NULL; } // A "move assignment operator" for moving from a temporary. BufferedFile &operator=(Proxy p) { close(); file_ = p.file; return *this; } // A "move assignment operator" for moving from an lvalue. BufferedFile &operator=(BufferedFile &other) { close(); file_ = other.file_; other.file_ = FMT_NULL; return *this; } // Returns a proxy object for moving from a temporary: // BufferedFile file = BufferedFile(...); operator Proxy() FMT_NOEXCEPT { Proxy p = {file_}; file_ = FMT_NULL; return p; } #else private: FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); public: BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) { other.file_ = FMT_NULL; } BufferedFile& operator=(BufferedFile &&other) { close(); file_ = other.file_; other.file_ = FMT_NULL; return *this; } #endif // Opens a file. FMT_API BufferedFile(CStringRef filename, CStringRef mode); // Closes the file. FMT_API void close(); // Returns the pointer to a FILE object representing this file. FILE *get() const FMT_NOEXCEPT { return file_; } // We place parentheses around fileno to workaround a bug in some versions // of MinGW that define fileno as a macro. FMT_API int (fileno)() const; void print(CStringRef format_str, const ArgList &args) { fmt::print(file_, format_str, args); } FMT_VARIADIC(void, print, CStringRef) }; // A file. Closed file is represented by a File object with descriptor -1. // Methods that are not declared with FMT_NOEXCEPT may throw // fmt::SystemError in case of failure. Note that some errors such as // closing the file multiple times will cause a crash on Windows rather // than an exception. You can get standard behavior by overriding the // invalid parameter handler with _set_invalid_parameter_handler. class File { private: int fd_; // File descriptor. // Constructs a File object with a given descriptor. explicit File(int fd) : fd_(fd) {} public: // Possible values for the oflag argument to the constructor. enum { RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. }; // Constructs a File object which doesn't represent any file. File() FMT_NOEXCEPT : fd_(-1) {} // Opens a file and constructs a File object representing this file. FMT_API File(CStringRef path, int oflag); #if !FMT_USE_RVALUE_REFERENCES // Emulate a move constructor and a move assignment operator if rvalue // references are not supported. private: // A proxy object to emulate a move constructor. // It is private to make it impossible call operator Proxy directly. struct Proxy { int fd; }; public: // A "move constructor" for moving from a temporary. File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} // A "move constructor" for moving from an lvalue. File(File &other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } // A "move assignment operator" for moving from a temporary. File &operator=(Proxy p) { close(); fd_ = p.fd; return *this; } // A "move assignment operator" for moving from an lvalue. File &operator=(File &other) { close(); fd_ = other.fd_; other.fd_ = -1; return *this; } // Returns a proxy object for moving from a temporary: // File file = File(...); operator Proxy() FMT_NOEXCEPT { Proxy p = {fd_}; fd_ = -1; return p; } #else private: FMT_DISALLOW_COPY_AND_ASSIGN(File); public: File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } File& operator=(File &&other) { close(); fd_ = other.fd_; other.fd_ = -1; return *this; } #endif // Destroys the object closing the file it represents if any. FMT_API ~File() FMT_NOEXCEPT; // Returns the file descriptor. int descriptor() const FMT_NOEXCEPT { return fd_; } // Closes the file. FMT_API void close(); // Returns the file size. The size has signed type for consistency with // stat::st_size. FMT_API LongLong size() const; // Attempts to read count bytes from the file into the specified buffer. FMT_API std::size_t read(void *buffer, std::size_t count); // Attempts to write count bytes from the specified buffer to the file. FMT_API std::size_t write(const void *buffer, std::size_t count); // Duplicates a file descriptor with the dup function and returns // the duplicate as a file object. FMT_API static File dup(int fd); // Makes fd be the copy of this file descriptor, closing fd first if // necessary. FMT_API void dup2(int fd); // Makes fd be the copy of this file descriptor, closing fd first if // necessary. FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT; // Creates a pipe setting up read_end and write_end file objects for reading // and writing respectively. FMT_API static void pipe(File &read_end, File &write_end); // Creates a BufferedFile object associated with this file and detaches // this File object from the file. FMT_API BufferedFile fdopen(const char *mode); }; // Returns the memory page size. long getpagesize(); #if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \ !defined(__ANDROID__) && !defined(__CYGWIN__) # define FMT_LOCALE #endif #ifdef FMT_LOCALE // A "C" numeric locale. class Locale { private: # ifdef _MSC_VER typedef _locale_t locale_t; enum { LC_NUMERIC_MASK = LC_NUMERIC }; static locale_t newlocale(int category_mask, const char *locale, locale_t) { return _create_locale(category_mask, locale); } static void freelocale(locale_t locale) { _free_locale(locale); } static double strtod_l(const char *nptr, char **endptr, _locale_t locale) { return _strtod_l(nptr, endptr, locale); } # endif locale_t locale_; FMT_DISALLOW_COPY_AND_ASSIGN(Locale); public: typedef locale_t Type; Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { if (!locale_) FMT_THROW(fmt::SystemError(errno, "cannot create locale")); } ~Locale() { freelocale(locale_); } Type get() const { return locale_; } // Converts string to floating-point number and advances str past the end // of the parsed input. double strtod(const char *&str) const { char *end = FMT_NULL; double result = strtod_l(str, &end, locale_); str = end; return result; } }; #endif // FMT_LOCALE } // namespace fmt #if !FMT_USE_RVALUE_REFERENCES namespace std { // For compatibility with C++98. inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; } inline fmt::File &move(fmt::File &f) { return f; } } #endif #endif // FMT_POSIX_H_ DarkRadiant-2.5.0/libs/libfmt/fmt/time.h000066400000000000000000000027731321750546400200250ustar00rootroot00000000000000/* Formatting library for C++ - time formatting Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. For the license information refer to format.h. */ #ifndef FMT_TIME_H_ #define FMT_TIME_H_ #include "format.h" #include namespace fmt { template void format(BasicFormatter &f, const char *&format_str, const std::tm &tm) { if (*format_str == ':') ++format_str; const char *end = format_str; while (*end && *end != '}') ++end; if (*end != '}') FMT_THROW(FormatError("missing '}' in format string")); internal::MemoryBuffer format; format.append(format_str, end + 1); format[format.size() - 1] = '\0'; Buffer &buffer = f.writer().buffer(); std::size_t start = buffer.size(); for (;;) { std::size_t size = buffer.capacity() - start; std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); if (count != 0) { buffer.resize(start + count); break; } if (size >= format.size() * 256) { // If the buffer is 256 times larger than the format string, assume // that `strftime` gives an empty result. There doesn't seem to be a // better way to distinguish the two cases: // https://github.com/fmtlib/fmt/issues/367 break; } const std::size_t MIN_GROWTH = 10; buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); } format_str = end + 1; } } #endif // FMT_TIME_H_ DarkRadiant-2.5.0/libs/math/000077500000000000000000000000001321750546400155735ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/math/AABB.cpp000066400000000000000000000124751321750546400167750ustar00rootroot00000000000000#include "AABB.h" #include "Plane3.h" #include "Matrix4.h" // Expand this AABB to include the given point. void AABB::includePoint(const Vector3& point) { // If not yet initialised, simply set the AABB to equal the point if (!isValid()) { origin = point; extents = Vector3(0, 0, 0); } else { // Extend each axis separately for (int i = 0; i < 3; ++i) { // Axis displacement from origin to point double axisDisp = point[i] - origin[i]; // Half of extent increase needed (maybe negative if point inside) double halfDif = 0.5f * (std::abs(axisDisp) - extents[i]); if (halfDif > 0) { origin[i] += (axisDisp > 0) ? halfDif : -halfDif; extents[i] += halfDif; } } } } // Expand this AABB to include another AABB void AABB::includeAABB(const AABB& other) { // Validity check. If both this and other are valid, use the extension // algorithm. If only the other AABB is valid, set this AABB equal to it. // If neither are valid we do nothing. if (isValid() && other.isValid()) { // Extend each axis separately for (int i = 0; i < 3; ++i) { double displacement = other.origin[i] - origin[i]; double difference = other.extents[i] - extents[i]; if (fabs(displacement) > fabs(difference)) { double half_difference = 0.5f * (fabs(displacement) + difference); if (half_difference > 0.0f) { origin[i] += (displacement >= 0.0f) ? half_difference : -half_difference; extents[i] += half_difference; } } else if (difference > 0.0f) { origin[i] = other.origin[i]; extents[i] = other.extents[i]; } } } else if (other.isValid()) { origin = other.origin; extents = other.extents; } } VolumeIntersectionValue AABB::classifyPlane(const Plane3& plane) const { double originDot = plane.normal().dot(origin); double extentsDot = fabs(plane.normal().x()) * extents.x() + fabs(plane.normal().y()) * extents.y() + fabs(plane.normal().z()) * extents.z(); if (originDot + extentsDot - plane.dist() < 0) { // largest dot product is still behind the plane return VOLUME_OUTSIDE; } if (originDot - extentsDot - plane.dist() >= 0) { // smallest dot product still in front of the plane return VOLUME_INSIDE; } return VOLUME_PARTIAL; #if 0 // greebo: I've adjusted this code (as the old one was very likely wrong) // following the explanations on AABB vs. Frustum intersection tests // found here: http://fgiesen.wordpress.com/2010/10/17/view-frustum-culling/ double originDot = plane.normal().dot(origin); double extendsDot = fabs(plane.normal().x()) * extents[0] + fabs(plane.normal().y()) * extents[1] + fabs(plane.normal().z()) * extents[2]; if (originDot + extendsDot > plane.dist()) { // At least one point is on the positive side of the plane return VOLUME_PARTIAL; // partially inside } else if (originDot - extendsDot >= plane.dist()) { // Even the minimum point is on the positive side return VOLUME_INSIDE; // totally inside } return VOLUME_OUTSIDE; // totally outside #endif } unsigned int AABB::classifyOrientedPlane(const Matrix4& transform, const Plane3& plane) const { double distance_origin = plane.normal().dot(origin) + plane.dist(); if (fabs(distance_origin) < (fabs(extents[0] * plane.normal().dot(transform.x().getVector3())) + fabs(extents[1] * plane.normal().dot(transform.y().getVector3())) + fabs(extents[2] * plane.normal().dot(transform.z().getVector3())))) { return 1; // partially inside } else if (distance_origin < 0) { return 2; // totally inside } return 0; // totally outside } void AABB::getCorners(Vector3 corners[8], const Matrix4& rotation) const { Vector3 x = rotation.x().getVector3() * extents.x(); Vector3 y = rotation.y().getVector3() * extents.y(); Vector3 z = rotation.z().getVector3() * extents.z(); corners[0] = origin - x + y + z; corners[1] = origin + x + y + z; corners[2] = origin + x - y + z; corners[3] = origin - x - y + z; corners[4] = origin - x + y - z; corners[5] = origin + x + y - z; corners[6] = origin + x - y - z; corners[7] = origin - x - y - z; } void AABB::getPlanes(Plane3 planes[6], const Matrix4& rotation) const { double x = rotation.x().getVector3().dot(origin); double y = rotation.y().getVector3().dot(origin); double z = rotation.z().getVector3().dot(origin); planes[0] = Plane3( rotation.x().getVector3(), x + extents[0]); planes[1] = Plane3(-rotation.x().getVector3(), -(x - extents[0])); planes[2] = Plane3( rotation.y().getVector3(), y + extents[1]); planes[3] = Plane3(-rotation.y().getVector3(), -(y - extents[1])); planes[4] = Plane3( rotation.z().getVector3(), z + extents[2]); planes[5] = Plane3(-rotation.z().getVector3(), -(z - extents[2])); } AABB AABB::createFromOrientedAABB(const AABB& aabb, const Matrix4& transform) { return AABB( transform.transformPoint(aabb.origin), Vector3( fabs(transform[0] * aabb.extents[0]) + fabs(transform[4] * aabb.extents[1]) + fabs(transform[8] * aabb.extents[2]), fabs(transform[1] * aabb.extents[0]) + fabs(transform[5] * aabb.extents[1]) + fabs(transform[9] * aabb.extents[2]), fabs(transform[2] * aabb.extents[0]) + fabs(transform[6] * aabb.extents[1]) + fabs(transform[10] * aabb.extents[2]) ) ); } DarkRadiant-2.5.0/libs/math/AABB.h000066400000000000000000000220321321750546400164300ustar00rootroot00000000000000#pragma once #include "math/Vector3.h" #include "math/Plane3.h" #include "VolumeIntersectionValue.h" // Forward declaration, include Matrix4.h for definition class Matrix4; /** * An Axis Aligned Bounding Box is a simple cuboid which encloses a given set * of points, such as the vertices of a model. It is defined by an origin, * located at the centre of the AABB, and symmetrical extents in 3 dimension * which determine its size. * * A valid extents vector has all components >= 0, an assumption allowing for * a few optimisations. */ class AABB { public: /// The origin of the AABB, which is always located at the centre. Vector3 origin; /// The symmetrical extents in 3 dimensions. Vector3 extents; /** Construct an AABB with default origin and invalid extents. */ AABB() : origin(0, 0, 0), extents(-1,-1,-1) {} /** Construct an AABB with the provided origin and extents * vectors. */ AABB(const Vector3& origin_, const Vector3& extents_) : origin(origin_), extents(extents_) {} /** * Static named constructor to create an AABB that encloses the provided * minimum and maximum points. */ static AABB createFromMinMax(const Vector3& min, const Vector3& max); /** * Static named constructor to create an AABB that encloses the given AABB rotated * by the given transformation matrix. */ static AABB createFromOrientedAABB(const AABB& aabb, const Matrix4& transform); /** * Static named constructor to create an AABB that encloses the given AABB rotated * by the given transformation matrix. If the given AABB is not valid, it is returned as it is. */ static AABB createFromOrientedAABBSafe(const AABB& aabb, const Matrix4& transform); /** * Create an AABB that is as large as possible (extents are filled with FLT_MAX) */ static AABB createInfinite(); /** * Equality operator, returns true if both origina nd extents are exactly the same */ bool operator==(const AABB& other) const; /** * Inequality operator, using this is equivalent to calling !(operator==) */ bool operator!=(const AABB& other) const; /** * Check whether the AABB is valid, or if the extents are still uninitialised */ bool isValid() const; /** Get the origin of this AABB. * * @returns * A const reference to a Vector3 containing the AABB's origin. */ const Vector3& getOrigin() const; /** Get the extents of this AABB. * * @returns * A const reference to a Vector3 containing the AABB's extents. */ const Vector3& getExtents() const; /** Get the radius of the smallest sphere which encloses this * bounding box. */ float getRadius() const; /** Expand this AABB in-place to include the given point in * world space. * * @param point * Vector3 representing the point to include. */ void includePoint(const Vector3& point); /** Expand this AABB in-place to include the given AABB in * world space. * * @param other * The other AABB to include. */ void includeAABB(const AABB& other); /** * Extends this AABB by the given vector's length. * Equivalent to aabb.extents += extension */ void extendBy(const Vector3& extension); /** * Returns true if this AABB contains the other AABB (all dimensions) */ bool contains(const AABB& other) const; /** * Returns true if this AABB intersects the given one. */ bool intersects(const AABB& other) const; /** * Returns true if this AABB intersects the given point. */ bool intersects(const Vector3& point) const; /** * Classifies the position of this AABB with respect to the given plane. * * @returns: intersection classification, where VOLUME_INSIDE refers to * the AABB being completely on the positive side of the plane (where the * normal vector is pointing to). */ VolumeIntersectionValue classifyPlane(const Plane3& plane) const; /** * Like classifyPlane, but uses the given matrix to transform the plane. * * TODO: Better documentation. TODO: Use enum as return value * * @returns: 0 = totally outside, 1 = partially inside, 2 = totally inside */ unsigned int classifyOrientedPlane(const Matrix4& transform, const Plane3& plane) const; /** * Stores the 3D coordinates of the AABB corner points into the given array. */ void getCorners(Vector3 corners[8]) const; /** * Stores the 3D coordinates of the rotated AABB corner points into the given array. * The rotation is performed using the given transformation matrix. */ void getCorners(Vector3 corners[8], const Matrix4& rotation) const; /** * Writes the 6 Plane3 objects into the given array, corresponding to the * six sides of this AABB. */ void getPlanes(Plane3 planes[6]) const; /** * Writes the 6 Plane3 objects into the given array, corresponding to the * six rotated sides of this AABB. The given matrix is used to rotate this AABB. */ void getPlanes(Plane3 planes[6], const Matrix4& rotation) const; }; inline AABB AABB::createFromMinMax(const Vector3& min, const Vector3& max) { // Origin is the midpoint of the two vectors Vector3 origin = (min + max) * 0.5f; // Extents is the vector from the origin to the max point Vector3 extents = max - origin; // Construct and return the resulting AABB; return AABB(origin, extents); } inline AABB AABB::createFromOrientedAABBSafe(const AABB& aabb, const Matrix4& transform) { return aabb.isValid() ? createFromOrientedAABB(aabb, transform) : aabb; } inline AABB AABB::createInfinite() { return AABB(Vector3(0, 0, 0), Vector3(FLT_MAX, FLT_MAX, FLT_MAX)); } inline bool AABB::operator==(const AABB& other) const { return (origin == other.origin && extents == other.extents); } inline bool AABB::operator!=(const AABB& other) const { return !operator==(other); } inline bool AABB::isValid() const { // Check each origin and extents value. The origins must be between // +/- FLT_MAX, and the extents between 0 and FLT_MAX. for (int i = 0; i < 3; ++i) { if (origin[i] < -FLT_MAX || origin[i] > FLT_MAX || extents[i] < 0 || extents[i] > FLT_MAX) { return false; } } return true; // all checks passed } inline const Vector3& AABB::getOrigin() const { return origin; } inline const Vector3& AABB::getExtents() const { return extents; } inline float AABB::getRadius() const { return extents.getLength(); // Pythagorean length of extents vector } inline bool AABB::contains(const AABB& other) const { // Return true if all coordinates of are contained within these bounds return (origin[0] + extents[0] >= other.origin[0] + other.extents[0]) && (origin[0] - extents[0] <= other.origin[0] - other.extents[0]) && (origin[1] + extents[1] >= other.origin[1] + other.extents[1]) && (origin[1] - extents[1] <= other.origin[1] - other.extents[1]) && (origin[2] + extents[2] >= other.origin[2] + other.extents[2]) && (origin[2] - extents[2] <= other.origin[2] - other.extents[2]); } inline bool AABB::intersects(const AABB& other) const { return fabs(other.origin[0] - origin[0]) < (extents[0] + other.extents[0]) && fabs(other.origin[1] - origin[1]) < (extents[1] + other.extents[1]) && fabs(other.origin[2] - origin[2]) < (extents[2] + other.extents[2]); } inline bool AABB::intersects(const Vector3& point) const { return fabs(point[0] - origin[0]) < extents[0] && fabs(point[1] - origin[1]) < extents[1] && fabs(point[2] - origin[2]) < extents[2]; } inline void AABB::extendBy(const Vector3& extension) { extents += extension; } inline void AABB::getCorners(Vector3 corners[8]) const { Vector3 min(origin - extents); Vector3 max(origin + extents); corners[0] = Vector3(min[0], max[1], max[2]); corners[1] = Vector3(max[0], max[1], max[2]); corners[2] = Vector3(max[0], min[1], max[2]); corners[3] = Vector3(min[0], min[1], max[2]); corners[4] = Vector3(min[0], max[1], min[2]); corners[5] = Vector3(max[0], max[1], min[2]); corners[6] = Vector3(max[0], min[1], min[2]); corners[7] = Vector3(min[0], min[1], min[2]); } inline void AABB::getPlanes(Plane3 planes[6]) const { planes[0] = Plane3( g_vector3_axes[0], origin[0] + extents[0]); planes[1] = Plane3(-g_vector3_axes[0], -(origin[0] - extents[0])); planes[2] = Plane3( g_vector3_axes[1], origin[1] + extents[1]); planes[3] = Plane3(-g_vector3_axes[1], -(origin[1] - extents[1])); planes[4] = Plane3( g_vector3_axes[2], origin[2] + extents[2]); planes[5] = Plane3(-g_vector3_axes[2], -(origin[2] - extents[2])); } /** * Stream insertion for AABB class. */ inline std::ostream& operator<< (std::ostream& os, const AABB& aabb) { os << "AABB { origin=" << aabb.getOrigin() << ", extents=" << aabb.getExtents() << " }"; return os; } class AABBExtendByPoint { AABB& m_aabb; public: AABBExtendByPoint(AABB& aabb) : m_aabb(aabb) { } void operator()(const Vector3& point) const { m_aabb.includePoint(point); } }; const Vector3 aabb_normals[6] = { Vector3( 1, 0, 0 ), Vector3( 0, 1, 0 ), Vector3( 0, 0, 1 ), Vector3(-1, 0, 0 ), Vector3( 0,-1, 0 ), Vector3( 0, 0,-1 ), }; const double aabb_texcoord_topleft[2] = { 0, 0 }; const double aabb_texcoord_topright[2] = { 1, 0 }; const double aabb_texcoord_botleft[2] = { 0, 1 }; const double aabb_texcoord_botright[2] = { 1, 1 }; DarkRadiant-2.5.0/libs/math/FloatTools.h000066400000000000000000000056371321750546400200450ustar00rootroot00000000000000#ifndef FLOATTOOLS_H_ #define FLOATTOOLS_H_ /* greebo: this contains some handy (?) functions for manipulating float variables */ #include #include "lrint.h" #if defined (_MSC_VER) /*inline int lrint (double flt) { int i; _asm { fld flt fistp i }; return i; } */ #elif defined(__FreeBSD__) inline int lrint(double f) { return static_cast(f + 0.5); } #elif defined(__GNUC__) // lrint is part of ISO C99 #define _ISOC9X_SOURCE 1 #define _ISOC99_SOURCE 1 #define __USE_ISOC9X 1 #define __USE_ISOC99 1 #else #error "unsupported platform" #endif #include #include #include #include // ========================================================================================= /// \brief Returns true if \p self is equal to other \p other within \p epsilon. template inline bool float_equal_epsilon(const Element& self, const OtherElement& other, const Element& epsilon) { return fabs(other - self) < epsilon; } /// \brief Returns the value midway between \p self and \p other. template inline Element float_mid(const Element& self, const Element& other) { return Element((self + other) * 0.5); } /// \brief Returns \p f rounded to the nearest integer. Note that this is not the same behaviour as casting from float to int. template inline int float_to_integer(const Element& f) { return static_cast(lrint(f)); } /// \brief Returns \p f rounded to the nearest multiple of \p snap. template inline Element float_snapped(const Element& f, const OtherElement& snap) { return Element(float_to_integer(f / snap) * snap); } /// \brief Returns true if \p f has no decimal fraction part. template inline bool float_is_integer(const Element& f) { return f == Element(float_to_integer(f)); } /// \brief Returns \p self modulated by the range [0, \p modulus) /// \p self must be in the range [\p -modulus, \p modulus) template inline Element float_mod_range(const Element& self, const ModulusElement& modulus) { return Element((self < 0.0) ? self + modulus : self); } /// \brief Returns \p self modulated by the range [0, \p modulus) template inline Element float_mod(const Element& self, const ModulusElement& modulus) { return float_mod_range(Element(fmod(static_cast(self), static_cast(modulus))), modulus); } template inline bool isNaN(Element x) { return x != x; } // greebo: This checks the given element for NaN and infinity // returns TRUE if the number is safe template inline bool isValid(Element x) { // Check for infinity and NaN return !isNaN(x) && !(std::numeric_limits::has_infinity && fabs(x) == std::numeric_limits::infinity()); } #endif /*FLOATTOOLS_H_*/ DarkRadiant-2.5.0/libs/math/Frustum.cpp000066400000000000000000000107051321750546400177470ustar00rootroot00000000000000#include "Frustum.h" #include "AABB.h" // Normalise all planes in frustum void Frustum::normalisePlanes() { left = left.getNormalised(); right = right.getNormalised(); top = top.getNormalised(); bottom = bottom.getNormalised(); back = back.getNormalised(); front = front.getNormalised(); } // Get a projection matrix from the frustum Matrix4 Frustum::getProjectionMatrix() const { return Matrix4::byColumns( // col 1 (right.normal().x() - left.normal().x()) / 2, (top.normal().x() - bottom.normal().x()) / 2, (back.normal().x() - front.normal().x()) / 2, right.normal().x() - (right.normal().x() - left.normal().x()) / 2, // col 2 (right.normal().y() - left.normal().y()) / 2, (top.normal().y() - bottom.normal().y()) / 2, (back.normal().y() - front.normal().y()) / 2, right.normal().y() - (right.normal().y() - left.normal().y()) / 2, // col 3 (right.normal().z() - left.normal().z()) / 2, (top.normal().z() - bottom.normal().z()) / 2, (back.normal().z() - front.normal().z()) / 2, right.normal().z() - (right.normal().z() - left.normal().z()) / 2, // col 4 (right.dist() - left.dist()) / 2, (top.dist() - bottom.dist()) / 2, (back.dist() - front.dist()) / 2, right.dist() - (right.dist() - left.dist()) / 2 ); } // Get a transformed copy of this frustum Frustum Frustum::getTransformedBy(const Matrix4& matrix) const { // greebo: DR's Plane3 is seriuosly hampered by its internal representation // which is nx,ny,nz,dist instead of a,b,c,d. This causes a lot of confusion // and makes it necessary to invert the dist() member each time before // applying a transformation matrix. Plane3 rightTemp = Plane3(right.normal(), -right.dist()).transform(matrix); Plane3 leftTemp = Plane3(left.normal(), -left.dist()).transform(matrix); Plane3 topTemp = Plane3(top.normal(), -top.dist()).transform(matrix); Plane3 bottomTemp = Plane3(bottom.normal(), -bottom.dist()).transform(matrix); Plane3 backTemp = Plane3(back.normal(), -back.dist()).transform(matrix); Plane3 frontTemp = Plane3(front.normal(), -front.dist()).transform(matrix); rightTemp.dist() = -rightTemp.dist(); leftTemp.dist() = -leftTemp.dist(); topTemp.dist() = -topTemp.dist(); bottomTemp.dist() = -bottomTemp.dist(); backTemp.dist() = -backTemp.dist(); frontTemp.dist() = -frontTemp.dist(); return Frustum( rightTemp, leftTemp, bottomTemp, topTemp, backTemp, frontTemp ); } // Test intersection with an AABB VolumeIntersectionValue Frustum::testIntersection(const AABB& aabb) const { VolumeIntersectionValue result = VOLUME_INSIDE; switch (aabb.classifyPlane(right)) { case VOLUME_OUTSIDE: return VOLUME_OUTSIDE; case VOLUME_PARTIAL: result = VOLUME_PARTIAL; break; default: break; } switch (aabb.classifyPlane(left)) { case VOLUME_OUTSIDE: return VOLUME_OUTSIDE; case VOLUME_PARTIAL: result = VOLUME_PARTIAL; break; default: break; } switch (aabb.classifyPlane(bottom)) { case VOLUME_OUTSIDE: return VOLUME_OUTSIDE; case VOLUME_PARTIAL: result = VOLUME_PARTIAL; break; default: break; } switch (aabb.classifyPlane(top)) { case VOLUME_OUTSIDE: return VOLUME_OUTSIDE; case VOLUME_PARTIAL: result = VOLUME_PARTIAL; break; default: break; } switch (aabb.classifyPlane(back)) { case VOLUME_OUTSIDE: return VOLUME_OUTSIDE; case VOLUME_PARTIAL: result = VOLUME_PARTIAL; break; default: break; } switch (aabb.classifyPlane(front)) { case VOLUME_OUTSIDE: return VOLUME_OUTSIDE; case VOLUME_PARTIAL: result = VOLUME_PARTIAL; break; default: break; } return result; } VolumeIntersectionValue Frustum::testIntersection(const AABB& aabb, const Matrix4& localToWorld) const { AABB aabb_world(aabb); aabb_world.origin = localToWorld.transformPoint(aabb_world.origin); if (right.containsAABB(aabb_world, localToWorld) || left.containsAABB(aabb_world, localToWorld) || bottom.containsAABB(aabb_world, localToWorld) || top.containsAABB(aabb_world, localToWorld) || back.containsAABB(aabb_world, localToWorld) || front.containsAABB(aabb_world, localToWorld)) { return VOLUME_OUTSIDE; } return VOLUME_INSIDE; } DarkRadiant-2.5.0/libs/math/Frustum.h000066400000000000000000000076461321750546400174260ustar00rootroot00000000000000#pragma once /// \file /// \brief View-frustum data types and related operations. #include "math/Matrix4.h" #include "math/Segment.h" #include "VolumeIntersectionValue.h" class AABB; class Plane3; /** * \brief * Object representing a frustum, defined by six planes. */ class Frustum { public: Plane3 right, left, bottom, top, back, front; Frustum() {} Frustum(const Plane3& _right, const Plane3& _left, const Plane3& _bottom, const Plane3& _top, const Plane3& _back, const Plane3& _front) : right(_right), left(_left), bottom(_bottom), top(_top), back(_back), front(_front) {} /** * Construct the frustum planes from the given projection matrix. */ static Frustum createFromViewproj(const Matrix4& viewproj); /** * \brief * Normalise all planes in the frustum. */ void normalisePlanes(); /** * \brief * Get the projection matrix corresponding to the planes of this frustum. */ Matrix4 getProjectionMatrix() const; /** * \brief * Return a copy of this frustum transformed by the given matrix. */ Frustum getTransformedBy(const Matrix4& transform) const; /** * \brief * Test the intersection of this frustum with an AABB. */ VolumeIntersectionValue testIntersection(const AABB& aabb) const; /** * Test the intersection of this frustum with a transformed AABB. */ VolumeIntersectionValue testIntersection(const AABB& aabb, const Matrix4& localToWorld) const; /** * Returns true if the given point is contained in this frustum. */ bool testPoint(const Vector3& point) const; bool testLine(const Segment& segment) const; }; inline Frustum Frustum::createFromViewproj(const Matrix4& viewproj) { // greebo: Note that the usual plane-from-frustum equations which can be found // throughout the internet are referring to a plane format a,b,c,d whereas // DarkRadiant uses a,b,c and dist, that's why the fourth terms in each line are negated. return Frustum ( Plane3(viewproj[3] - viewproj[0], viewproj[7] - viewproj[4], viewproj[11] - viewproj[ 8], -viewproj[15] + viewproj[12]).getNormalised(), Plane3(viewproj[3] + viewproj[0], viewproj[7] + viewproj[4], viewproj[11] + viewproj[ 8], -viewproj[15] - viewproj[12]).getNormalised(), Plane3(viewproj[3] + viewproj[1], viewproj[7] + viewproj[5], viewproj[11] + viewproj[ 9], -viewproj[15] - viewproj[13]).getNormalised(), Plane3(viewproj[3] - viewproj[1], viewproj[7] - viewproj[5], viewproj[11] - viewproj[ 9], -viewproj[15] + viewproj[13]).getNormalised(), Plane3(viewproj[3] - viewproj[2], viewproj[7] - viewproj[6], viewproj[11] - viewproj[10], -viewproj[15] + viewproj[14]).getNormalised(), Plane3(viewproj[3] + viewproj[2], viewproj[7] + viewproj[6], viewproj[11] + viewproj[10], -viewproj[15] - viewproj[14]).getNormalised() ); } inline bool Frustum::testPoint(const Vector3& point) const { return !right.testPoint(point) && !left.testPoint(point) && !bottom.testPoint(point) && !top.testPoint(point) && !back.testPoint(point) && !front.testPoint(point); } inline bool plane3_test_line(const Plane3& plane, const Segment& segment) { return segment.classifyPlane(plane) == 2; // totally inside } inline bool Frustum::testLine(const Segment& segment) const { return !plane3_test_line(right, segment) && !plane3_test_line(left, segment) && !plane3_test_line(bottom, segment) && !plane3_test_line(top, segment) && !plane3_test_line(back, segment) && !plane3_test_line(front, segment); } /** * \brief * Operator insertion for Frustum. */ inline std::ostream& operator<< (std::ostream& os, const Frustum& frustum) { os << "Frustum { " << "left = " << frustum.left << ", " << "right = " << frustum.right << ", " << "top = " << frustum.top << ", " << "bottom = " << frustum.bottom << ", " << "front = " << frustum.front << ", " << "back = " << frustum.back << " }"; return os; } DarkRadiant-2.5.0/libs/math/Line.h000066400000000000000000000011331321750546400166310ustar00rootroot00000000000000#pragma once /// \file /// \brief Line data types and related operations. #include "Vector3.h" /// \brief A line segment defined by a start point and and end point. class Line { public: Vector3 start, end; Line() {} Line(const Vector3& start_, const Vector3& end_) : start(start_), end(end_) {} Vector3 getClosestPoint(const Vector3& point) const { Vector3 v = end - start; Vector3 w = point - start; double c1 = w.dot(v); if (c1 <= 0) { return start; } double c2 = v.dot(v); if (c2 <= c1) { return end; } return Vector3(start + v * (c1 / c2)); } }; DarkRadiant-2.5.0/libs/math/Makefile.am000066400000000000000000000015211321750546400176260ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs AM_CXXFLAGS = -fPIC pkglib_LTLIBRARIES = libmath.la libmath_la_LDFLAGS = -release @PACKAGE_VERSION@ libmath_la_SOURCES = Matrix4.cpp \ Frustum.cpp \ Plane3.cpp \ AABB.cpp \ Quaternion.cpp TESTS = vectorTest matrixTest quaternionTest planeTest check_PROGRAMS = vectorTest matrixTest quaternionTest planeTest vectorTest_SOURCES = test/vectorTest.cpp vectorTest_LDADD = $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) matrixTest_SOURCES = test/matrixTest.cpp matrixTest_LDADD = $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) libmath.la quaternionTest_SOURCES = test/quaternionTest.cpp quaternionTest_LDADD = $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) planeTest_SOURCES = test/planeTest.cpp planeTest_LDADD = $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) libmath.la DarkRadiant-2.5.0/libs/math/Matrix4.cpp000066400000000000000000000515501321750546400176350ustar00rootroot00000000000000#include "Matrix4.h" #include "Quaternion.h" #include namespace { /// \brief Returns \p euler angles converted from degrees to radians. inline Vector3 euler_degrees_to_radians(const Vector3& euler) { return Vector3( degrees_to_radians(euler.x()), degrees_to_radians(euler.y()), degrees_to_radians(euler.z()) ); } inline bool quaternion_component_is_90(double component) { return (fabs(component) - c_half_sqrt2f) < 0.001f; } } // Main explicit constructor (private) Matrix4::Matrix4(double xx_, double xy_, double xz_, double xw_, double yx_, double yy_, double yz_, double yw_, double zx_, double zy_, double zz_, double zw_, double tx_, double ty_, double tz_, double tw_) { xx() = xx_; xy() = xy_; xz() = xz_; xw() = xw_; yx() = yx_; yy() = yy_; yz() = yz_; yw() = yw_; zx() = zx_; zy() = zy_; zz() = zz_; zw() = zw_; tx() = tx_; ty() = ty_; tz() = tz_; tw() = tw_; } // Named constructors // Identity matrix const Matrix4& Matrix4::getIdentity() { static const Matrix4 _identity( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return _identity; } Matrix4 Matrix4::getRotation(const std::string& rotationString) { double rotation[9]; std::stringstream strm(rotationString); strm << std::skipws; for (int i = 0; i < 9; ++i) { strm >> rotation[i]; } if (!strm) { // Parsing failed, fall back to the identity matrix return Matrix4::getIdentity(); } return Matrix4::byColumns( rotation[0], rotation[1], rotation[2], 0, rotation[3], rotation[4], rotation[5], 0, rotation[6], rotation[7], rotation[8], 0, 0, 0, 0, 1 ); } // Get a translation matrix for the given vector Matrix4 Matrix4::getTranslation(const Vector3& translation) { return Matrix4::byColumns( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, translation.x(), translation.y(), translation.z(), 1 ); } // Get a rotation from 2 vectors (named constructor) Matrix4 Matrix4::getRotation(const Vector3& a, const Vector3& b) { double angle = a.angle(b); Vector3 axis = b.crossProduct(a).getNormalised(); return getRotation(axis, angle); } Matrix4 Matrix4::getRotation(const Vector3& axis, const double angle) { // Pre-calculate the terms double cosPhi = cos(angle); double sinPhi = sin(angle); double oneMinusCosPhi = static_cast(1) - cos(angle); double x = axis.x(); double y = axis.y(); double z = axis.z(); return Matrix4::byColumns( cosPhi + oneMinusCosPhi*x*x, oneMinusCosPhi*x*y - sinPhi*z, oneMinusCosPhi*x*z + sinPhi*y, 0, oneMinusCosPhi*y*x + sinPhi*z, cosPhi + oneMinusCosPhi*y*y, oneMinusCosPhi*y*z - sinPhi*x, 0, oneMinusCosPhi*z*x - sinPhi*y, oneMinusCosPhi*z*y + sinPhi*x, cosPhi + oneMinusCosPhi*z*z, 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotation(const Quaternion& quaternion) { const double x2 = quaternion[0] + quaternion[0]; const double y2 = quaternion[1] + quaternion[1]; const double z2 = quaternion[2] + quaternion[2]; const double xx = quaternion[0] * x2; const double xy = quaternion[0] * y2; const double xz = quaternion[0] * z2; const double yy = quaternion[1] * y2; const double yz = quaternion[1] * z2; const double zz = quaternion[2] * z2; const double wx = quaternion[3] * x2; const double wy = quaternion[3] * y2; const double wz = quaternion[3] * z2; return Matrix4::byColumns( 1.0f - (yy + zz), xy + wz, xz - wy, 0, xy - wz, 1.0f - (xx + zz), yz + wx, 0, xz + wy, yz - wx, 1.0f - (xx + yy), 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotationQuantised(const Quaternion& quaternion) { if (quaternion.y() == 0 && quaternion.z() == 0 && quaternion_component_is_90(quaternion.x()) && quaternion_component_is_90(quaternion.w())) { return Matrix4::getRotationAboutXForSinCos((quaternion.x() > 0) ? 1.0f : -1.0f, 0); } if (quaternion.x() == 0 && quaternion.z() == 0 && quaternion_component_is_90(quaternion.y()) && quaternion_component_is_90(quaternion.w())) { return Matrix4::getRotationAboutYForSinCos((quaternion.y() > 0) ? 1.0f : -1.0f, 0); } if (quaternion.x() == 0 && quaternion.y() == 0 && quaternion_component_is_90(quaternion.z()) && quaternion_component_is_90(quaternion.w())) { return Matrix4::getRotationAboutZForSinCos((quaternion.z() > 0) ? 1.0f : -1.0f, 0); } return getRotation(quaternion); } Matrix4 Matrix4::getRotationAboutXForSinCos(double s, double c) { return Matrix4::byColumns( 1, 0, 0, 0, 0, c, s, 0, 0,-s, c, 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotationAboutYForSinCos(double s, double c) { return Matrix4::byColumns( c, 0,-s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotationAboutZForSinCos(double s, double c) { return Matrix4::byColumns( c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); } /*! \verbatim clockwise rotation around X, Y, Z, facing along axis 1 0 0 cy 0 -sy cz sz 0 0 cx sx 0 1 0 -sz cz 0 0 -sx cx sy 0 cy 0 0 1 rows of Z by cols of Y cy*cz -sy*cz+sz -sy*sz+cz -sz*cy -sz*sy+cz .. or something like that.. final rotation is Z * Y * X cy*cz -sx*-sy*cz+cx*sz cx*-sy*sz+sx*cz -cy*sz sx*sy*sz+cx*cz -cx*-sy*sz+sx*cz sy -sx*cy cx*cy transposed cy.cz + 0.sz + sy.0 cy.-sz + 0 .cz + sy.0 cy.0 + 0 .0 + sy.1 | sx.sy.cz + cx.sz + -sx.cy.0 sx.sy.-sz + cx.cz + -sx.cy.0 sx.sy.0 + cx.0 + -sx.cy.1 | -cx.sy.cz + sx.sz + cx.cy.0 -cx.sy.-sz + sx.cz + cx.cy.0 -cx.sy.0 + 0 .0 + cx.cy.1 | \endverbatim */ Matrix4 Matrix4::getRotationForEulerXYZ(const Vector3& euler) { double cx = cos(euler[0]); double sx = sin(euler[0]); double cy = cos(euler[1]); double sy = sin(euler[1]); double cz = cos(euler[2]); double sz = sin(euler[2]); return Matrix4::byColumns( cy*cz, cy*sz, -sy, 0, sx*sy*cz + cx*-sz, sx*sy*sz + cx*cz, sx*cy, 0, cx*sy*cz + sx*sz, cx*sy*sz + -sx*cz, cx*cy, 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotationForEulerXYZDegrees(const Vector3& euler) { return getRotationForEulerXYZ(euler_degrees_to_radians(euler)); } Matrix4 Matrix4::getRotationForEulerYZX(const Vector3& euler) { double cx = cos(euler[0]); double sx = sin(euler[0]); double cy = cos(euler[1]); double sy = sin(euler[1]); double cz = cos(euler[2]); double sz = sin(euler[2]); return Matrix4::byColumns( cy*cz, cx*cy*sz + sx*sy, sx*cy*sz - cx*sy, 0, -sz, cx*cz, sx*cz, 0, sy*cz, cx*sy*sz - sx*cy, sx*sy*sz + cx*cy, 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotationForEulerYZXDegrees(const Vector3& euler) { return getRotationForEulerYZX(euler_degrees_to_radians(euler)); } Matrix4 Matrix4::getRotationForEulerXZY(const Vector3& euler) { double cx = cos(euler[0]); double sx = sin(euler[0]); double cy = cos(euler[1]); double sy = sin(euler[1]); double cz = cos(euler[2]); double sz = sin(euler[2]); return Matrix4::byColumns( cy*cz, sz, -sy*cz, 0, sx*sy - cx*cy*sz, cx*cz, cx*sy*sz + sx*cy, 0, sx*cy*sz + cx*sy, -sx*cz, cx*cy - sx*sy*sz, 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotationForEulerXZYDegrees(const Vector3& euler) { return getRotationForEulerXZY(euler_degrees_to_radians(euler)); } Matrix4 Matrix4::getRotationForEulerYXZ(const Vector3& euler) { double cx = cos(euler[0]); double sx = sin(euler[0]); double cy = cos(euler[1]); double sy = sin(euler[1]); double cz = cos(euler[2]); double sz = sin(euler[2]); return Matrix4::byColumns( cy*cz - sx*sy*sz, cy*sz + sx*sy*cz, -cx*sy, 0, -cx*sz, cx*cz, sx, 0, sy*cz + sx*cy*sz, sy*sz - sx*cy*cz, cx*cy, 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotationForEulerYXZDegrees(const Vector3& euler) { return getRotationForEulerYXZ(euler_degrees_to_radians(euler)); } Matrix4 Matrix4::getRotationForEulerZXY(const Vector3& euler) { double cx = cos(euler[0]); double sx = sin(euler[0]); double cy = cos(euler[1]); double sy = sin(euler[1]); double cz = cos(euler[2]); double sz = sin(euler[2]); return Matrix4::byColumns( cy*cz + sx*sy*sz, cx*sz, sx*cy*sz - sy*cz, 0, sx*sy*cz - cy*sz, cx*cz, sy*sz + sx*cy*cz, 0, cx*sy, -sx, cx*cy, 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotationForEulerZXYDegrees(const Vector3& euler) { return getRotationForEulerZXY(euler_degrees_to_radians(euler)); } Matrix4 Matrix4::getRotationForEulerZYX(const Vector3& euler) { double cx = cos(euler[0]); double sx = sin(euler[0]); double cy = cos(euler[1]); double sy = sin(euler[1]); double cz = cos(euler[2]); double sz = sin(euler[2]); return Matrix4::byColumns( cy*cz, cx*sz + sx*sy*cz, sx*sz - cx*sy*cz, 0, -cy*sz, cx*cz - sx*sy*sz, sx*cz + cx*sy*sz, 0, sy, -sx*cy, cx*cy, 0, 0, 0, 0, 1 ); } Matrix4 Matrix4::getRotationForEulerZYXDegrees(const Vector3& euler) { return getRotationForEulerZYX(euler_degrees_to_radians(euler)); } // Get a scale matrix Matrix4 Matrix4::getScale(const Vector3& scale) { return Matrix4::byColumns( scale[0], 0, 0, 0, 0, scale[1], 0, 0, 0, 0, scale[2], 0, 0, 0, 0, 1 ); } // Transpose the matrix in-place void Matrix4::transpose() { std::swap(_m[1], _m[4]); // xy <=> yx std::swap(_m[2], _m[8]); // xz <=> zx std::swap(_m[3], _m[12]); // xw <=> tx std::swap(_m[6], _m[9]); // yz <=> zy std::swap(_m[7], _m[13]); // yw <=> ty std::swap(_m[11], _m[14]); // zw <=> tz } // Return transposed copy Matrix4 Matrix4::getTransposed() const { return Matrix4( xx(), yx(), zx(), tx(), xy(), yy(), zy(), ty(), xz(), yz(), zz(), tz(), xw(), yw(), zw(), tw() ); } // Return affine inverse Matrix4 Matrix4::getInverse() const { Matrix4 result; // determinant of rotation submatrix double det = _m[0] * ( _m[5]*_m[10] - _m[9]*_m[6] ) - _m[1] * ( _m[4]*_m[10] - _m[8]*_m[6] ) + _m[2] * ( _m[4]*_m[9] - _m[8]*_m[5] ); // throw exception here if (det*det < 1e-25) // invert rotation submatrix det = 1.0f / det; result[0] = ( (_m[5]*_m[10]- _m[6]*_m[9] )*det); result[1] = (- (_m[1]*_m[10]- _m[2]*_m[9] )*det); result[2] = ( (_m[1]*_m[6] - _m[2]*_m[5] )*det); result[3] = 0; result[4] = (- (_m[4]*_m[10]- _m[6]*_m[8] )*det); result[5] = ( (_m[0]*_m[10]- _m[2]*_m[8] )*det); result[6] = (- (_m[0]*_m[6] - _m[2]*_m[4] )*det); result[7] = 0; result[8] = ( (_m[4]*_m[9] - _m[5]*_m[8] )*det); result[9] = (- (_m[0]*_m[9] - _m[1]*_m[8] )*det); result[10]= ( (_m[0]*_m[5] - _m[1]*_m[4] )*det); result[11] = 0; // multiply translation part by rotation result[12] = - (_m[12] * result[0] + _m[13] * result[4] + _m[14] * result[8]); result[13] = - (_m[12] * result[1] + _m[13] * result[5] + _m[14] * result[9]); result[14] = - (_m[12] * result[2] + _m[13] * result[6] + _m[14] * result[10]); result[15] = 1; return result; } Matrix4 Matrix4::getFullInverse() const { // The inverse is generated through the adjugate matrix // 2x2 minors (re-usable for the determinant) double minor01 = zz() * tw() - zw() * tz(); double minor02 = zy() * tw() - zw() * ty(); double minor03 = zx() * tw() - zw() * tx(); double minor04 = zy() * tz() - zz() * ty(); double minor05 = zx() * tz() - zz() * tx(); double minor06 = zx() * ty() - zy() * tx(); // 2x2 minors (not usable for the determinant) double minor07 = yz() * tw() - yw() * tz(); double minor08 = yy() * tw() - yw() * ty(); double minor09 = yy() * tz() - yz() * ty(); double minor10 = yx() * tw() - yw() * tx(); double minor11 = yx() * tz() - yz() * tx(); double minor12 = yx() * ty() - yy() * tx(); double minor13 = yz() * zw() - yw() * zz(); double minor14 = yy() * zw() - yw() * zy(); double minor15 = yy() * zz() - yz() * zy(); double minor16 = yx() * zw() - yw() * zx(); double minor17 = yx() * zz() - yz() * zx(); double minor18 = yx() * zy() - yy() * zx(); // 3x3 minors (re-usable for the determinant) double minor3x3_11 = yy() * minor01 - yz() * minor02 + yw() * minor04; double minor3x3_21 = yx() * minor01 - yz() * minor03 + yw() * minor05; double minor3x3_31 = yx() * minor02 - yy() * minor03 + yw() * minor06; double minor3x3_41 = yx() * minor04 - yy() * minor05 + yz() * minor06; // 3x3 minors (not usable for the determinant) double minor3x3_12 = xy() * minor01 - xz() * minor02 + xw() * minor04; double minor3x3_22 = xx() * minor01 - xz() * minor03 + xw() * minor05; double minor3x3_32 = xx() * minor02 - xy() * minor03 + xw() * minor06; double minor3x3_42 = xx() * minor04 - xy() * minor05 + xz() * minor06; double minor3x3_13 = xy() * minor07 - xz() * minor08 + xw() * minor09; double minor3x3_23 = xx() * minor07 - xz() * minor10 + xw() * minor11; double minor3x3_33 = xx() * minor08 - xy() * minor10 + xw() * minor12; double minor3x3_43 = xx() * minor09 - xy() * minor11 + xz() * minor12; double minor3x3_14 = xy() * minor13 - xz() * minor14 + xw() * minor15; double minor3x3_24 = xx() * minor13 - xz() * minor16 + xw() * minor17; double minor3x3_34 = xx() * minor14 - xy() * minor16 + xw() * minor18; double minor3x3_44 = xx() * minor15 - xy() * minor17 + xz() * minor18; double determinant = xx() * minor3x3_11 - xy() * minor3x3_21 + xz() * minor3x3_31 - xw() * minor3x3_41; double invDet = 1.0f / determinant; return Matrix4::byColumns( +minor3x3_11 * invDet, -minor3x3_12 * invDet, +minor3x3_13 * invDet, -minor3x3_14 * invDet, -minor3x3_21 * invDet, +minor3x3_22 * invDet, -minor3x3_23 * invDet, +minor3x3_24 * invDet, +minor3x3_31 * invDet, -minor3x3_32 * invDet, +minor3x3_33 * invDet, -minor3x3_34 * invDet, -minor3x3_41 * invDet, +minor3x3_42 * invDet, -minor3x3_43 * invDet, +minor3x3_44 * invDet ); } // Multiply by another matrix, in-place void Matrix4::multiplyBy(const Matrix4& other) { *this = getMultipliedBy(other); } // Add a translation component void Matrix4::translateBy(const Vector3& translation) { multiplyBy(getTranslation(translation)); } // Add a scale component void Matrix4::scaleBy(const Vector3& scale) { multiplyBy(getScale(scale)); } namespace { class Vector4ClipLT { public: static bool compare(const Vector4& self, std::size_t index) { return self[index] < self[3]; } static double scale(const Vector4& self, const Vector4& other, std::size_t index) { return (self[index] - self[3]) / (other[3] - other[index]); } }; class Vector4ClipGT { public: static bool compare(const Vector4& self, std::size_t index) { return self[index] > -self[3]; } static double scale(const Vector4& self, const Vector4& other, std::size_t index) { return (self[index] + self[3]) / (-other[3] - other[index]); } }; template class Vector4ClipPolygon { public: typedef Vector4* iterator; typedef const Vector4* const_iterator; static std::size_t apply(const_iterator first, const_iterator last, iterator out, std::size_t index) { const_iterator next = first, i = last - 1; iterator tmp(out); bool b0 = ClipPlane::compare(*i, index); while(next != last) { bool b1 = ClipPlane::compare(*next, index); if(b0 ^ b1) { *out = *next - *i; double scale = ClipPlane::scale(*i, *out, index); (*out)[0] = (*i)[0] + scale*((*out)[0]); (*out)[1] = (*i)[1] + scale*((*out)[1]); (*out)[2] = (*i)[2] + scale*((*out)[2]); (*out)[3] = (*i)[3] + scale*((*out)[3]); ++out; } if(b1) { *out = *next; ++out; } i = next; ++next; b0 = b1; } return out - tmp; } }; #define CLIP_X_LT_W(p) (Vector4ClipLT::compare(p, 0)) #define CLIP_X_GT_W(p) (Vector4ClipGT::compare(p, 0)) #define CLIP_Y_LT_W(p) (Vector4ClipLT::compare(p, 1)) #define CLIP_Y_GT_W(p) (Vector4ClipGT::compare(p, 1)) #define CLIP_Z_LT_W(p) (Vector4ClipLT::compare(p, 2)) #define CLIP_Z_GT_W(p) (Vector4ClipGT::compare(p, 2)) inline ClipResult homogenous_clip_point(const Vector4& clipped) { ClipResult result = c_CLIP_FAIL; if (CLIP_X_LT_W(clipped)) result &= ~c_CLIP_LT_X; // X < W if (CLIP_X_GT_W(clipped)) result &= ~c_CLIP_GT_X; // X > -W if (CLIP_Y_LT_W(clipped)) result &= ~c_CLIP_LT_Y; // Y < W if (CLIP_Y_GT_W(clipped)) result &= ~c_CLIP_GT_Y; // Y > -W if (CLIP_Z_LT_W(clipped)) result &= ~c_CLIP_LT_Z; // Z < W if (CLIP_Z_GT_W(clipped)) result &= ~c_CLIP_GT_Z; // Z > -W return result; } inline std::size_t homogenous_clip_line(Vector4 clipped[2]) { const Vector4& p0 = clipped[0]; const Vector4& p1 = clipped[1]; // early out { ClipResult mask0 = homogenous_clip_point(clipped[0]); ClipResult mask1 = homogenous_clip_point(clipped[1]); if ((mask0 | mask1) == c_CLIP_PASS) // both points passed all planes { return 2; } if (mask0 & mask1) // both points failed any one plane { return 0; } } { const bool index = CLIP_X_LT_W(p0); if (index ^ CLIP_X_LT_W(p1)) { Vector4 clip(p1 - p0); double scale = (p0[0] - p0[3]) / (clip[3] - clip[0]); clip[0] = p0[0] + scale * clip[0]; clip[1] = p0[1] + scale * clip[1]; clip[2] = p0[2] + scale * clip[2]; clip[3] = p0[3] + scale * clip[3]; clipped[index] = clip; } else if(index == 0) { return 0; } } { const bool index = CLIP_X_GT_W(p0); if (index ^ CLIP_X_GT_W(p1)) { Vector4 clip(p1 - p0); double scale = (p0[0] + p0[3]) / (-clip[3] - clip[0]); clip[0] = p0[0] + scale * clip[0]; clip[1] = p0[1] + scale * clip[1]; clip[2] = p0[2] + scale * clip[2]; clip[3] = p0[3] + scale * clip[3]; clipped[index] = clip; } else if(index == 0) { return 0; } } { const bool index = CLIP_Y_LT_W(p0); if (index ^ CLIP_Y_LT_W(p1)) { Vector4 clip(p1 - p0); double scale = (p0[1] - p0[3]) / (clip[3] - clip[1]); clip[0] = p0[0] + scale * clip[0]; clip[1] = p0[1] + scale * clip[1]; clip[2] = p0[2] + scale * clip[2]; clip[3] = p0[3] + scale * clip[3]; clipped[index] = clip; } else if (index == 0) { return 0; } } { const bool index = CLIP_Y_GT_W(p0); if (index ^ CLIP_Y_GT_W(p1)) { Vector4 clip(p1 - p0); double scale = (p0[1] + p0[3]) / (-clip[3] - clip[1]); clip[0] = p0[0] + scale * clip[0]; clip[1] = p0[1] + scale * clip[1]; clip[2] = p0[2] + scale * clip[2]; clip[3] = p0[3] + scale * clip[3]; clipped[index] = clip; } else if (index == 0) { return 0; } } { const bool index = CLIP_Z_LT_W(p0); if (index ^ CLIP_Z_LT_W(p1)) { Vector4 clip(p1 - p0); double scale = (p0[2] - p0[3]) / (clip[3] - clip[2]); clip[0] = p0[0] + scale * clip[0]; clip[1] = p0[1] + scale * clip[1]; clip[2] = p0[2] + scale * clip[2]; clip[3] = p0[3] + scale * clip[3]; clipped[index] = clip; } else if (index == 0) { return 0; } } { const bool index = CLIP_Z_GT_W(p0); if (index ^ CLIP_Z_GT_W(p1)) { Vector4 clip(p1 - p0); double scale = (p0[2] + p0[3]) / (-clip[3] - clip[2]); clip[0] = p0[0] + scale * clip[0]; clip[1] = p0[1] + scale * clip[1]; clip[2] = p0[2] + scale * clip[2]; clip[3] = p0[3] + scale * clip[3]; clipped[index] = clip; } else if (index == 0) { return 0; } } return 2; } inline std::size_t homogenous_clip_triangle(Vector4 clipped[9]) { Vector4 buffer[9]; std::size_t count = 3; count = Vector4ClipPolygon< Vector4ClipLT >::apply(clipped, clipped + count, buffer, 0); count = Vector4ClipPolygon< Vector4ClipGT >::apply(buffer, buffer + count, clipped, 0); count = Vector4ClipPolygon< Vector4ClipLT >::apply(clipped, clipped + count, buffer, 1); count = Vector4ClipPolygon< Vector4ClipGT >::apply(buffer, buffer + count, clipped, 1); count = Vector4ClipPolygon< Vector4ClipLT >::apply(clipped, clipped + count, buffer, 2); return Vector4ClipPolygon< Vector4ClipGT >::apply(buffer, buffer + count, clipped, 2); } } // namespace ClipResult Matrix4::clipPoint(const Vector3& point, Vector4& clipped) const { clipped[0] = point[0]; clipped[1] = point[1]; clipped[2] = point[2]; clipped[3] = 1; clipped = transform(clipped); return homogenous_clip_point(clipped); } std::size_t Matrix4::clipTriangle(const Vector3& p0, const Vector3& p1, const Vector3& p2, Vector4 clipped[9]) const { clipped[0][0] = p0[0]; clipped[0][1] = p0[1]; clipped[0][2] = p0[2]; clipped[0][3] = 1; clipped[1][0] = p1[0]; clipped[1][1] = p1[1]; clipped[1][2] = p1[2]; clipped[1][3] = 1; clipped[2][0] = p2[0]; clipped[2][1] = p2[1]; clipped[2][2] = p2[2]; clipped[2][3] = 1; clipped[0] = transform(clipped[0]); clipped[1] = transform(clipped[1]); clipped[2] = transform(clipped[2]); return homogenous_clip_triangle(clipped); } std::size_t Matrix4::clipLine(const Vector3& p0, const Vector3& p1, Vector4 clipped[2]) const { clipped[0][0] = p0[0]; clipped[0][1] = p0[1]; clipped[0][2] = p0[2]; clipped[0][3] = 1; clipped[1][0] = p1[0]; clipped[1][1] = p1[1]; clipped[1][2] = p1[2]; clipped[1][3] = 1; clipped[0] = transform(clipped[0]); clipped[1] = transform(clipped[1]); return homogenous_clip_line(clipped); } DarkRadiant-2.5.0/libs/math/Matrix4.h000066400000000000000000001116761321750546400173100ustar00rootroot00000000000000#pragma once /// \file /// \brief Matrix data types and related operations. #include "math/Vector3.h" #include "math/Vector4.h" #include "math/pi.h" class Quaternion; typedef unsigned char ClipResult; const ClipResult c_CLIP_PASS = 0x00; // 000000 const ClipResult c_CLIP_LT_X = 0x01; // 000001 const ClipResult c_CLIP_GT_X = 0x02; // 000010 const ClipResult c_CLIP_LT_Y = 0x04; // 000100 const ClipResult c_CLIP_GT_Y = 0x08; // 001000 const ClipResult c_CLIP_LT_Z = 0x10; // 010000 const ClipResult c_CLIP_GT_Z = 0x20; // 100000 const ClipResult c_CLIP_FAIL = 0x3F; // 111111 /** * A 4x4 matrix stored in double-precision floating-point. * * The elements of this matrix are stored columnwise in memory: * * | 0 4 8 12 | * | 1 5 9 13 | * | 2 6 10 14 | * | 3 7 11 15 | * * or, alternatively, as the 4 columns are regarded as 4 vectors named x, y, z, t: * * | xx yx zx tx | * | xy yy zy ty | * | xz yz zz tz | * | xw yw zw tw | */ class Matrix4 { // Elements of the 4x4 matrix. These appear to be treated COLUMNWISE, i.e. // elements [0] through [3] are the first column, [4] through [7] are the // second column, etc. double _m[16]; private: // Initialising constructor, elements are passed in column-wise order Matrix4(double xx_, double xy_, double xz_, double xw_, double yx_, double yy_, double yz_, double yw_, double zx_, double zy_, double zz_, double zw_, double tx_, double ty_, double tz_, double tw_); public: /// Construct a matrix with uninitialised values. Matrix4() { } /* NAMED CONSTRUCTORS FOR SPECIFIC MATRICES */ /** * \brief * Obtain the identity matrix. */ static const Matrix4& getIdentity(); /** * \brief * Get a matrix representing the given 3D translation. * * @param translation * Vector3 representing the translation in 3D space. */ static Matrix4 getTranslation(const Vector3& translation); /** * greebo: Attempts to parse the rotation from the given string, which is * a whitespace-separated chain of nine floating point values, as used * in entity spawnargs. * * Example: "0 1 0 -1 0 0 0 0 1" * * Returns: the parsed (translation-free) matrix. In case of parser errors * the identity matrix is returned. */ static Matrix4 getRotation(const std::string& rotationString); /** * greebo: Returns the rotation matrix defined by two three-component * vectors. * The rotational axis is defined by the normalised cross product of those * two vectors, the angle can be retrieved from the dot product. */ static Matrix4 getRotation(const Vector3& a, const Vector3& b); /** * greebo: Returns the rotation matrix defined by an arbitrary axis * and an angle. * * Important: the axis vector must be normalised. */ static Matrix4 getRotation(const Vector3& axis, const double angle); /** * Constructs a pure-rotation matrix from the given quaternion. */ static Matrix4 getRotation(const Quaternion& quaternion); /** * Constructs a pure-rotation matrix from the given quaternion, quantised. */ static Matrix4 getRotationQuantised(const Quaternion& quaternion); /** * Constructs a pure-rotation matrix about the x axis from sin and cosine of an angle. */ static Matrix4 getRotationAboutXForSinCos(double s, double c); /** * Constructs a pure-rotation matrix about the x axis from an angle in radians */ static Matrix4 getRotationAboutX(double angle); /** * Constructs a pure-rotation matrix about the x axis from an angle in degrees. */ static Matrix4 getRotationAboutXDegrees(double angle); /** * Constructs a pure-rotation matrix about the y axis from sin and cosine of an angle. */ static Matrix4 getRotationAboutYForSinCos(double s, double c); /** * Constructs a pure-rotation matrix about the y axis from an angle in radians */ static Matrix4 getRotationAboutY(double angle); /** * Constructs a pure-rotation matrix about the y axis from an angle in degrees. */ static Matrix4 getRotationAboutYDegrees(double angle); /** * Constructs a pure-rotation matrix about the z axis from sin and cosine of an angle. */ static Matrix4 getRotationAboutZForSinCos(double s, double c); /** * Constructs a pure-rotation matrix about the z axis from an angle in radians */ static Matrix4 getRotationAboutZ(double angle); /** * Constructs a pure-rotation matrix about the z axis from an angle in degrees. */ static Matrix4 getRotationAboutZDegrees(double angle); /** * Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (x, y, z). */ static Matrix4 getRotationForEulerXYZ(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (x, y, z). */ static Matrix4 getRotationForEulerXYZDegrees(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (y, z, x). */ static Matrix4 getRotationForEulerYZX(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (y, z, x). */ static Matrix4 getRotationForEulerYZXDegrees(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (x, z, y). */ static Matrix4 getRotationForEulerXZY(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (x, z, y). */ static Matrix4 getRotationForEulerXZYDegrees(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (y, x, z). */ static Matrix4 getRotationForEulerYXZ(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (y, x, z). */ static Matrix4 getRotationForEulerYXZDegrees(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (z, x, y). */ static Matrix4 getRotationForEulerZXY(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (z, x, y). */ static Matrix4 getRotationForEulerZXYDegrees(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (z, y, x). */ static Matrix4 getRotationForEulerZYX(const Vector3& euler); /** * Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (z, y, x). */ static Matrix4 getRotationForEulerZYXDegrees(const Vector3& euler); /** * \brief * Get a matrix representing the given scale in 3D space. * * \param scale * Vector3 representing the scale. */ static Matrix4 getScale(const Vector3& scale); /** * Returns a perspective projection matrix for the six given frustum planes. The result is the projection * matrix as constructed by openGL when calling the glFrustum() function. */ static Matrix4 getProjectionForFrustum(double left, double right, double bottom, double top, double nearval, double farval); /** * \brief * Construct a matrix containing the given elements. * * The elements are specified column-wise, starting with the left-most * column. */ static Matrix4 byColumns(double xx, double xy, double xz, double xw, double yx, double yy, double yz, double yw, double zx, double zy, double zz, double zw, double tx, double ty, double tz, double tw); /** * \brief * Construct a matrix containing the given elements. * * The elements are specified row-wise, starting with the top row. */ static Matrix4 byRows(double xx, double yx, double zx, double tx, double xy, double yy, double zy, double ty, double xz, double yz, double zz, double tz, double xw, double yw, double zw, double tw); enum Handedness { RIGHTHANDED = 0, LEFTHANDED = 1, }; /** * Return matrix elements * \{ */ double& xx() { return _m[0]; } const double& xx() const { return _m[0]; } double& xy() { return _m[1]; } const double& xy() const { return _m[1]; } double& xz() { return _m[2]; } const double& xz() const { return _m[2]; } double& xw() { return _m[3]; } const double& xw() const { return _m[3]; } double& yx() { return _m[4]; } const double& yx() const { return _m[4]; } double& yy() { return _m[5]; } const double& yy() const { return _m[5]; } double& yz() { return _m[6]; } const double& yz() const { return _m[6]; } double& yw() { return _m[7]; } const double& yw() const { return _m[7]; } double& zx() { return _m[8]; } const double& zx() const { return _m[8]; } double& zy() { return _m[9]; } const double& zy() const { return _m[9]; } double& zz() { return _m[10]; } const double& zz() const { return _m[10]; } double& zw() { return _m[11]; } const double& zw() const { return _m[11]; } double& tx() { return _m[12]; } const double& tx() const { return _m[12]; } double& ty() { return _m[13]; } const double& ty() const { return _m[13]; } double& tz() { return _m[14]; } const double& tz() const { return _m[14]; } double& tw() { return _m[15]; } const double& tw() const { return _m[15]; } /** * \} */ /** * Return columns of the matrix as vectors. * \{ */ Vector4& x() { return reinterpret_cast(xx()); } const Vector4& x() const { return reinterpret_cast(xx()); } Vector4& y() { return reinterpret_cast(yx()); } const Vector4& y() const { return reinterpret_cast(yx()); } Vector4& z() { return reinterpret_cast(zx()); } const Vector4& z() const { return reinterpret_cast(zx()); } Vector4& t() { return reinterpret_cast(tx()); } const Vector4& t() const { return reinterpret_cast(tx()); } /** * \} */ /** * Cast to double* for use with GL functions that accept a double * array, also provides operator[]. */ operator double* () { return _m; } /** * Cast to const double* to provide operator[] for const objects. */ operator const double* () const { return _m; } /** * \brief * Transpose this matrix in-place. */ void transpose(); /** * \brief * Return a transposed copy of this matrix. */ Matrix4 getTransposed() const; /** * \brief * Return the affine inverse of this transformation matrix. */ Matrix4 getInverse() const; /** * Affine invert this matrix in-place. */ void invert(); /** * \brief * Return the full inverse of this matrix. */ Matrix4 getFullInverse() const; /** * Invert this matrix in-place. */ void invertFull(); /** * Returns the given 3-component point transformed by this matrix. */ template BasicVector3 transformPoint(const BasicVector3& point) const; /** * Returns the given 3-component direction transformed by this matrix. * The given vector is treated as direction so it won't receive a translation, just like * a 4-component vector with its w-component set to 0 would be transformed. */ template BasicVector3 transformDirection(const BasicVector3& direction) const; /** * \brief * Use this matrix to transform the provided vector and return a new vector * containing the result. * * \param vector4 * The 4-element vector to transform. */ template BasicVector4 transform(const BasicVector4& vector4) const; /** * \brief * Return the result of this matrix post-multiplied by another matrix. */ Matrix4 getMultipliedBy(const Matrix4& other) const; /** * \brief * Post-multiply this matrix by another matrix, in-place. */ void multiplyBy(const Matrix4& other); /** * Returns this matrix pre-multiplied by the other */ Matrix4 getPremultipliedBy(const Matrix4& other) const; /** * Pre-multiplies this matrix by other in-place. */ void premultiplyBy(const Matrix4& other); /** * \brief * Add a translation component to the transformation represented by this * matrix. * * Equivalent to multiplyBy(Matrix4::getTranslation(translation)); */ void translateBy(const Vector3& translation); /** * \brief * Add a translation component to the transformation represented by this * matrix. * * Equivalent to getMultipliedBy(Matrix4::getTranslation(translation)); */ Matrix4 getTranslatedBy(const Vector3& translation) const; /** * Returns this matrix concatenated with the rotation transform produced by the given quat. * The concatenated rotation occurs before the transformation of this matrix. * * Equivalent to getMultipliedBy(getRotation(rotation)); */ Matrix4 getRotatedBy(const Quaternion& rotation) const; /** * Concatenates this matrix with the rotation transform produced by the given quat. * The concatenated rotation occurs before the transformation of this matrix. */ void rotateBy(const Quaternion& rotation); /** * Concatenates this matrix with the pivoted rotation transform produced by the given quat. * The concatenated rotation occurs before the transformation of this matrix. */ void rotateBy(const Quaternion& rotation, const Vector3& pivot); /** * \brief * Add a scale component to the transformation represented by this matrix. * * Equivalent to multiplyBy(Matrix4::getScale(scale)); */ void scaleBy(const Vector3& scale); /** * \brief * Add a pivoted scale transformation to this matrix. */ void scaleBy(const Vector3& scale, const Vector3& pivot); /** * Equality operator, Returns true if this and the other are exactly element-wise equal. */ bool operator==(const Matrix4& other) const; /** * Inequality operator. */ bool operator!=(const Matrix4& other) const; /** * Returns true if self and other are element-wise equal within epsilon. */ bool isEqual(const Matrix4& other, double epsilon) const; /** * Returns true if this and the given matrix are exactly element-wise equal. * This and the other matrix must be affine. */ bool isAffineEqual(const Matrix4& other) const; /** * Returns RIGHTHANDED if this is right-handed, else returns LEFTHANDED. */ Handedness getHandedness() const; /** * Returns true if this matrix is affine. */ bool isAffine() const; /** * Returns this matrix post-multiplied by the other. * This and the other matrix must be affine. */ Matrix4 getAffineMultipliedBy(const Matrix4& other) const; /** * Post-multiplies this matrix by the other in-place. * This and the other matrix must be affine. */ void affineMultiplyBy(const Matrix4& other); /** * Returns this matrix pre-multiplied by the other. * This matrix and the other must be affine. */ Matrix4 getAffinePremultipliedBy(const Matrix4& other) const; /** * Pre-multiplies this matrix by the other in-place. * This and the other matrix must be affine. */ void affinePremultiplyBy(const Matrix4& other); /** * Returns the determinant of this 4x4 matrix */ double getDeterminant() const; /// Return the 3-element translation component of this matrix const Vector3& translation() const; /** * Concatenates this with the rotation transform produced * by euler angles (degrees) in the order (x, y, z). * The concatenated rotation occurs before self. */ void rotateByEulerXYZDegrees(const Vector3& euler); /** * Concatenates this with the pivoted rotation transform produced * by euler angles (degrees) in the order (x, y, z). * The concatenated rotation occurs before self. */ void rotateByEulerXYZDegrees(const Vector3& euler, const Vector3& pivot); /** * Returns this matrix concatenated with the rotation transform produced by the given * euler angles (degrees) in the order (y, x, z). The concatenated rotation occurs before this matrix. */ Matrix4 getRotatedByEulerYXZDegrees(const Vector3& euler) const; /** * Concatenates this with the rotation transform produced * by euler angles (degrees) in the order (y, x, z). * The concatenated rotation occurs before self. */ void rotateByEulerYXZDegrees(const Vector3& euler); /** * Returns this matrix concatenated with the rotation transform produced by the given * euler angles (degrees) in the order (z, x, y). The concatenated rotation occurs before this matrix. */ Matrix4 getRotatedByEulerZXYDegrees(const Vector3& euler) const; /** * Concatenates this with the rotation transform produced * by euler angles (degrees) in the order (z, x, y). * The concatenated rotation occurs before self. */ void rotateByEulerZXYDegrees(const Vector3& euler); /** * Calculates and returns a set of euler angles in radians that produce * the rotation component of this matrix when applied in the order (x, y, z). * This matrix must be affine and orthonormal (unscaled) to produce a meaningful result. */ Vector3 getEulerAnglesXYZ() const; /** * Calculates and returns a set of euler angles in degrees that produce * the rotation component of this matrix when applied in the order (x, y, z). * This matrix must be affine and orthonormal (unscaled) to produce a meaningful result. */ Vector3 getEulerAnglesXYZDegrees() const; /** * Calculates and returns a set of euler angles in radians that produce * the rotation component of this matrix when applied in the order (y, x, z). * This matrix must be affine and orthonormal (unscaled) to produce a meaningful result. */ Vector3 getEulerAnglesYXZ() const; /** * Calculates and returns a set of euler angles in degrees that produce * the rotation component of this matrix when applied in the order (y, x, z). * This matrix must be affine and orthonormal (unscaled) to produce a meaningful result. */ Vector3 getEulerAnglesYXZDegrees() const; /** * Calculates and returns a set of euler angles in radians that produce * the rotation component of this matrix when applied in the order (z, x, y). * This matrix must be affine and orthonormal (unscaled) to produce a meaningful result. */ Vector3 getEulerAnglesZXY() const; /** * Calculates and returns a set of euler angles in degrees that produce * the rotation component of this matrix when applied in the order (z, x, y). * This matrix must be affine and orthonormal (unscaled) to produce a meaningful result. */ Vector3 getEulerAnglesZXYDegrees() const; /** * Calculates and returns a set of euler angles in radians that produce * the rotation component of this matrix when applied in the order (z, y, x). * This matrix must be affine and orthonormal (unscaled) to produce a meaningful result. */ Vector3 getEulerAnglesZYX() const; /** * Calculates and returns a set of euler angles in degrees that produce * the rotation component of this matrix when applied in the order (z, y, x). * This matrix must be affine and orthonormal (unscaled) to produce a meaningful result. */ Vector3 getEulerAnglesZYXDegrees() const; /** * Calculates and returns the (x, y, z) scale values that produce the scale component of this matrix. * This matrix must be affine and orthogonal to produce a meaningful result. */ Vector3 getScale() const; /** * Transforms and clips the line formed by p0, p1 by this canonical matrix. * Stores the resulting line in clipped. * * @returns: the number of points in the resulting line. */ std::size_t clipLine(const Vector3& p0, const Vector3& p1, Vector4 clipped[2]) const; /** * Clips point by this canonical matrix and stores the result in clipped. * Returns a bitmask indicating which clip-planes the point was outside. */ ClipResult clipPoint(const Vector3& point, Vector4& clipped) const; /** * Transforms and clips the triangle formed by p0, p1, p2 by this canonical matrix. * Stores the resulting polygon in clipped. * Returns the number of points in the resulting polygon. */ std::size_t clipTriangle(const Vector3& p0, const Vector3& p1, const Vector3& p2, Vector4 clipped[9]) const; }; // ========================================================================================= // Inlined member definitions // ========================================================================================= // Construct a matrix with given column elements inline Matrix4 Matrix4::byColumns(double xx, double xy, double xz, double xw, double yx, double yy, double yz, double yw, double zx, double zy, double zz, double zw, double tx, double ty, double tz, double tw) { return Matrix4(xx, xy, xz, xw, yx, yy, yz, yw, zx, zy, zz, zw, tx, ty, tz, tw); } // Construct a matrix with given row elements inline Matrix4 Matrix4::byRows(double xx, double yx, double zx, double tx, double xy, double yy, double zy, double ty, double xz, double yz, double zz, double tz, double xw, double yw, double zw, double tw) { return Matrix4(xx, xy, xz, xw, yx, yy, yz, yw, zx, zy, zz, zw, tx, ty, tz, tw); } // Post-multiply this with other inline Matrix4 Matrix4::getMultipliedBy(const Matrix4& other) const { return Matrix4::byColumns( other[0] * _m[0] + other[1] * _m[4] + other[2] * _m[8] + other[3] * _m[12], other[0] * _m[1] + other[1] * _m[5] + other[2] * _m[9] + other[3] * _m[13], other[0] * _m[2] + other[1] * _m[6] + other[2] * _m[10]+ other[3] * _m[14], other[0] * _m[3] + other[1] * _m[7] + other[2] * _m[11]+ other[3] * _m[15], other[4] * _m[0] + other[5] * _m[4] + other[6] * _m[8] + other[7] * _m[12], other[4] * _m[1] + other[5] * _m[5] + other[6] * _m[9] + other[7] * _m[13], other[4] * _m[2] + other[5] * _m[6] + other[6] * _m[10]+ other[7] * _m[14], other[4] * _m[3] + other[5] * _m[7] + other[6] * _m[11]+ other[7] * _m[15], other[8] * _m[0] + other[9] * _m[4] + other[10]* _m[8] + other[11]* _m[12], other[8] * _m[1] + other[9] * _m[5] + other[10]* _m[9] + other[11]* _m[13], other[8] * _m[2] + other[9] * _m[6] + other[10]* _m[10]+ other[11]* _m[14], other[8] * _m[3] + other[9] * _m[7] + other[10]* _m[11]+ other[11]* _m[15], other[12]* _m[0] + other[13]* _m[4] + other[14]* _m[8] + other[15]* _m[12], other[12]* _m[1] + other[13]* _m[5] + other[14]* _m[9] + other[15]* _m[13], other[12]* _m[2] + other[13]* _m[6] + other[14]* _m[10]+ other[15]* _m[14], other[12]* _m[3] + other[13]* _m[7] + other[14]* _m[11]+ other[15]* _m[15] ); } inline Matrix4 Matrix4::getPremultipliedBy(const Matrix4& other) const { return other.getMultipliedBy(*this); } inline Matrix4 Matrix4::getRotationAboutX(double angle) { return getRotationAboutXForSinCos(sin(angle), cos(angle)); } inline Matrix4 Matrix4::getRotationAboutXDegrees(double angle) { return getRotationAboutX(degrees_to_radians(angle)); } inline Matrix4 Matrix4::getRotationAboutY(double angle) { return getRotationAboutYForSinCos(sin(angle), cos(angle)); } inline Matrix4 Matrix4::getRotationAboutYDegrees(double angle) { return getRotationAboutY(degrees_to_radians(angle)); } inline Matrix4 Matrix4::getRotationAboutZ(double angle) { return getRotationAboutZForSinCos(sin(angle), cos(angle)); } inline Matrix4 Matrix4::getRotationAboutZDegrees(double angle) { return getRotationAboutZ(degrees_to_radians(angle)); } inline Matrix4 Matrix4::getProjectionForFrustum(double left, double right, double bottom, double top, double nearval, double farval) { return Matrix4::byColumns( (2*nearval) / (right-left), 0, 0, 0, 0, (2*nearval) / (top-bottom), 0, 0, (right+left) / (right-left), (top+bottom) / (top-bottom), -(farval+nearval) / (farval-nearval), -1, 0, 0, -(2*farval*nearval) / (farval-nearval), 0 ); } inline bool Matrix4::operator==(const Matrix4& other) const { return xx() == other.xx() && xy() == other.xy() && xz() == other.xz() && xw() == other.xw() && yx() == other.yx() && yy() == other.yy() && yz() == other.yz() && yw() == other.yw() && zx() == other.zx() && zy() == other.zy() && zz() == other.zz() && zw() == other.zw() && tx() == other.tx() && ty() == other.ty() && tz() == other.tz() && tw() == other.tw(); } inline bool Matrix4::operator!=(const Matrix4& other) const { return !operator==(other); } inline bool Matrix4::isEqual(const Matrix4& other, double epsilon) const { return float_equal_epsilon(xx(), other.xx(), epsilon) && float_equal_epsilon(xy(), other.xy(), epsilon) && float_equal_epsilon(xz(), other.xz(), epsilon) && float_equal_epsilon(xw(), other.xw(), epsilon) && float_equal_epsilon(yx(), other.yx(), epsilon) && float_equal_epsilon(yy(), other.yy(), epsilon) && float_equal_epsilon(yz(), other.yz(), epsilon) && float_equal_epsilon(yw(), other.yw(), epsilon) && float_equal_epsilon(zx(), other.zx(), epsilon) && float_equal_epsilon(zy(), other.zy(), epsilon) && float_equal_epsilon(zz(), other.zz(), epsilon) && float_equal_epsilon(zw(), other.zw(), epsilon) && float_equal_epsilon(tx(), other.tx(), epsilon) && float_equal_epsilon(ty(), other.ty(), epsilon) && float_equal_epsilon(tz(), other.tz(), epsilon) && float_equal_epsilon(tw(), other.tw(), epsilon); } inline bool Matrix4::isAffineEqual(const Matrix4& other) const { return xx() == other.xx() && xy() == other.xy() && xz() == other.xz() && yx() == other.yx() && yy() == other.yy() && yz() == other.yz() && zx() == other.zx() && zy() == other.zy() && zz() == other.zz() && tx() == other.tx() && ty() == other.ty() && tz() == other.tz(); } inline Matrix4::Handedness Matrix4::getHandedness() const { return (x().getVector3().crossProduct(y().getVector3()).dot(z().getVector3()) < 0.0f) ? LEFTHANDED : RIGHTHANDED; } inline void Matrix4::premultiplyBy(const Matrix4& other) { *this = getPremultipliedBy(other); } inline bool Matrix4::isAffine() const { return xw() == 0 && yw() == 0 && zw() == 0 && tw() == 1; } inline Matrix4 Matrix4::getAffineMultipliedBy(const Matrix4& other) const { return Matrix4::byColumns( other.xx() * xx() + other.xy() * yx() + other.xz() * zx(), other.xx() * xy() + other.xy() * yy() + other.xz() * zy(), other.xx() * xz() + other.xy() * yz() + other.xz() * zz(), 0, other.yx() * xx() + other.yy() * yx() + other.yz() * zx(), other.yx() * xy() + other.yy() * yy() + other.yz() * zy(), other.yx() * xz() + other.yy() * yz() + other.yz() * zz(), 0, other.zx() * xx() + other.zy() * yx() + other.zz()* zx(), other.zx() * xy() + other.zy() * yy() + other.zz()* zy(), other.zx() * xz() + other.zy() * yz() + other.zz()* zz(), 0, other.tx()* xx() + other.ty()* yx() + other.tz()* zx() + tx(), other.tx()* xy() + other.ty()* yy() + other.tz()* zy() + ty(), other.tx()* xz() + other.ty()* yz() + other.tz()* zz()+ tz(), 1 ); } inline void Matrix4::affineMultiplyBy(const Matrix4& other) { *this = getAffineMultipliedBy(other); } inline Matrix4 Matrix4::getAffinePremultipliedBy(const Matrix4& other) const { return other.getAffineMultipliedBy(*this); } inline void Matrix4::affinePremultiplyBy(const Matrix4& other) { *this = getAffinePremultipliedBy(other); } template BasicVector3 Matrix4::transformPoint(const BasicVector3& point) const { return BasicVector3( static_cast(xx() * point[0] + yx() * point[1] + zx() * point[2] + tx()), static_cast(xy() * point[0] + yy() * point[1] + zy() * point[2] + ty()), static_cast(xz() * point[0] + yz() * point[1] + zz() * point[2] + tz()) ); } template BasicVector3 Matrix4::transformDirection(const BasicVector3& direction) const { return BasicVector3( static_cast(xx() * direction[0] + yx() * direction[1] + zx() * direction[2]), static_cast(xy() * direction[0] + yy() * direction[1] + zy() * direction[2]), static_cast(xz() * direction[0] + yz() * direction[1] + zz() * direction[2]) ); } template BasicVector4 Matrix4::transform(const BasicVector4& vector4) const { return BasicVector4( static_cast(_m[0] * vector4[0] + _m[4] * vector4[1] + _m[8] * vector4[2] + _m[12] * vector4[3]), static_cast(_m[1] * vector4[0] + _m[5] * vector4[1] + _m[9] * vector4[2] + _m[13] * vector4[3]), static_cast(_m[2] * vector4[0] + _m[6] * vector4[1] + _m[10] * vector4[2] + _m[14] * vector4[3]), static_cast(_m[3] * vector4[0] + _m[7] * vector4[1] + _m[11] * vector4[2] + _m[15] * vector4[3]) ); } inline void Matrix4::invert() { *this = getInverse(); } inline void Matrix4::invertFull() { *this = getFullInverse(); } inline double Matrix4::getDeterminant() const { // greebo: This is following Laplace's formula by expanding it along the first column // It needs a couple of 2x2 minors (which are re-used two times each) and four 3x3 minors // The expanded formula is like this: det A = a11*M11 - a21*M21 + a31*M31 + a41*M41 // where aij is a matrix element, and Mij is the minor leaving out the i-th row and j-th column // Six 2x2 minors, each is used two times double minor1 = zz() * tw() - zw() * tz(); double minor2 = zy() * tw() - zw() * ty(); double minor3 = zx() * tw() - zw() * tx(); double minor4 = zy() * tz() - zz() * ty(); double minor5 = zx() * tz() - zz() * tx(); double minor6 = zx() * ty() - zy() * tx(); // Four 3x3 minors double minor11 = yy() * minor1 - yz() * minor2 + yw() * minor4; double minor21 = yx() * minor1 - yz() * minor3 + yw() * minor5; double minor31 = yx() * minor2 - yy() * minor3 + yw() * minor6; double minor41 = yx() * minor4 - yy() * minor5 + yz() * minor6; // Assemble and return final determinant return xx() * minor11 - xy() * minor21 + xz() * minor31 - xw() * minor41; } inline const Vector3& Matrix4::translation() const { return t().getVector3(); } inline Matrix4 Matrix4::getTranslatedBy(const Vector3& translation) const { return getMultipliedBy(Matrix4::getTranslation(translation)); } inline Matrix4 Matrix4::getRotatedBy(const Quaternion& rotation) const { return getMultipliedBy(getRotation(rotation)); } inline void Matrix4::rotateBy(const Quaternion& rotation) { *this = getRotatedBy(rotation); } inline void Matrix4::rotateBy(const Quaternion& rotation, const Vector3& pivot) { translateBy(pivot); rotateBy(rotation); translateBy(-pivot); } inline void Matrix4::rotateByEulerXYZDegrees(const Vector3& euler) { multiplyBy(getRotationForEulerXYZDegrees(euler)); } inline void Matrix4::rotateByEulerXYZDegrees(const Vector3& euler, const Vector3& pivot) { translateBy(pivot); rotateByEulerXYZDegrees(euler); translateBy(-pivot); } inline Matrix4 Matrix4::getRotatedByEulerYXZDegrees(const Vector3& euler) const { return getMultipliedBy(getRotationForEulerYXZDegrees(euler)); } /// \brief Concatenates \p self with the rotation transform produced by \p euler angles (degrees) in the order (y, x, z). /// The concatenated rotation occurs before \p self. inline void Matrix4::rotateByEulerYXZDegrees(const Vector3& euler) { *this = getRotatedByEulerYXZDegrees(euler); } inline Matrix4 Matrix4::getRotatedByEulerZXYDegrees(const Vector3& euler) const { return getMultipliedBy(getRotationForEulerZXYDegrees(euler)); } inline void Matrix4::rotateByEulerZXYDegrees(const Vector3& euler) { *this = getRotatedByEulerZXYDegrees(euler); } inline Vector3 Matrix4::getEulerAnglesXYZ() const { double a = asin(-xz()); double ca = cos(a); if (fabs(ca) > 0.005f) // Gimbal lock? { return Vector3( atan2(yz() / ca, zz() / ca), a, atan2(xy() / ca, xx() / ca) ); } else // Gimbal lock has occurred { return Vector3( atan2(-zy(), yy()), a, 0 ); } } inline Vector3 Matrix4::getEulerAnglesXYZDegrees() const { Vector3 eulerRad = getEulerAnglesXYZ(); return Vector3(radians_to_degrees(eulerRad.x()), radians_to_degrees(eulerRad.y()), radians_to_degrees(eulerRad.z())); } inline Vector3 Matrix4::getEulerAnglesYXZ() const { double a = asin(yz()); double ca = cos(a); if (fabs(ca) > 0.005f) // Gimbal lock? { return Vector3( a, atan2(-xz() / ca, zz() / ca), atan2(-yx() / ca, yy() / ca) ); } else // Gimbal lock has occurred { return Vector3( a, atan2(zx(), xx()), 0 ); } } inline Vector3 Matrix4::getEulerAnglesYXZDegrees() const { Vector3 eulerRad = getEulerAnglesYXZ(); return Vector3(radians_to_degrees(eulerRad.x()), radians_to_degrees(eulerRad.y()), radians_to_degrees(eulerRad.z())); } inline Vector3 Matrix4::getEulerAnglesZXY() const { double a = asin(-zy()); double ca = cos(a); if (fabs(ca) > 0.005f) // Gimbal lock? { return Vector3( a, atan2(zx() / ca, zz() / ca), atan2(xy() / ca, yy()/ ca) ); } else // Gimbal lock has occurred { return Vector3( a, 0, atan2(-yx(), xx()) ); } } inline Vector3 Matrix4::getEulerAnglesZXYDegrees() const { Vector3 eulerRad = getEulerAnglesZXY(); return Vector3(radians_to_degrees(eulerRad.x()), radians_to_degrees(eulerRad.y()), radians_to_degrees(eulerRad.z())); } inline Vector3 Matrix4::getEulerAnglesZYX() const { double a = asin(zx()); double ca = cos(a); if (fabs(ca) > 0.005f) // Gimbal lock? { return Vector3( atan2(-zy() / ca, zz()/ ca), a, atan2(-yx() / ca, xx() / ca) ); } else // Gimbal lock has occurred { return Vector3( 0, a, atan2(xy(), yy()) ); } } inline Vector3 Matrix4::getEulerAnglesZYXDegrees() const { Vector3 eulerRad = getEulerAnglesZYX(); return Vector3(radians_to_degrees(eulerRad.x()), radians_to_degrees(eulerRad.y()), radians_to_degrees(eulerRad.z())); } inline Vector3 Matrix4::getScale() const { return Vector3( x().getVector3().getLength(), y().getVector3().getLength(), z().getVector3().getLength() ); } inline void Matrix4::scaleBy(const Vector3& scale, const Vector3& pivot) { translateBy(pivot); scaleBy(scale); translateBy(-pivot); } /** Stream insertion operator for Matrix4. */ inline std::ostream& operator<<(std::ostream& st, const Matrix4& m) { st << "|" << m[0] << ", " << m[4] << ", " << m[8] << ", " << m[12] << "|\n"; st << "|" << m[1] << ", " << m[5] << ", " << m[9] << ", " << m[13] << "|\n"; st << "|" << m[2] << ", " << m[6] << ", " << m[10] << ", " << m[14] << "|\n"; st << "|" << m[3] << ", " << m[7] << ", " << m[11] << ", " << m[15] << "|\n"; return st; } DarkRadiant-2.5.0/libs/math/Plane3.cpp000066400000000000000000000022351321750546400174230ustar00rootroot00000000000000#include "Plane3.h" #include "AABB.h" #include "Matrix4.h" double Plane3::distanceToOrientedExtents(const Vector3& extents, const Matrix4& orientation) const { return fabs(extents[0] * normal().dot(orientation.x().getVector3())) + fabs(extents[1] * normal().dot(orientation.y().getVector3())) + fabs(extents[2] * normal().dot(orientation.z().getVector3())); } bool Plane3::containsAABB(const AABB& aabb, const Matrix4& orientation) const { double dot = distanceToPoint(aabb.origin); return !(dot > 0 || -dot < distanceToOrientedExtents(aabb.extents, orientation)); } void Plane3::translate(const Vector3& translation) { _dist = _dist - ( translation.x() * _normal.x() + translation.y() * _normal.y() + translation.z() * _normal.z()); } Plane3& Plane3::transform(const Matrix4& m) { _normal = m.transformDirection(_normal); _dist = _normal.x() * (_dist * _normal.x() - m.tx()) + _normal.y() * (_dist * _normal.y() - m.ty()) + _normal.z() * (_dist * _normal.z() - m.tz()); return *this; } Plane3 Plane3::transformed(const Matrix4& m) const { return Plane3(*this).transform(m); } DarkRadiant-2.5.0/libs/math/Plane3.h000066400000000000000000000176171321750546400171020ustar00rootroot00000000000000#pragma once /* greebo: A plane in 3D space can be represented by a point and a normal vector. * * It is sufficient to specify four numbers to fully describe the plane: the three * components of the normal vector (x,y,z) and the dot product of the normal and any point of this plane. * * There are several constructors available: one requires all four number be passed directly, * the second requires the normal vector and the distance to be passed, the third and fourth * requires a set of three points that define the plane. * * Note: the plane numbers are stored in double precision. * Note: the constructor requiring three points does NOT check if two or more points are equal. * Note: two planes are considered equal when the difference of their normals and distances are below an epsilon. * * Note about the stored values: in contrast to the Doom 3 Engine sources where the idPlane object * stores 4 floating point values a, b, c, d as in the plane equation a*x + b*y + c*z + d = 0, * DarkRadiant's Plane3 class stores the normal vector and the actual distance value, which is -d. * The Plane3::dist() method returns the same value as idPlane3::Dist(), just beware when using the dist * member variable in any of Plane3 backends. */ #include "FloatTools.h" #include "Vector3.h" class Matrix4; class AABB; namespace { // Some constants for "equality" check. const double EPSILON_NORMAL = 0.0001f; const double EPSILON_DIST = 0.02f; } class Plane3 { Vector3 _normal; // normal vector (a, b, c) double _dist; // distance (-d) public: // Constructor with no arguments Plane3() {} // Constructor which expects four numbers, the first three are the components of the normal vector. Plane3(double nx, double ny, double nz, double dist) : _normal(nx, ny, nz), _dist(dist) {} // Construct a plane from any BasicVector3 and the distance template Plane3(const BasicVector3& normal, double dist) : _normal(normal), _dist(dist) {} // Construct a plane from three points , and template Plane3(const BasicVector3& p0, const BasicVector3& p1, const BasicVector3& p2) : _normal((p1 - p0).crossProduct(p2 - p0).getNormalised()), _dist(p0.dot(_normal)) {} // Construct a plane from three points (same as above, just with an array as argument template Plane3(const BasicVector3 points[3]) : _normal((points[1] - points[0]).crossProduct(points[2] - points[0]).getNormalised()), _dist(points[0].dot(_normal)) {} // The negation operator for this plane - the normal vector components and the distance are negated Plane3 operator- () const { return Plane3(-_normal, -_dist); } /** * greebo: Note that planes are considered equal if their normal vectors and * distances don't differ more than an epsilon value. */ bool operator== (const Plane3& other) const { return _normal.isEqual(other._normal, EPSILON_NORMAL) && float_equal_epsilon(_dist, other._dist, EPSILON_DIST); } // Subtracts plane equations: the returned plane is (a1-a2)*x + (b1-b2)*y + (c1-c2)*z + (d1-d2) = 0 Plane3 operator-(const Plane3& other) const { return Plane3(_normal[0] - other._normal[0], _normal[1] - other._normal[1], _normal[2] - other._normal[2], _dist - other._dist); } // Returns the normal vector of this plane Vector3& normal() { return _normal; } const Vector3& normal() const { return _normal; } // Returns the distance of the plane (where the plane intersects the z-axis) double& dist() { return _dist; } const double& dist() const { return _dist; } /* greebo: This normalises the plane by turning the normal vector into a unit vector (dividing it by its length) * and scaling the distance down by the same amount */ Plane3 getNormalised() const { double rmagnitudeInv = 1 / _normal.getLength(); return Plane3(_normal * rmagnitudeInv, _dist * rmagnitudeInv); } // Normalises this Plane3 object in-place void normalise() { double rmagnitudeInv = 1 / _normal.getLength(); _normal *= rmagnitudeInv; _dist *= rmagnitudeInv; } /// Reverses this plane, by negating all components void reverse() { _normal = -_normal; _dist = -_dist; } /// Translate this plane, leaving the normal vector untouched void translate(const Vector3& translation); /** * \brief * Transform this plane by an arbitrary matrix * * \return * A reference to *this. */ Plane3& transform(const Matrix4& matrix); /// Return a copy of this plane transformed with the given matrix Plane3 transformed(const Matrix4& matrix) const; // Checks if the floats of this plane are valid, returns true if this is the case bool isValid() const { return float_equal_epsilon(_normal.dot(_normal), 1.0, 0.01); } /* greebo: Use this to calculate the projection of a onto this plane. * * @returns: the Vector3 pointing to the point on the plane with the shortest * distance from the passed */ Vector3 getProjection(const Vector3& pointToProject) const { // Get the normal vector of this plane and normalise it Vector3 n = _normal.getNormalised(); // Retrieve a point of the plane Vector3 planePoint = n*_dist; // Calculate the projection and return it return pointToProject + planePoint - n*pointToProject.dot(n); } /** greebo: Returns the signed distance to the given point. */ double distanceToPoint(const Vector3& point) const { return point.dot(_normal) - _dist; } /** * Returns true if the given point is on the negative side of this plane (which is the * opposite side as the plane's normal vector is). */ bool testPoint(const Vector3& point) const { return point.dot(_normal) + _dist <= 0; } /* greebo: This calculates the intersection point of three planes. * Returns <0,0,0> if no intersection point could be found, otherwise returns the coordinates of the intersection point * (this may also be 0,0,0) */ static Vector3 intersect(const Plane3& plane1, const Plane3& plane2, const Plane3& plane3) { const Vector3& n1 = plane1.normal(); const Vector3& n2 = plane2.normal(); const Vector3& n3 = plane3.normal(); Vector3 n1n2 = n1.crossProduct(n2); Vector3 n2n3 = n2.crossProduct(n3); Vector3 n3n1 = n3.crossProduct(n1); double denom = n1.dot(n2n3); // Check if the denominator is zero (which would mean that no intersection is to be found if (denom != 0) { return (n2n3*plane1.dist() + n3n1*plane2.dist() + n1n2*plane3.dist()) / denom; } else { // No intersection could be found, return <0,0,0> return Vector3(0,0,0); } } double distanceToOrientedExtents(const Vector3& extents, const Matrix4& orientation) const; /** * Return false if the given AABB with the given orientation is partially or completely outside this plane. */ bool containsAABB(const AABB& aabb, const Matrix4& orientation) const; }; // class Plane3 /** * \brief * Stream insertion operator for a plane. */ inline std::ostream& operator<< (std::ostream& os, const Plane3& plane) { os << "Plane3 { " << plane.normal().x() << "x + " << plane.normal().y() << "y + " << plane.normal().z() << "z = " << plane.dist() << " }"; return os; } DarkRadiant-2.5.0/libs/math/Quaternion.cpp000066400000000000000000000024471321750546400204330ustar00rootroot00000000000000#include "Quaternion.h" #include "Matrix4.h" Quaternion Quaternion::createForMatrix(const Matrix4& matrix4) { Matrix4 transposed = matrix4.getTransposed(); double trace = transposed[0] + transposed[5] + transposed[10] + 1.0; if (trace > 0.0001) { double S = 0.5 / sqrt(trace); return Quaternion( (transposed[9] - transposed[6]) * S, (transposed[2] - transposed[8]) * S, (transposed[4] - transposed[1]) * S, 0.25f / S ); } if (transposed[0] >= transposed[5] && transposed[0] >= transposed[10]) { double S = 2.0 * sqrt(1.0 + transposed[0] - transposed[5] - transposed[10]); return Quaternion( 0.25 / S, (transposed[1] + transposed[4]) / S, (transposed[2] + transposed[8]) / S, (transposed[6] + transposed[9]) / S ); } if (transposed[5] >= transposed[0] && transposed[5] >= transposed[10]) { double S = 2.0 * sqrt(1.0 + transposed[5] - transposed[0] - transposed[10]); return Quaternion( (transposed[1] + transposed[4]) / S, 0.25 / S, (transposed[6] + transposed[9]) / S, (transposed[2] + transposed[8]) / S ); } double S = 2.0 * sqrt(1.0 + transposed[10] - transposed[0] - transposed[5]); return Quaternion( (transposed[2] + transposed[8]) / S, (transposed[6] + transposed[9]) / S, 0.25 / S, (transposed[1] + transposed[4]) / S ); } DarkRadiant-2.5.0/libs/math/Quaternion.h000066400000000000000000000135631321750546400201010ustar00rootroot00000000000000#pragma once /// \file /// \brief Quaternion data types and related operations. #include "Vector3.h" #include "Vector4.h" // Forward declaration, include Matrix4.h for definition class Matrix4; /// \brief A quaternion stored in single-precision floating-point. class Quaternion : public Vector4 { public: Quaternion() : Vector4() {} Quaternion(const Vector4& vector) : Vector4(vector) {} // Construct a Quaternion out of the 4 arguments Quaternion(ElementType x_, ElementType y_, ElementType z_, ElementType w_) : Vector4(x_, y_, z_, w_) {} // Construct a Quaternion out of a Vector3 plus a fourth argument Quaternion(const Vector3& other, ElementType w_) : Vector4(other, w_) {} /** * Returns the identity quaternion (named constructor) */ static const Quaternion& Identity(); /** * Constructs a quaternion which rotates between two points on the unit-sphere (from and to). */ static Quaternion createForUnitVectors(const Vector3& from, const Vector3& to); /** * Constructs a rotation quaternion for the given euler angles. * Each component of the given vector refers to an angle (in degrees) * of one of the x-/y-/z-axis. */ static Quaternion createForEulerXYZDegrees(const Vector3& eulerXYZ); /** * Constructs a quat for the given axis and angle */ static Quaternion createForAxisAngle(const Vector3& axis, double angle); /** * Constructs a rotation quaternion about a given axis. */ static Quaternion createForX(double angle); static Quaternion createForY(double angle); static Quaternion createForZ(double angle); /** * Retrieves the quaternion from the given matrix. */ static Quaternion createForMatrix(const Matrix4& matrix4); /** * Returns this quaternion multiplied by the other one. */ Quaternion getMultipliedBy(const Quaternion& other) const; /** * Multiplies this quaternion by the other one in place. * Equivalent to: *this = getMultipliedBy(other); */ void multiplyBy(const Quaternion& other); /** * Multiplies this quaternion by the other one in place. * Equivalent to: *this = other.getMultipliedBy(*this); */ void preMultiplyBy(const Quaternion& other); /** * Returns the inverse of this quaternion. */ Quaternion getInverse() const; /** * Conjugates/inverts this quaternion in place. */ void conjugate(); /** * Returns a normalised copy of this quaternion. */ Quaternion getNormalised() const; /** * Normalise this quaternion in-place. */ void normalise(); /** * Returns the given point as transformed by this quaternion */ Vector3 transformPoint(const Vector3& point) const; }; inline const Quaternion& Quaternion::Identity() { static Quaternion _identity(0, 0, 0, 1); return _identity; } inline Quaternion Quaternion::createForUnitVectors(const Vector3& from, const Vector3& to) { return Quaternion(from.crossProduct(to), from.dot(to)); } inline Quaternion Quaternion::createForEulerXYZDegrees(const Vector3& eulerXYZ) { double cx = cos(degrees_to_radians(eulerXYZ[0] * 0.5f)); double sx = sin(degrees_to_radians(eulerXYZ[0] * 0.5f)); double cy = cos(degrees_to_radians(eulerXYZ[1] * 0.5f)); double sy = sin(degrees_to_radians(eulerXYZ[1] * 0.5f)); double cz = cos(degrees_to_radians(eulerXYZ[2] * 0.5f)); double sz = sin(degrees_to_radians(eulerXYZ[2] * 0.5f)); return Quaternion( cz * cy * sx - sz * sy * cx, cz * sy * cx + sz * cy * sx, sz * cy * cx - cz * sy * sx, cz * cy * cx + sz * sy * sx ); } inline Quaternion Quaternion::createForAxisAngle(const Vector3& axis, double angle) { angle *= 0.5f; double sa = sin(angle); return Quaternion(axis[0] * sa, axis[1] * sa, axis[2] * sa, cos(angle)); } inline Quaternion Quaternion::createForX(double angle) { angle *= 0.5f; return Quaternion(sin(angle), 0, 0, cos(angle)); } inline Quaternion Quaternion::createForY(double angle) { angle *= 0.5f; return Quaternion(0, sin(angle), 0, cos(angle)); } inline Quaternion Quaternion::createForZ(double angle) { angle *= 0.5f; return Quaternion(0, 0, sin(angle), cos(angle)); } inline Quaternion Quaternion::getMultipliedBy(const Quaternion& other) const { return Quaternion( w() * other.x() + x() * other.w() + y() * other.z() - z() * other.y(), w() * other.y() + y() * other.w() + z() * other.x() - x() * other.z(), w() * other.z() + z() * other.w() + x() * other.y() - y() * other.x(), w() * other.w() - x() * other.x() - y() * other.y() - z() * other.z() ); } inline void Quaternion::multiplyBy(const Quaternion& other) { *this = getMultipliedBy(other); } inline void Quaternion::preMultiplyBy(const Quaternion& other) { *this = other.getMultipliedBy(*this); } inline Quaternion Quaternion::getInverse() const { return Quaternion(-getVector3(), w()); } inline void Quaternion::conjugate() { *this = getInverse(); } inline Quaternion Quaternion::getNormalised() const { const double n = 1.0f / sqrt(x() * x() + y() * y() + z() * z() + w() * w()); return Quaternion(x() * n, y() * n, z() * n, w() * n); } inline void Quaternion::normalise() { *this = getNormalised(); } inline Vector3 Quaternion::transformPoint(const Vector3& point) const { double xx = x() * x(); double yy = y() * y(); double zz = z() * z(); double ww = w() * w(); double xy2 = x() * y() * 2; double xz2 = x() * z() * 2; double xw2 = x() * w() * 2; double yz2 = y() * z() * 2; double yw2 = y() * w() * 2; double zw2 = z() * w() * 2; return Vector3( ww * point.x() + yw2 * point.z() - zw2 * point.y() + xx * point.x() + xy2 * point.y() + xz2 * point.z() - zz * point.x() - yy * point.x(), xy2 * point.x() + yy * point.y() + yz2 * point.z() + zw2 * point.x() - zz * point.y() + ww * point.y() - xw2 * point.z() - xx * point.y(), xz2 * point.x() + yz2 * point.y() + zz * point.z() - yw2 * point.x() - yy * point.z() + xw2 * point.y() - xx * point.z() + ww * point.z() ); } const double c_half_sqrt2 = 0.70710678118654752440084436210485; const float c_half_sqrt2f = static_cast(c_half_sqrt2); DarkRadiant-2.5.0/libs/math/Ray.h000066400000000000000000000153221321750546400165020ustar00rootroot00000000000000#pragma once #include "Vector3.h" #include "Plane3.h" #include "Matrix4.h" #include "AABB.h" class Ray { public: Vector3 origin; Vector3 direction; Ray() {} Ray(const Vector3& origin_, const Vector3& direction_) : origin(origin_), direction(direction_) {} static Ray createForPoints(const Vector3& origin, const Vector3& p2) { return Ray(origin, (p2 - origin).getNormalised()); } /* greebo: this calculates the intersection point of two rays * (copied from Radiant's Intersection code, there may be better ways) */ Vector3 getIntersection(Ray& other) { Vector3 intersection = origin - other.origin; Vector3::ElementType dot = direction.dot(other.direction); Vector3::ElementType d = direction.dot(intersection); Vector3::ElementType e = other.direction.dot(intersection); Vector3::ElementType D = 1 - dot*dot; // always >= 0 if (D < 0.000001f) { // the lines are almost parallel return other.origin + other.direction*e; } else { return other.origin + other.direction*((e - dot*d) / D); } } void transform(const Matrix4& matrix) { origin = matrix.transformPoint(origin); direction = matrix.transformDirection(direction); } // closest-point-on-line Vector3::ElementType getSquaredDistance(const Vector3& point) const { return (point - (origin + direction * (point - origin).dot(direction))).getLengthSquared(); } Vector3::ElementType getDistance(const Plane3& plane) const { return -(plane.normal().dot(origin) - plane.dist()) / direction.dot(plane.normal()); } /** * Intersect this ray with the given bounding box. If no intersection occurs, this method * returns FALSE, in case of intersection (even if the ray starts within the AABB volume) * the method returns TRUE. The coord parameter will contain the intersection point in the * latter case (which will be the Ray's origin if it starts within the AABB to test). * Algorithm taken and adjusted from http://tog.acm.org/resources/GraphicsGems/gems/RayBox.c */ bool intersectAABB(const AABB& aabb, Vector3& intersection) const { if (!aabb.isValid()) return false; #define QUADRANT_RIGHT 0 #define QUADRANT_LEFT 1 #define QUADRANT_MIDDLE 2 bool inside = true; char quadrant[3]; Vector3::ElementType candidatePlane[3]; Vector3 aabbMin = aabb.getOrigin() - aabb.getExtents(); Vector3 aabbMax = aabb.getOrigin() + aabb.getExtents(); // Find candidate planes; this loop can be avoided if // rays cast all from the eye(assume perpsective view) for (int i = 0; i < 3; i++) { if (origin[i] < aabbMin[i]) { quadrant[i] = QUADRANT_LEFT; candidatePlane[i] = aabbMin[i]; inside = false; } else if (origin[i] > aabbMax[i]) { quadrant[i] = QUADRANT_RIGHT; candidatePlane[i] = aabbMax[i]; inside = false; } else { quadrant[i] = QUADRANT_MIDDLE; } } // Ray origin inside bounding box if (inside) { intersection = origin; return true; } Vector3::ElementType maxT[3]; // Calculate T distances to candidate planes for (int i = 0; i < 3; i++) { if (quadrant[i] != QUADRANT_MIDDLE && direction[i] != 0) { maxT[i] = (candidatePlane[i] - origin[i]) / direction[i]; } else { maxT[i] = -1; } } // Get largest of the maxT's for final choice of intersection int whichPlane = 0; for (int i = 1; i < 3; i++) { if (maxT[whichPlane] < maxT[i]) { whichPlane = i; } } // Check final candidate actually inside box if (maxT[whichPlane] < 0) return false; for (int i = 0; i < 3; i++) { if (whichPlane != i) { intersection[i] = origin[i] + maxT[whichPlane] * direction[i]; if (intersection[i] < aabbMin[i] || intersection[i] > aabbMax[i]) { return false; } } else { intersection[i] = candidatePlane[i]; } } return true; // ray hits box } // Return type for intersectTriangle() enum eTriangleIntersectionType { NO_INTERSECTION, POINT, COPLANAR, }; /** * Intersect this ray with the given triangle as represented by the 3 given points and * returns the found intersection type. Only in the case of eTriangleIntersectionType::POINT * the intersection argument will be filled with suitable coordinates, otherwise it's undefined. * * Note: degenerate triangles will return NO_INTERSECTION. * Taken and adjusted from http://geomalgorithms.com/a06-_intersect-2.html */ eTriangleIntersectionType intersectTriangle(const Vector3& p1, const Vector3& p2, const Vector3& p3, Vector3& intersection) const { // get triangle edge vectors and plane normal Vector3 u = p2 - p1; Vector3 v = p3 - p1; Vector3 n = u.crossProduct(v); if (n.getLengthSquared() == 0) { return NO_INTERSECTION; // triangle is degenerate } Vector3 dir = direction; // ray direction vector Vector3 w0 = origin - p1; double a = -n.dot(w0); double b = n.dot(dir); if (fabs(b) < 0.00001) { // ray is parallel to triangle plane if (a == 0) { return COPLANAR; // ray lies in triangle plane } else { return NO_INTERSECTION; // ray disjoint from plane } } // get intersect point of ray with triangle plane double r = a / b; if (r < 0.0) // ray goes away from triangle { return NO_INTERSECTION; // => no intersect } // for a segment, also test if (r > 1.0) => no intersect intersection = origin + direction * r; // // intersect point of ray and plane // is I inside T? double uu = u.dot(u); double uv = u.dot(v); double vv = v.dot(v); Vector3 w = intersection - p1; double wu = w.dot(u); double wv = w.dot(v); double D = uv * uv - uu * vv; // get and test parametric coords double s = (uv * wv - vv * wu) / D; if (s < 0.0 || s > 1.0) { return NO_INTERSECTION; // intersection is outside T } double t = (uv * wu - uu * wv) / D; if (t < 0.0 || (s + t) > 1.0) { return NO_INTERSECTION; // intersection is outside T } return POINT; // I is in T } /** * Tries to find the intersection point of this Ray with the sphere given by its * origin and radius. The intersection will be written to the given Vector3. * Returns false if the Ray misses the sphere, in which case the intersection will * be returned as the Ray's nearest point to the sphere. * Taken from GtkRadiant's sphere_intersect_ray() function. */ bool intersectSphere(const Vector3& sphereOrigin, double radius, Vector3& intersection) const { intersection = sphereOrigin - origin; const double a = intersection.dot(direction); const double d = radius * radius - (intersection.dot(intersection) - a * a); if (d > 0) { intersection = origin + direction * (a - sqrt(d)); return true; } else { intersection = origin + direction*a; return false; } } }; DarkRadiant-2.5.0/libs/math/Segment.h000066400000000000000000000014161321750546400173500ustar00rootroot00000000000000#pragma once #include "Vector3.h" #include "Plane3.h" class Segment { public: Vector3 origin; Vector3 extents; Segment() {} Segment(const Vector3& origin_, const Vector3& extents_) : origin(origin_), extents(extents_) {} static Segment createForStartEnd(const Vector3& start, const Vector3& end) { Segment segment; segment.origin = start.mid(end); segment.extents = end - segment.origin; return segment; } unsigned int classifyPlane(const Plane3& plane) const { double distance_origin = plane.normal().dot(origin) - plane.dist(); if (fabs(distance_origin) < fabs(plane.normal().dot(extents))) { return 1; // partially inside } else if (distance_origin < 0) { return 2; // totally inside } return 0; // totally outside } }; DarkRadiant-2.5.0/libs/math/Vector2.h000066400000000000000000000216021321750546400172710ustar00rootroot00000000000000#pragma once /* greebo: This file contains the templated class definition of the two-component vector * * BasicVector2: A vector with two components of type * * The BasicVector2 is equipped with the most important operators like *, *= and so on. * * Note: The most commonly used Vector2 is a BasicVector2, this is also defined in this file * * Note: that the multiplication of a Vector2 with another one (Vector2*Vector2) does NOT * result in an inner product but in a component-wise scaling. Use the .dot() method to * execute an inner product of two vectors. */ #include "lrint.h" #include #include #include template class BasicVector2 { // This is where the components of the vector are stored. Element _v[2]; public: // Public typedef to read the type of our elements typedef Element ElementType; // Constructor with no arguments BasicVector2() { _v[0] = 0; _v[1] = 0; } // Templated copy constructor template BasicVector2(const BasicVector2& other) { x() = static_cast(other.x()); y() = static_cast(other.y()); } /** Construct a BasicVector2 with the 2 provided components. */ BasicVector2(const Element& x_, const Element& y_) { _v[0] = x_; _v[1] = y_; } /** Construct a BasicVector2 from a 2-element array. The array must be * valid as no bounds checking is done. */ BasicVector2(const Element* array) { for (int i = 0; i < 2; ++i) _v[i] = array[i]; } /** Construct a BasicVector2 by parsing the supplied string. The string * must contain 2 numeric values separated by whitespace. * * @param str * The string from which component values are extracted. */ BasicVector2(const std::string& str) { // Initialise the vector with 0, in case the string parse fails _v[0] = _v[1] = 0; // Use a stringstream to parse the string std::stringstream strm(str); strm << std::skipws; strm >> x(); strm >> y(); } /** Construct a BasicVector2 out of 2 elements of type OtherType */ template BasicVector2(OtherType x, OtherType y) { _v[0] = static_cast(x); _v[1] = static_cast(y); } // Return NON-CONSTANT references to the vector components Element& x() { return _v[0]; } Element& y() { return _v[1]; } // Return CONSTANT references to the vector components const Element& x() const { return _v[0]; } const Element& y() const { return _v[1]; } /** * Operator cast to Element*. Also provides operator[] due to the inbuilt * operation on a pointer type. */ operator Element* () { return _v; } /** * Operator cast to const Element*. */ operator const Element* () const { return _v; } Element* data() { return _v; } const Element* data() const { return _v; } /** Compare this BasicVector2 against another for equality. */ bool operator== (const BasicVector2& other) const { return (other.x() == x() && other.y() == y()); } /** Compare this BasicVector2 against another for inequality. */ bool operator!= (const BasicVector2& other) const { return !(*this == other); } /* Define the negation operator - * All the vector's components are negated */ BasicVector2 operator- () const { return BasicVector2( -_v[0], -_v[1] ); } /* Define the addition operators + and += with any other BasicVector2 of type OtherElement * The vectors are added to each other element-wise */ template BasicVector2 operator+ (const BasicVector2& other) const { return BasicVector2( _v[0] + static_cast(other.x()), _v[1] + static_cast(other.y()) ); } template BasicVector2& operator+= (const BasicVector2& other) { _v[0] += static_cast(other.x()); _v[1] += static_cast(other.y()); return *this; } /* Define the substraction operators - and -= with any other BasicVector2 of type OtherElement * The vectors are substracted from each other element-wise */ template BasicVector2 operator- (const BasicVector2& other) const { return BasicVector2( _v[0] - static_cast(other.x()), _v[1] - static_cast(other.y()) ); } template BasicVector2& operator-= (const BasicVector2& other) { _v[0] -= static_cast(other.x()); _v[1] -= static_cast(other.y()); return *this; } /* Define the multiplication operators * and *= with another Vector2 of type OtherElement * * The vectors are multiplied element-wise * * greebo: This is mathematically kind of senseless, as this is a mixture of * a dot product and scalar multiplication. It can be used to scale each * vector component by a different factor, so maybe this comes in handy. */ template BasicVector2 operator* (const BasicVector2& other) const { return BasicVector2( _v[0] * static_cast(other.x()), _v[1] * static_cast(other.y()) ); } template BasicVector2& operator*= (const BasicVector2& other) { _v[0] *= static_cast(other.x()); _v[1] *= static_cast(other.y()); return *this; } /* Define the multiplications * and *= with a scalar */ template BasicVector2 operator* (const OtherElement& other) const { Element factor = static_cast(other); return BasicVector2( _v[0] * factor, _v[1] * factor ); } template BasicVector2& operator*= (const OtherElement& other) { Element factor = static_cast(other); _v[0] *= factor; _v[1] *= factor; return *this; } /* Define the division operators / and /= with another Vector2 of type OtherElement * The vectors are divided element-wise */ template BasicVector2 operator/ (const BasicVector2& other) const { return BasicVector2( _v[0] / static_cast(other.x()), _v[1] / static_cast(other.y()) ); } template BasicVector2& operator/= (const BasicVector2& other) { _v[0] /= static_cast(other.x()); _v[1] /= static_cast(other.y()); return *this; } /* Define the scalar divisions / and /= */ template BasicVector2 operator/ (const OtherElement& other) const { Element divisor = static_cast(other); return BasicVector2( _v[0] / divisor, _v[1] / divisor ); } template BasicVector2& operator/= (const OtherElement& other) { Element divisor = static_cast(other); _v[0] /= divisor; _v[1] /= divisor; return *this; } /** Cast to std::string */ operator std::string() const { return fmt::format("{0:f} {1:f}", _v[0], _v[1]); } /** Return the length of this vector. * * @returns * The Pythagorean length of this vector. */ double getLength() const { double lenSquared = _v[0]*_v[0] + _v[1]*_v[1]; return sqrt(lenSquared); } /** Return the squared length of this vector. */ double getLengthSquared() const { double lenSquared = _v[0]*_v[0] + _v[1]*_v[1]; return lenSquared; } /* Scalar product this vector with another Vector2, * returning the projection of onto * * @param other * The Vector2 to dot-product with this Vector2. * * @returns * The inner product (a scalar): a[0]*b[0] + a[1]*b[1] */ template Element dot(const BasicVector2& other) const { return Element(_v[0] * other.x() + _v[1] * other.y()); } /* Cross-product this vector with another Vector2, returning the scalar result * * @param other * The Vector2 to cross-product with this Vector2. * * @returns * The cross-product of the two vectors, a scalar: a[0]*b[1] - b[0]*a[1] */ template Element crossProduct(const BasicVector2& other) const { return Element(_v[0] * other.y() - _v[1] * other.x()); } // Returns the mid-point of this vector and the other one BasicVector2 mid(const BasicVector2& other) const { return (*this + other) * 0.5f; } }; // ========================================================================================== // A 2-element vector stored in double-precision floating-point. typedef BasicVector2 Vector2; // Stream insertion operator for a BasicVector2 template std::ostream& operator<<(std::ostream& st, BasicVector2 vec) { st << "<" << vec.x() << ", " << vec.y() << ">"; return st; } DarkRadiant-2.5.0/libs/math/Vector3.h000066400000000000000000000356641321750546400173070ustar00rootroot00000000000000#pragma once /* greebo: This file contains the templated class definition of the three-component vector * * BasicVector3: A vector with three components of type * * The BasicVector3 is equipped with the most important operators like *, *= and so on. * * Note: The most commonly used Vector3 is a BasicVector3, this is also defined in this file * * Note: that the multiplication of a Vector3 with another one (Vector3*Vector3) does NOT * result in an inner product but in a component-wise scaling. Use the .dot() method to * execute an inner product of two vectors. */ #include #include #include #include #include "math/pi.h" #include "lrint.h" #include "FloatTools.h" /// A 3-element vector of type Element template class BasicVector3 { // The actual values of the vector, an array containing 3 values of type // Element Element _v[3]; public: // Public typedef to read the type of our elements typedef Element ElementType; /** * Default constructor. Initialise Vector with all zeroes. */ BasicVector3() { _v[0] = 0; _v[1] = 0; _v[2] = 0; } /// Construct a BasicVector3 with the 3 provided components. BasicVector3(const Element& x_, const Element& y_, const Element& z_) { x() = x_; y() = y_; z() = z_; } /** Construct a BasicVector3 from a 3-element array. The array must be * valid as no bounds checking is done. */ BasicVector3(const Element* array) { for (int i = 0; i < 3; ++i) _v[i] = array[i]; } /** * Named constructor, returning a vector on the unit sphere for the given spherical coordinates. */ static BasicVector3 createForSpherical(Element theta, Element phi) { return BasicVector3( cos(theta) * cos(phi), sin(theta) * cos(phi), sin(phi) ); } /** Set all 3 components to the provided values. */ void set(const Element& x, const Element& y, const Element& z) { _v[0] = x; _v[1] = y; _v[2] = z; } /** * Check if this Vector is valid. A Vector is invalid if any of its * components are NaN. */ bool isValid() const { return !isnan(_v[0]) && !isnan(_v[1]) && !isnan(_v[2]); } // Return NON-CONSTANT references to the vector components Element& x() { return _v[0]; } Element& y() { return _v[1]; } Element& z() { return _v[2]; } // Return CONSTANT references to the vector components const Element& x() const { return _v[0]; } const Element& y() const { return _v[1]; } const Element& z() const { return _v[2]; } /** Compare this BasicVector3 against another for equality. */ bool operator== (const BasicVector3& other) const { return (other.x() == x() && other.y() == y() && other.z() == z()); } /** Compare this BasicVector3 against another for inequality. */ bool operator!= (const BasicVector3& other) const { return !(*this == other); } /* Define the negation operator - * All the vector's components are negated */ BasicVector3 operator- () const { return BasicVector3( -_v[0], -_v[1], -_v[2] ); } /* Define the addition operators + and += with any other BasicVector3 of type OtherElement * The vectors are added to each other element-wise */ template BasicVector3 operator+ (const BasicVector3& other) const { return BasicVector3( _v[0] + static_cast(other.x()), _v[1] + static_cast(other.y()), _v[2] + static_cast(other.z()) ); } template BasicVector3& operator+= (const BasicVector3& other) { _v[0] += static_cast(other.x()); _v[1] += static_cast(other.y()); _v[2] += static_cast(other.z()); return *this; } /* Define the substraction operators - and -= with any other BasicVector3 of type OtherElement * The vectors are substracted from each other element-wise */ template BasicVector3 operator- (const BasicVector3& other) const { return BasicVector3( _v[0] - static_cast(other.x()), _v[1] - static_cast(other.y()), _v[2] - static_cast(other.z()) ); } template BasicVector3& operator-= (const BasicVector3& other) { _v[0] -= static_cast(other.x()); _v[1] -= static_cast(other.y()); _v[2] -= static_cast(other.z()); return *this; } /* Define the multiplication operators * and *= with another Vector3 of type OtherElement * * The vectors are multiplied element-wise * * greebo: This is mathematically kind of senseless, as this is a mixture of * a dot product and scalar multiplication. It can be used to scale each * vector component by a different factor, so maybe this comes in handy. */ template BasicVector3 operator* (const BasicVector3& other) const { return BasicVector3( _v[0] * static_cast(other.x()), _v[1] * static_cast(other.y()), _v[2] * static_cast(other.z()) ); } template BasicVector3& operator*= (const BasicVector3& other) { _v[0] *= static_cast(other.x()); _v[1] *= static_cast(other.y()); _v[2] *= static_cast(other.z()); return *this; } /* Define the multiplications * and *= with a scalar */ template BasicVector3 operator* (const OtherElement& other) const { Element factor = static_cast(other); return BasicVector3( _v[0] * factor, _v[1] * factor, _v[2] * factor ); } template BasicVector3& operator*= (const OtherElement& other) { Element factor = static_cast(other); _v[0] *= factor; _v[1] *= factor; _v[2] *= factor; return *this; } /* Define the division operators / and /= with another Vector3 of type OtherElement * The vectors are divided element-wise */ template BasicVector3 operator/ (const BasicVector3& other) const { return BasicVector3( _v[0] / static_cast(other.x()), _v[1] / static_cast(other.y()), _v[2] / static_cast(other.z()) ); } template BasicVector3& operator/= (const BasicVector3& other) { _v[0] /= static_cast(other.x()); _v[1] /= static_cast(other.y()); _v[2] /= static_cast(other.z()); return *this; } /* Define the scalar divisions / and /= */ template BasicVector3 operator/ (const OtherElement& other) const { Element divisor = static_cast(other); return BasicVector3( _v[0] / divisor, _v[1] / divisor, _v[2] / divisor ); } template BasicVector3& operator/= (const OtherElement& other) { Element divisor = static_cast(other); _v[0] /= divisor; _v[1] /= divisor; _v[2] /= divisor; return *this; } /* * Mathematical operations on the BasicVector3 */ /** Return the length of this vector. * * @returns * The Pythagorean length of this vector. */ float getLength() const { float lenSquared = getLengthSquared(); return sqrt(lenSquared); } /** Return the squared length of this vector. */ float getLengthSquared() const { float lenSquared = float(_v[0]) * float(_v[0]) + float(_v[1]) * float(_v[1]) + float(_v[2]) * float(_v[2]); return lenSquared; } /** * Return a new BasicVector3 equivalent to the normalised version of this * BasicVector3 (scaled by the inverse of its size) */ BasicVector3 getNormalised() const { return (*this)/getLength(); } /** * Normalise this vector in-place by scaling by the inverse of its size. * Returns the length it had before normalisation. */ float normalise() { float length = getLength(); float inverseLength = 1/length; _v[0] *= inverseLength; _v[1] *= inverseLength; _v[2] *= inverseLength; return length; } // Returns a vector with the reciprocal values of each component BasicVector3 getInversed() { return BasicVector3( 1.0f / _v[0], 1.0f / _v[1], 1.0f / _v[2] ); } /* Scalar product this vector with another Vector3, * returning the projection of onto * * @param other * The Vector3 to dot-product with this Vector3. * * @returns * The inner product (a scalar): a[0]*b[0] + a[1]*b[1] + a[2]*b[2] */ template Element dot(const BasicVector3& other) const { return Element(_v[0] * other.x() + _v[1] * other.y() + _v[2] * other.z()); } /* Returns the angle between and * * @returns * The angle as defined by the arccos( (a*b) / (|a|*|b|) ) */ template Element angle(const BasicVector3& other) const { BasicVector3 aNormalised = getNormalised(); BasicVector3 otherNormalised = other.getNormalised(); Element dot = aNormalised.dot(otherNormalised); // greebo: Sanity correction: Make sure the dot product // of two normalised vectors is not greater than 1 if (dot > 1.0) { dot = 1; } return acos( dot ); } /* Cross-product this vector with another Vector3, returning the result * in a new Vector3. * * @param other * The Vector3 to cross-product with this Vector3. * * @returns * The cross-product of the two vectors. */ template BasicVector3 crossProduct(const BasicVector3& other) const { return BasicVector3( _v[1] * other.z() - _v[2] * other.y(), _v[2] * other.x() - _v[0] * other.z(), _v[0] * other.y() - _v[1] * other.x()); } /** Implicit cast to C-style array. This allows a Vector3 to be * passed directly to GL functions that expect an array (e.g. * glFloat3dv()). These functions implicitly provide operator[] * as well, since the C-style array provides this function. */ operator const Element* () const { return _v; } operator Element* () { return _v; } // Returns the maximum absolute value of the components Element max() const { return std::max(fabs(_v[0]), std::max(fabs(_v[1]), fabs(_v[2]))); } // Returns the minimum absolute value of the components Element min() const { return std::min(fabs(_v[0]), std::min(fabs(_v[1]), fabs(_v[2]))); } template bool isParallel(const BasicVector3& other) const { return float_equal_epsilon(angle(other), 0.0, 0.001) || float_equal_epsilon(angle(other), c_pi, 0.001); } // Swaps all components with the other vector template void swap(BasicVector3& other) { std::swap(x(), other.x()); std::swap(y(), other.y()); std::swap(z(), other.z()); } // Returns the mid-point of this vector and the other one BasicVector3 mid(const BasicVector3& other) const { return (*this + other) * 0.5f; } // Returns true if this vector is equal to the other one, considering the given tolerance. template bool isEqual(const BasicVector3& other, Epsilon epsilon) const { return float_equal_epsilon(x(), other.x(), epsilon) && float_equal_epsilon(y(), other.y(), epsilon) && float_equal_epsilon(z(), other.z(), epsilon); } /** * Returns a "snapped" copy of this Vector, each component rounded to integers. */ BasicVector3 getSnapped() const { return BasicVector3( static_cast(float_to_integer(x())), static_cast(float_to_integer(y())), static_cast(float_to_integer(z())) ); } /** * Snaps this vector to integer values in place. */ void snap() { *this = getSnapped(); } /** * Returns a "snapped" copy of this Vector, each component rounded to the given precision. */ template BasicVector3 getSnapped(const OtherElement& snap) const { return BasicVector3( static_cast(float_snapped(x(), snap)), static_cast(float_snapped(y(), snap)), static_cast(float_snapped(z(), snap)) ); } /** * Snaps this vector to the given precision in place. */ template void snap(const OtherElement& snap) { *this = getSnapped(snap); } }; /// Stream insertion for BasicVector3 template inline std::ostream& operator<<(std::ostream& st, BasicVector3 vec) { return st << vec.x() << " " << vec.y() << " " << vec.z(); } /// Stream extraction for BasicVector3 template inline std::istream& operator>>(std::istream& st, BasicVector3& vec) { return st >> std::skipws >> vec.x() >> vec.y() >> vec.z(); } // ========================================================================================== // A 3-element vector stored in double-precision floating-point. typedef BasicVector3 Vector3; // A 3-element vector (single-precision variant) typedef BasicVector3 Vector3f; // =============== Vector3 Constants ================================================== const Vector3 g_vector3_identity(0, 0, 0); const Vector3 g_vector3_axis_x(1, 0, 0); const Vector3 g_vector3_axis_y(0, 1, 0); const Vector3 g_vector3_axis_z(0, 0, 1); const Vector3 g_vector3_axes[3] = { g_vector3_axis_x, g_vector3_axis_y, g_vector3_axis_z }; DarkRadiant-2.5.0/libs/math/Vector4.h000066400000000000000000000244221321750546400172760ustar00rootroot00000000000000#pragma once /* greebo: This file contains the templated class definition of the three-component vector * * BasicVector4: A vector with three components of type * * The BasicVector4 is equipped with the most important operators like *, *= and so on. * * Note: The most commonly used Vector4 is a BasicVector4, this is also defined in this file * * Note: that the multiplication of a Vector4 with another one (Vector4*Vector4) does NOT * result in an inner product but in a component-wise scaling. Use the .dot() method to * execute an inner product of two vectors. */ #include "lrint.h" #include "FloatTools.h" #include "Vector3.h" /// A 4-element vector of type template class BasicVector4 { // The components of this vector Element _v[4]; public: // Public typedef to read the type of our elements typedef Element ElementType; // Constructor (no arguments) BasicVector4() { _v[0] = 0; _v[1] = 0; _v[2] = 0; _v[3] = 0; } // Construct a BasicVector4 out of the 4 arguments BasicVector4(Element x_, Element y_, Element z_, Element w_) { _v[0] = x_; _v[1] = y_; _v[2] = z_; _v[3] = w_; } // Construct a BasicVector4 out of a Vector3 plus a fourth argument BasicVector4(const BasicVector3& other, Element w_) { _v[0] = other.x(); _v[1] = other.y(); _v[2] = other.z(); _v[3] = w_; } // Return non-constant references to the components Element& x() { return _v[0]; } Element& y() { return _v[1]; } Element& z() { return _v[2]; } Element& w() { return _v[3]; } // Return constant references to the components const Element& x() const { return _v[0]; } const Element& y() const { return _v[1]; } const Element& z() const { return _v[2]; } const Element& w() const { return _v[3]; } Element index(std::size_t i) const { return _v[i]; } Element& index(std::size_t i) { return _v[i]; } /** Compare this BasicVector4 against another for equality. */ bool operator== (const BasicVector4& other) const { return (other.x() == x() && other.y() == y() && other.z() == z() && other.w() == w()); } /** Compare this BasicVector4 against another for inequality. */ bool operator!= (const BasicVector4& other) const { return !(*this == other); } /* Define the addition operators + and += with any other BasicVector4 of type OtherElement * The vectors are added to each other element-wise */ template BasicVector4 operator+ (const BasicVector4& other) const { return BasicVector4( _v[0] + static_cast(other.x()), _v[1] + static_cast(other.y()), _v[2] + static_cast(other.z()), _v[3] + static_cast(other.w()) ); } template BasicVector4& operator+= (const BasicVector4& other) { _v[0] += static_cast(other.x()); _v[1] += static_cast(other.y()); _v[2] += static_cast(other.z()); _v[3] += static_cast(other.w()); return *this; } /* Define the substraction operators - and -= with any other BasicVector4 of type OtherElement * The vectors are substracted from each other element-wise */ template BasicVector4 operator- (const BasicVector4& other) const { return BasicVector4( _v[0] - static_cast(other.x()), _v[1] - static_cast(other.y()), _v[2] - static_cast(other.z()), _v[3] - static_cast(other.w()) ); } template BasicVector4& operator-= (const BasicVector4& other) { _v[0] -= static_cast(other.x()); _v[1] -= static_cast(other.y()); _v[2] -= static_cast(other.z()); _v[3] -= static_cast(other.w()); return *this; } /* Define the multiplication operators * and *= with another Vector4 of type OtherElement * * The vectors are multiplied element-wise * * greebo: This is mathematically kind of senseless, as this is a mixture of * a dot product and scalar multiplication. It can be used to scale each * vector component by a different factor, so maybe this comes in handy. */ template BasicVector4 operator* (const BasicVector4& other) const { return BasicVector4( _v[0] * static_cast(other.x()), _v[1] * static_cast(other.y()), _v[2] * static_cast(other.z()), _v[3] * static_cast(other.w()) ); } template BasicVector4& operator*= (const BasicVector4& other) { _v[0] *= static_cast(other.x()); _v[1] *= static_cast(other.y()); _v[2] *= static_cast(other.z()); _v[3] *= static_cast(other.w()); return *this; } /* Define the multiplications * and *= with a scalar */ template BasicVector4 operator* (const OtherElement& other) const { Element factor = static_cast(other); return BasicVector4( _v[0] * factor, _v[1] * factor, _v[2] * factor, _v[3] * factor ); } template BasicVector4& operator*= (const OtherElement& other) { Element factor = static_cast(other); _v[0] *= factor; _v[1] *= factor; _v[2] *= factor; _v[3] *= factor; return *this; } /* Define the division operators / and /= with another Vector4 of type OtherElement * The vectors are divided element-wise */ template BasicVector4 operator/ (const BasicVector4& other) const { return BasicVector4( _v[0] / static_cast(other.x()), _v[1] / static_cast(other.y()), _v[2] / static_cast(other.z()), _v[3] / static_cast(other.w()) ); } template BasicVector4& operator/= (const BasicVector4& other) { _v[0] /= static_cast(other.x()); _v[1] /= static_cast(other.y()); _v[2] /= static_cast(other.z()); _v[3] /= static_cast(other.w()); return *this; } /* Define the scalar divisions / and /= */ template BasicVector4 operator/ (const OtherElement& other) const { Element divisor = static_cast(other); return BasicVector4( _v[0] / divisor, _v[1] / divisor, _v[2] / divisor, _v[3] / divisor ); } template BasicVector4& operator/= (const OtherElement& other) { Element divisor = static_cast(other); _v[0] /= divisor; _v[1] /= divisor; _v[2] /= divisor; _v[3] /= divisor; return *this; } /* Scalar product this vector with another Vector4, * returning the projection of onto * * @param other * The Vector4 to dot-product with this Vector4. * * @returns * The inner product (a scalar): a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3] */ template Element dot(const BasicVector4& other) const { return Element(_v[0] * other.x() + _v[1] * other.y() + _v[2] * other.z() + _v[3] * other.w()); } /** Project this homogeneous Vector4 into a Cartesian Vector3 * by dividing by w. * * @returns * A Vector3 representing the Cartesian equivalent of this * homogeneous vector. */ BasicVector3 getProjected() { return BasicVector3( _v[0] / _v[3], _v[1] / _v[3], _v[2] / _v[3]); } /** Implicit cast to C-style array. This allows a Vector4 to be * passed directly to GL functions that expect an array (e.g. * glFloat4dv()). These functions implicitly provide operator[] * as well, since the C-style array provides this function. */ operator const Element* () const { return _v; } operator Element* () { return _v; } /* Cast this Vector4 onto a Vector3, both const and non-const */ BasicVector3& getVector3() { return *reinterpret_cast*>(_v); } const BasicVector3& getVector3() const { return *reinterpret_cast*>(_v); } /** * Equality check with tolerance epsilon. */ template bool isEqual(const BasicVector4& other, Element epsilon) const { return float_equal_epsilon(x(), other.x(), epsilon) && float_equal_epsilon(y(), other.y(), epsilon) && float_equal_epsilon(z(), other.z(), epsilon) && float_equal_epsilon(w(), other.w(), epsilon); } }; // BasicVector4 /// Stream insertion for BasicVector4 template inline std::ostream& operator<<(std::ostream& st, BasicVector4 vec) { return st << vec.x() << " " << vec.y() << " " << vec.z() << " " << vec.w(); } /// Stream extraction for BasicVector4 template inline std::istream& operator>>(std::istream& st, BasicVector4& vec) { return st >> std::skipws >> vec.x() >> vec.y() >> vec.z() >> vec.w(); } // A 4-element vector stored in double-precision floating-point. typedef BasicVector4 Vector4; // A 4-element vector stored in single-precision floating-point. typedef BasicVector4 Vector4f; DarkRadiant-2.5.0/libs/math/ViewProjection.h000066400000000000000000000015361321750546400207200ustar00rootroot00000000000000#pragma once #include "Matrix4.h" /** * Specialisation of the Matrix4 class offering convenience methods * for viewprojection-related calculations. */ class ViewProjection : public Matrix4 { public: ViewProjection() : Matrix4() {} ViewProjection(const Matrix4& other) : Matrix4(other) {} bool testPoint(const Vector3& point) const; bool testPoint(const Vector3& point, const Matrix4& localToWorld) const; }; inline bool ViewProjection::testPoint(const Vector3& point) const { Vector4 hpoint = transform(Vector4(point, 1.0f)); if (fabs(hpoint[0]) < fabs(hpoint[3]) && fabs(hpoint[1]) < fabs(hpoint[3]) && fabs(hpoint[2]) < fabs(hpoint[3])) { return true; } return false; } inline bool ViewProjection::testPoint(const Vector3& point, const Matrix4& localToWorld) const { return testPoint(localToWorld.transformPoint(point)); } DarkRadiant-2.5.0/libs/math/Viewer.h000066400000000000000000000037251321750546400172140ustar00rootroot00000000000000#pragma once #include "Vector4.h" #include "Plane3.h" #include "Matrix4.h" #include "ViewProjection.h" class Viewer : public Vector4 { public: Viewer() : Vector4() {} Viewer(const Vector4& other) : Vector4(other) {} Viewer(const Vector3& vec3, float w) : Vector4(vec3, w) {} static Viewer createFromTransformedViewer(const Vector4& viewer, const Matrix4& transform); static Viewer createFromViewProjection(const ViewProjection& viewproj); bool testPlane(const Plane3& plane) const; bool testPlane(const Plane3& plane, const Matrix4& localToWorld) const; bool testTriangle(const Vector3& p0, const Vector3& p1, const Vector3& p2) const; }; inline bool Viewer::testPlane(const Plane3& plane) const { return (plane.normal().x() * x()) + (plane.normal().y() * y()) + (plane.normal().z() * z()) + (plane.dist() * w()) > 0; } inline bool Viewer::testPlane(const Plane3& plane, const Matrix4& localToWorld) const { return testPlane(plane.transformed(localToWorld)); } inline Viewer Viewer::createFromTransformedViewer(const Vector4& viewer, const Matrix4& transform) { if (viewer.w() == 0) { return Viewer(transform.transformDirection(viewer.getVector3()), 0); } else { return Viewer(transform.transformPoint(viewer.getVector3()), viewer.w()); } } inline Vector3 triangle_cross(const Vector3& p0, const Vector3& p1, const Vector3& p2) { return (p1 - p0).crossProduct(p1 - p2); } inline bool Viewer::testTriangle(const Vector3& p0, const Vector3& p1, const Vector3& p2) const { Vector3 cross = triangle_cross(p0, p1, p2); return (x() * cross[0]) + (y() * cross[1]) + (z() * cross[2]) > 0; } inline Viewer Viewer::createFromViewProjection(const ViewProjection& viewproj) { // get viewer pos in object coords Vector4 viewer(viewproj.getFullInverse().transform(Vector4(0, 0, -1, 0))); if (viewer[3] != 0) // non-affine matrix { viewer[0] /= viewer[3]; viewer[1] /= viewer[3]; viewer[2] /= viewer[3]; viewer[3] /= viewer[3]; } return viewer; } DarkRadiant-2.5.0/libs/math/curve.h000066400000000000000000000147051321750546400170770ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(INCLUDED_MATH_CURVE_H) #define INCLUDED_MATH_CURVE_H /// \file /// \brief Curve data types and related operations. #include #include "math/Matrix4.h" template struct BernsteinPolynomial { static double apply(double t) { return 1; // general case not implemented } }; /// \brief A compile-time-constant integer. template struct IntegralConstantC { enum unnamed_{ VALUE = VALUE_ }; }; typedef IntegralConstantC<0> Zero; typedef IntegralConstantC<1> One; typedef IntegralConstantC<2> Two; typedef IntegralConstantC<3> Three; typedef IntegralConstantC<4> Four; template<> struct BernsteinPolynomial { static double apply(double t) { return 1; } }; template<> struct BernsteinPolynomial { static double apply(double t) { return 1 - t; } }; template<> struct BernsteinPolynomial { static double apply(double t) { return t; } }; template<> struct BernsteinPolynomial { static double apply(double t) { return (1 - t) * (1 - t); } }; template<> struct BernsteinPolynomial { static double apply(double t) { return 2 * (1 - t) * t; } }; template<> struct BernsteinPolynomial { static double apply(double t) { return t * t; } }; template<> struct BernsteinPolynomial { static double apply(double t) { return (1 - t) * (1 - t) * (1 - t); } }; template<> struct BernsteinPolynomial { static double apply(double t) { return 3 * (1 - t) * (1 - t) * t; } }; template<> struct BernsteinPolynomial { static double apply(double t) { return 3 * (1 - t) * t * t; } }; template<> struct BernsteinPolynomial { static double apply(double t) { return t * t * t; } }; typedef std::vector ControlPoints; inline Vector3 CubicBezier_evaluate(const Vector3* firstPoint, double t) { Vector3 result(0, 0, 0); double denominator = 0; { double weight = BernsteinPolynomial::apply(t); result += (*firstPoint++) * weight; denominator += weight; } { double weight = BernsteinPolynomial::apply(t); result += (*firstPoint++) * weight; denominator += weight; } { double weight = BernsteinPolynomial::apply(t); result += (*firstPoint++) * weight; denominator += weight; } { double weight = BernsteinPolynomial::apply(t); result += (*firstPoint++) * weight; denominator += weight; } return result / denominator; } inline Vector3 CubicBezier_evaluateMid(const Vector3* firstPoint) { return firstPoint[0]*0.125 + firstPoint[1]*0.375 + firstPoint[2]*0.375 + firstPoint[3]*0.125; } inline Vector3 CatmullRom_evaluate(const ControlPoints& controlPoints, double t) { // scale t to be segment-relative t *= double(controlPoints.size() - 1); // subtract segment index; std::size_t segment = 0; for(std::size_t i = 0; i < controlPoints.size() - 1; ++i) { if(t <= double(i+1)) { segment = i; break; } } t -= segment; const double reciprocal_alpha = 1.0 / 3.0; Vector3 bezierPoints[4]; bezierPoints[0] = controlPoints[segment]; bezierPoints[1] = (segment > 0) ? controlPoints[segment] + (controlPoints[segment + 1] - controlPoints[segment - 1]) * (reciprocal_alpha * 0.5) : controlPoints[segment] + (controlPoints[segment + 1] - controlPoints[segment]) * reciprocal_alpha; bezierPoints[2] = (segment < controlPoints.size() - 2) ? controlPoints[segment + 1] + (controlPoints[segment] - controlPoints[segment + 2]) * (reciprocal_alpha * 0.5) : controlPoints[segment + 1] + (controlPoints[segment] - controlPoints[segment + 1]) * reciprocal_alpha; bezierPoints[3] = controlPoints[segment + 1]; return CubicBezier_evaluate(bezierPoints, t); } typedef std::vector Knots; inline double BSpline_basis(const Knots& knots, std::size_t i, std::size_t degree, double t) { if(degree == 0) { if(knots[i] <= t && t < knots[i + 1] && knots[i] < knots[i + 1]) { return 1; } return 0; } double leftDenom = knots[i + degree] - knots[i]; double left = (leftDenom == 0) ? 0 : ((t - knots[i]) / leftDenom) * BSpline_basis(knots, i, degree - 1, t); double rightDenom = knots[i + degree + 1] - knots[i + 1]; double right = (rightDenom == 0) ? 0 : ((knots[i + degree + 1] - t) / rightDenom) * BSpline_basis(knots, i + 1, degree - 1, t); return left + right; } inline Vector3 BSpline_evaluate(const ControlPoints& controlPoints, const Knots& knots, std::size_t degree, double t) { Vector3 result(0, 0, 0); for(std::size_t i = 0; i < controlPoints.size(); ++i) { result += controlPoints[i] * BSpline_basis(knots, i, degree, t); } return result; } typedef std::vector NURBSWeights; inline Vector3 NURBS_evaluate(const ControlPoints& controlPoints, const NURBSWeights& weights, const Knots& knots, std::size_t degree, double t) { Vector3 result(0, 0, 0); double denominator = 0; for(std::size_t i = 0; i < controlPoints.size(); ++i) { double weight = weights[i] * BSpline_basis(knots, i, degree, t); result += controlPoints[i] * weight; denominator += weight; } return result / denominator; } inline void KnotVector_openUniform(Knots& knots, std::size_t count, std::size_t degree) { knots.resize(count + degree + 1); std::size_t equalKnots = 1; for(std::size_t i = 0; i < equalKnots; ++i) { knots[i] = 0; knots[knots.size() - (i + 1)] = 1; } std::size_t difference = knots.size() - 2 * (equalKnots); for(std::size_t i = 0; i < difference; ++i) { knots[i + equalKnots] = Knots::value_type(double(i + 1) * 1.0 / double(difference + 1)); } } #endif DarkRadiant-2.5.0/libs/math/lrint.h000066400000000000000000000020011321750546400170650ustar00rootroot00000000000000#ifndef LRINT_H_ #define LRINT_H_ /** * Casting a double to an int the fast way. */ #if defined (_MSC_VER) #if defined (_WIN64) // greebo: It seems VC++ 2012 math.h already provides lrint #if _MSC_VER >= 1800 #include #else // Only for SSE2 or x64 #include // greebo: VC++ x64 doesn't support inline assembly, we have to use x64 intrinsics instead inline int lrint(const double x) { return _mm_cvtsd_si32(_mm_load_sd(&x)); } #endif #else // greebo: It seems VC++ 2012 math.h already provides lrint #if _MSC_VER >= 1800 #include #else // Win32 target inline int lrint (double flt) { int i; _asm { fld flt fistp i }; return i; } #endif #endif #elif defined(__FreeBSD__) inline int lrint(double f) { return static_cast(f + 0.5); } #elif defined(__GNUC__) // lrint is part of ISO C99 #define _ISOC9X_SOURCE 1 #define _ISOC99_SOURCE 1 #define __USE_ISOC9X 1 #define __USE_ISOC99 1 #else #error "unsupported platform" #endif #endif /*LRINT_H_*/ DarkRadiant-2.5.0/libs/math/pi.h000066400000000000000000000023731321750546400163610ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once /// \file /// \brief Pi constants and degrees/radians conversion. const double c_pi = 3.1415926535897932384626433832795; const double c_half_pi = c_pi / 2; const double c_2pi = 2 * c_pi; const double c_inv_2pi = 1 / c_2pi; const double c_DEG2RADMULT = c_pi / 180.0; const double c_RAD2DEGMULT = 180.0 / c_pi; inline double radians_to_degrees(double radians) { return radians * c_RAD2DEGMULT; } inline double degrees_to_radians(double degrees) { return degrees * c_DEG2RADMULT; } DarkRadiant-2.5.0/libs/math/test/000077500000000000000000000000001321750546400165525ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/math/test/matrixTest.cpp000066400000000000000000000457421321750546400214360ustar00rootroot00000000000000#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE matrixTest #include #include BOOST_AUTO_TEST_CASE(constructIdentity) { const Matrix4 identity = Matrix4::getIdentity(); BOOST_CHECK(identity.xx() == 1); BOOST_CHECK(identity.xy() == 0); BOOST_CHECK(identity.xz() == 0); BOOST_CHECK(identity.xw() == 0); BOOST_CHECK(identity.yx() == 0); BOOST_CHECK(identity.yy() == 1); BOOST_CHECK(identity.yz() == 0); BOOST_CHECK(identity.yw() == 0); BOOST_CHECK(identity.zx() == 0); BOOST_CHECK(identity.zy() == 0); BOOST_CHECK(identity.zz() == 1); BOOST_CHECK(identity.zw() == 0); BOOST_CHECK(identity.tx() == 0); BOOST_CHECK(identity.ty() == 0); BOOST_CHECK(identity.tz() == 0); BOOST_CHECK(identity.tw() == 1); Matrix4 identity2; identity2.xx() = 1; identity2.xy() = 0; identity2.xz() = 0; identity2.xw() = 0; identity2.yx() = 0; identity2.yy() = 1; identity2.yz() = 0; identity2.yw() = 0; identity2.zx() = 0; identity2.zy() = 0; identity2.zz() = 1; identity2.zw() = 0; identity2.tx() = 0; identity2.ty() = 0; identity2.tz() = 0; identity2.tw() = 1; BOOST_CHECK(identity == identity2); } BOOST_AUTO_TEST_CASE(constructInitialised) { Matrix4 m = Matrix4::byRows(1, 2.5, 3, 0.34, 51, -6, 7, 9, 9, 100, 11, 20, 13.11, 24, 15, 32); // Check individual values BOOST_CHECK_EQUAL(m.xx(), 1); BOOST_CHECK_EQUAL(m.xy(), 51); BOOST_CHECK_EQUAL(m.xz(), 9); BOOST_CHECK_EQUAL(m.xw(), 13.11); BOOST_CHECK_EQUAL(m.yx(), 2.5); BOOST_CHECK_EQUAL(m.yy(), -6); BOOST_CHECK_EQUAL(m.yz(), 100); BOOST_CHECK_EQUAL(m.yw(), 24); BOOST_CHECK_EQUAL(m.zx(), 3); BOOST_CHECK_EQUAL(m.zy(), 7); BOOST_CHECK_EQUAL(m.zz(), 11); BOOST_CHECK_EQUAL(m.zw(), 15); BOOST_CHECK_EQUAL(m.tx(), 0.34); BOOST_CHECK_EQUAL(m.ty(), 9); BOOST_CHECK_EQUAL(m.tz(), 20); BOOST_CHECK_EQUAL(m.tw(), 32); // Check vector components BOOST_CHECK_EQUAL(m.x(), Vector4(1, 51, 9, 13.11)); BOOST_CHECK_EQUAL(m.y(), Vector4(2.5, -6, 100, 24)); BOOST_CHECK_EQUAL(m.z(), Vector4(3, 7, 11, 15)); BOOST_CHECK_EQUAL(m.t(), Vector4(0.34, 9, 20, 32)); BOOST_CHECK_EQUAL(m.translation(), Vector3(0.34, 9, 20)); } BOOST_AUTO_TEST_CASE(testRotationMatrices) { double angle = 30.0; double cosAngle = cos(degrees_to_radians(angle)); double sinAngle = sin(degrees_to_radians(angle)); double EPSILON = 0.00001f; // Test X rotation Matrix4 xRot = Matrix4::getRotationAboutXDegrees(angle); BOOST_CHECK(float_equal_epsilon(xRot.xx(), 1, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.xy(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.xz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.xw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.yx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.yy(), cosAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.yz(), sinAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.yw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.zx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.zy(), -sinAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.zz(), cosAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.zw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.tx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.ty(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.tz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(xRot.tw(), 1, EPSILON)); // Test Y rotation Matrix4 yRot = Matrix4::getRotationAboutYDegrees(angle); BOOST_CHECK(float_equal_epsilon(yRot.xx(), cosAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.xy(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.xz(), -sinAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.xw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.yx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.yy(), 1, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.yz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.yw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.zx(), sinAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.zy(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.zz(), cosAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.zw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.tx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.ty(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.tz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(yRot.tw(), 1, EPSILON)); // Test Z rotation Matrix4 zRot = Matrix4::getRotationAboutZDegrees(angle); BOOST_CHECK(float_equal_epsilon(zRot.xx(), cosAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.xy(), sinAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.xz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.xw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.yx(), -sinAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.yy(), cosAngle, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.yz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.yw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.zx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.zy(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.zz(), 1, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.zw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.tx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.ty(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.tz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(zRot.tw(), 1, EPSILON)); // Test euler angle constructors Vector3 euler(30, -55, 75); // Convert degrees to radians double pi = 3.141592653589793238462643383f; double cx = cos(euler[0] * c_pi / 180.0f); double sx = sin(euler[0] * c_pi / 180.0f); double cy = cos(euler[1] * c_pi / 180.0f); double sy = sin(euler[1] * c_pi / 180.0f); double cz = cos(euler[2] * c_pi / 180.0f); double sz = sin(euler[2] * c_pi / 180.0f); // XYZ { Matrix4 eulerXYZ = Matrix4::getRotationForEulerXYZDegrees(euler); BOOST_CHECK(float_equal_epsilon(eulerXYZ.xx(), cy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.xy(), cy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.xz(), -sy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.xw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.yx(), sx*sy*cz - cx*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.yy(), sx*sy*sz + cx*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.yz(), sx*cy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.yw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.zx(), cx*sy*cz + sx*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.zy(), cx*sy*sz - sx*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.zz(), cx*cy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.zw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.tx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.ty(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.tz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXYZ.tw(), 1, EPSILON)); } // YZX { Matrix4 eulerYZX = Matrix4::getRotationForEulerYZXDegrees(euler); BOOST_CHECK(float_equal_epsilon(eulerYZX.xx(), cy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.xy(), cx*cy*sz + sx*sy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.xz(), sx*cy*sz - cx*sy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.xw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.yx(), -sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.yy(), cx*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.yz(), sx*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.yw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.zx(), sy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.zy(), cx*sy*sz - sx*cy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.zz(), sx*sy*sz + cx*cy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.zw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.tx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.ty(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.tz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYZX.tw(), 1, EPSILON)); } // XZY { Matrix4 eulerXZY = Matrix4::getRotationForEulerXZYDegrees(euler); BOOST_CHECK(float_equal_epsilon(eulerXZY.xx(), cy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.xy(), sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.xz(), -sy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.xw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.yx(), sx*sy - cx*cy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.yy(), cx*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.yz(), cx*sy*sz + sx*cy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.yw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.zx(), sx*cy*sz + cx*sy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.zy(), -sx*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.zz(), cx*cy - sx*sy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.zw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.tx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.ty(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.tz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerXZY.tw(), 1, EPSILON)); } // YXZ { Matrix4 eulerYXZ = Matrix4::getRotationForEulerYXZDegrees(euler); BOOST_CHECK(float_equal_epsilon(eulerYXZ.xx(), cy*cz - sx*sy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.xy(), cy*sz + sx*sy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.xz(), -cx*sy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.xw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.yx(), -cx*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.yy(), cx*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.yz(), sx, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.yw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.zx(), sy*cz + sx*cy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.zy(), sy*sz - sx*cy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.zz(), cx*cy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.zw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.tx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.ty(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.tz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerYXZ.tw(), 1, EPSILON)); } // ZXY { Matrix4 eulerZXY = Matrix4::getRotationForEulerZXYDegrees(euler); BOOST_CHECK(float_equal_epsilon(eulerZXY.xx(), cy*cz + sx*sy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.xy(), cx*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.xz(), sx*cy*sz - sy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.xw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.yx(), sx*sy*cz - cy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.yy(), cx*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.yz(), sx*cy*cz + sy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.yw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.zx(), cx*sy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.zy(), -sx, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.zz(), cx*cy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.zw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.tx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.ty(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.tz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZXY.tw(), 1, EPSILON)); } // ZYX { Matrix4 eulerZYX = Matrix4::getRotationForEulerZYXDegrees(euler); BOOST_CHECK(float_equal_epsilon(eulerZYX.xx(), cy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.xy(), cx*sz + sx*sy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.xz(), sx*sz - cx*sy*cz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.xw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.yx(), -cy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.yy(), cx*cz - sx*sy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.yz(), sx*cz + cx*sy*sz, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.yw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.zx(), sy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.zy(), -sx*cy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.zz(), cx*cy, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.zw(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.tx(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.ty(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.tz(), 0, EPSILON)); BOOST_CHECK(float_equal_epsilon(eulerZYX.tw(), 1, EPSILON)); } // Test Euler Angle retrieval (XYZ) { Matrix4 eulerXYZ = Matrix4::getRotationForEulerXYZDegrees(euler); Vector3 testEuler = eulerXYZ.getEulerAnglesXYZDegrees(); BOOST_CHECK(float_equal_epsilon(testEuler.x(), euler.x(), EPSILON)); BOOST_CHECK(float_equal_epsilon(testEuler.y(), euler.y(), EPSILON)); BOOST_CHECK(float_equal_epsilon(testEuler.z(), euler.z(), EPSILON)); } // Test Euler Angle retrieval (YXZ) { Matrix4 eulerYXZ = Matrix4::getRotationForEulerYXZDegrees(euler); Vector3 testEuler = eulerYXZ.getEulerAnglesYXZDegrees(); BOOST_CHECK(float_equal_epsilon(testEuler.x(), euler.x(), EPSILON)); BOOST_CHECK(float_equal_epsilon(testEuler.y(), euler.y(), EPSILON)); BOOST_CHECK(float_equal_epsilon(testEuler.z(), euler.z(), EPSILON)); } // Test Euler Angle retrieval (ZXY) { Matrix4 eulerZXY = Matrix4::getRotationForEulerZXYDegrees(euler); Vector3 testEuler = eulerZXY.getEulerAnglesZXYDegrees(); BOOST_CHECK(float_equal_epsilon(testEuler.x(), euler.x(), EPSILON)); BOOST_CHECK(float_equal_epsilon(testEuler.y(), euler.y(), EPSILON)); BOOST_CHECK(float_equal_epsilon(testEuler.z(), euler.z(), EPSILON)); } // Test Euler Angle retrieval (ZYX) { Matrix4 eulerZYX = Matrix4::getRotationForEulerZYXDegrees(euler); Vector3 testEuler = eulerZYX.getEulerAnglesZYXDegrees(); BOOST_CHECK(float_equal_epsilon(testEuler.x(), euler.x(), EPSILON)); BOOST_CHECK(float_equal_epsilon(testEuler.y(), euler.y(), EPSILON)); BOOST_CHECK(float_equal_epsilon(testEuler.z(), euler.z(), EPSILON)); } } BOOST_AUTO_TEST_CASE(testMultiplication) { Matrix4 a = Matrix4::byColumns(3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59); Matrix4 b = Matrix4::byColumns(61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137); Matrix4 c = a.getMultipliedBy(b); BOOST_CHECK(c.xx() == 6252); BOOST_CHECK(c.xy() == 7076); BOOST_CHECK(c.xz() == 8196); BOOST_CHECK(c.xw() == 9430); BOOST_CHECK(c.yx() == 8068); BOOST_CHECK(c.yy() == 9124); BOOST_CHECK(c.yz() == 10564); BOOST_CHECK(c.yw() == 12150); BOOST_CHECK(c.zx() == 9432); BOOST_CHECK(c.zy() == 10696); BOOST_CHECK(c.zz() == 12400); BOOST_CHECK(c.zw() == 14298); BOOST_CHECK(c.tx() == 11680); BOOST_CHECK(c.ty() == 13224); BOOST_CHECK(c.tz() == 15312); BOOST_CHECK(c.tw() == 17618); // Test Pre-Multiplication BOOST_CHECK(b.getMultipliedBy(a) == a.getPremultipliedBy(b)); // Create an affine matrix Matrix4 affineA = a; affineA.xw() = 0; affineA.yw() = 0; affineA.zw() = 0; affineA.tw() = 1; BOOST_CHECK(affineA.isAffine()); } BOOST_AUTO_TEST_CASE(testTransformation) { Matrix4 a = Matrix4::byColumns(3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59); { Vector3 v(61, 67, 71); Vector3 transformed = a.transformPoint(v); BOOST_CHECK(transformed.x() == 3156); BOOST_CHECK(transformed.y() == 3692); BOOST_CHECK(transformed.z() == 4380); Vector3 transformedDir = a.transformDirection(v); BOOST_CHECK(transformedDir.x() == 3113); BOOST_CHECK(transformedDir.y() == 3645); BOOST_CHECK(transformedDir.z() == 4327); } { Vector4 vector(83, 89, 97, 101); Vector4 transformed = a.transform(vector); BOOST_CHECK(transformed.x() == 8562); BOOST_CHECK(transformed.y() == 9682); BOOST_CHECK(transformed.z() == 11214); BOOST_CHECK(transformed.w() == 12896); } BOOST_CHECK_EQUAL(a.translation().x(), 43); BOOST_CHECK_EQUAL(a.translation().y(), 47); BOOST_CHECK_EQUAL(a.translation().z(), 53); } BOOST_AUTO_TEST_CASE(testMatrixDeterminant) { Matrix4 a = Matrix4::byColumns(3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59); BOOST_CHECK(a.getDeterminant() == -448); } BOOST_AUTO_TEST_CASE(testMatrixInversion) { Matrix4 a = Matrix4::byColumns(3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59); Matrix4 inv = a.getFullInverse(); double EPSILON = 0.00001f; BOOST_CHECK(float_equal_epsilon(inv.xx(), 0.392857f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.xy(), -0.714286f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.xz(), -0.321429f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.xw(), 0.428571f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.yx(), -0.276786f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.yy(), 0.446429f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.yz(), -0.330357f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.yw(), 0.107143f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.zx(), -0.669643f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.zy(), 0.660714f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.zz(), 0.991071f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.zw(), -0.821429f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.tx(), 0.535714f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.ty(), -0.428571f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.tz(), -0.392857f, EPSILON)); BOOST_CHECK(float_equal_epsilon(inv.tw(), 0.357143f, EPSILON)); } ������������������������������DarkRadiant-2.5.0/libs/math/test/planeTest.cpp������������������������������������������������������0000664�0000000�0000000�00000012272�13217505464�0021221�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE vectorTest #include #include #include #include using namespace boost::math; namespace { const double EPSILON = 0.0000001; const double ONE_OVER_ROOT_TWO = 1.0 / constants::root_two(); } BOOST_AUTO_TEST_CASE(constructPlane) { Plane3 plane(1, 0, 0, 4); BOOST_CHECK_EQUAL(plane.normal(), Vector3(1, 0, 0)); BOOST_CHECK_EQUAL(plane.dist(), 4); Plane3 plane2(1.5, 56.4, -325, 19.23); BOOST_CHECK_EQUAL(plane2.normal(), Vector3(1.5, 56.4, -325)); BOOST_CHECK_EQUAL(plane2.dist(), 19.23); Plane3 copy = plane2; BOOST_CHECK_EQUAL(copy.normal(), Vector3(1.5, 56.4, -325)); BOOST_CHECK_EQUAL(copy.dist(), 19.23); } BOOST_AUTO_TEST_CASE(translatePlaneDirectly) { // Basic plane Plane3 plane(1, 0, 0, 2.5); plane.translate(Vector3(2, 0, 0)); BOOST_CHECK_EQUAL(plane.normal(), Vector3(1, 0, 0)); BOOST_CHECK_EQUAL(plane.dist(), 0.5); // Inclined plane Vector3 normal = Vector3(1.5, 2.6, -0.3).getNormalised(); Plane3 inclined(normal, 4.5); BOOST_CHECK_EQUAL(inclined.normal(), normal); inclined.translate(Vector3(1, 1, 0)); BOOST_CHECK_CLOSE(inclined.normal().x(), 0.49724515641972766, EPSILON); BOOST_CHECK_CLOSE(inclined.normal().y(), 0.86189160446086122, EPSILON); BOOST_CHECK_CLOSE(inclined.normal().z(), -0.0994490312834552, EPSILON); // Check that a plane transformed by a translation matrix matches the // plane's own translation function Vector3 normal3 = Vector3(0.5, 1.24, 56).getNormalised(); Plane3 plane3(normal3, -56.1); Vector3 transVec(1.5, 45, -2.29); Matrix4 transMat = Matrix4::getTranslation(transVec); Plane3 p3_matrix = plane3.transformed(transMat); Plane3 p3_trans = plane3; p3_trans.translate(transVec); BOOST_CHECK_CLOSE(p3_matrix.normal().x(), p3_trans.normal().x(), EPSILON); BOOST_CHECK_CLOSE(p3_matrix.normal().y(), p3_trans.normal().y(), EPSILON); BOOST_CHECK_CLOSE(p3_matrix.normal().z(), p3_trans.normal().z(), EPSILON); } BOOST_AUTO_TEST_CASE(translatePlaneWithMatrix) { // Plane for y = 5 Plane3 plane(0, 1, 0, 5); BOOST_CHECK_EQUAL(plane.normal(), Vector3(0, 1, 0)); // Translate the plane by 3 in the Y direction Matrix4 trans = Matrix4::getTranslation(Vector3(0, 3, 0)); Plane3 newPlane = plane.transformed(trans); // The normal should not have changed but the distance should, although for // some reason the translation happens backwards (i.e. negative Y) BOOST_CHECK_EQUAL(newPlane.normal(), Vector3(0, 1, 0)); BOOST_CHECK_EQUAL(newPlane.dist(), 2); // Inclined plane Plane3 inclined(1, -1, 0, 0); BOOST_CHECK_EQUAL(inclined.dist(), 0); // Again move 3 in the Y direction Plane3 newInclined = inclined.transformed(trans); // Again there should be no normal change, but a distance change BOOST_CHECK_EQUAL(newInclined.normal(), Vector3(1, -1, 0)); BOOST_CHECK_EQUAL(newInclined.dist(), 3); // If moved along Z the distance should not change Plane3 movedZ = inclined.transformed( Matrix4::getTranslation(Vector3(0, 0, 2.3)) ); BOOST_CHECK_EQUAL(movedZ.normal(), Vector3(1, -1, 0)); BOOST_CHECK_EQUAL(movedZ.dist(), 0); } BOOST_AUTO_TEST_CASE(rotatePlane) { // A plane at 5 in the Y direction Plane3 plane(0, 1, 0, 5); // Rotate 45 degrees around the Z axis Matrix4 rot = Matrix4::getRotation( Vector3(0, 0, 1), constants::pi() / 4 ); Plane3 rotated = plane.transformed(rot); BOOST_CHECK_CLOSE(rotated.normal().x(), ONE_OVER_ROOT_TWO, EPSILON); BOOST_CHECK_CLOSE(rotated.normal().y(), ONE_OVER_ROOT_TWO, EPSILON); BOOST_CHECK_EQUAL(rotated.normal().z(), 0); } BOOST_AUTO_TEST_CASE(scalePlane) { Plane3 plane(1, -1, 0, 3.5); // Scale the plane by a factor of 2 Matrix4 times2 = Matrix4::getScale(Vector3(2, 2, 2)); Plane3 scaled = plane.transformed(times2); BOOST_CHECK_EQUAL(scaled.normal().x(), 2); BOOST_CHECK_EQUAL(scaled.normal().y(), -2); BOOST_CHECK_EQUAL(scaled.normal().z(), 0); BOOST_CHECK_EQUAL(scaled.dist(), 28); } BOOST_AUTO_TEST_CASE(transformPlane) { // Check transform with some randomly-generated values with no particular // geometric meaning Plane3 plane(-2.643101, -47.856364, 17.5173264, -35.589485); Plane3 copy = plane; Matrix4 arbMatrix = Matrix4::byRows( -30.1065587, -28.048640, -10.003604, 18.986724, 18.94792014, -16.186764, -8.7790217, -32.59777, 12.85452125, -8.8305872, -36.502315, 32.345895, 0, 0, 0, 1 ); Plane3 transformed = plane.transformed(arbMatrix); BOOST_CHECK_EQUAL(plane, copy); // must not be changed by transformed() // TODO: Improve the maths so that this works more accurately static const double ROUGH_EPSILON = 0.001; BOOST_CHECK_CLOSE(transformed.normal().x(), 1246.64431, ROUGH_EPSILON); BOOST_CHECK_CLOSE(transformed.normal().y(), 570.775895, ROUGH_EPSILON); BOOST_CHECK_CLOSE(transformed.normal().z(), -250.79896, ROUGH_EPSILON); BOOST_CHECK_CLOSE(transformed.dist(), -69140351.87, EPSILON); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/math/test/quaternionTest.cpp�������������������������������������������������0000664�0000000�0000000�00000004043�13217505464�0022304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE quaternionTest #include #include BOOST_AUTO_TEST_CASE(testQuaternions) { Quaternion q1(3, 5, 7, 11); Quaternion q2(13, 17, 19, 23); Quaternion product = q1.getMultipliedBy(q2); BOOST_CHECK(product.x() == 188); BOOST_CHECK(product.y() == 336); BOOST_CHECK(product.z() == 356); BOOST_CHECK(product.w() == -4); Quaternion q1multiplied = q1; q1multiplied.multiplyBy(q2); BOOST_CHECK(q1multiplied.x() == 188); BOOST_CHECK(q1multiplied.y() == 336); BOOST_CHECK(q1multiplied.z() == 356); BOOST_CHECK(q1multiplied.w() == -4); Quaternion q1inverted = q1.getInverse(); BOOST_CHECK(q1inverted.x() == -3); BOOST_CHECK(q1inverted.y() == -5); BOOST_CHECK(q1inverted.z() == -7); BOOST_CHECK(q1inverted.w() == 11); Quaternion normalised = q1.getNormalised(); const double EPSILON = 0.0001f; BOOST_CHECK_CLOSE(normalised.x(), 0.2100420126042014f, EPSILON); BOOST_CHECK_CLOSE(normalised.y(), 0.3500700210070024f, EPSILON); BOOST_CHECK_CLOSE(normalised.z(), 0.4900980294098034f, EPSILON); BOOST_CHECK_CLOSE(normalised.w(), 0.7701540462154054f, EPSILON); Vector3 point(13, 17, 19); Vector3 transformed = q1.transformPoint(point); BOOST_CHECK(transformed.x() == q1.w()*q1.w()*point.x() + 2*q1.y()*q1.w()*point.z() - 2*q1.z()*q1.w()*point.y() + q1.x()*q1.x()*point.x() + 2*q1.y()*q1.x()*point.y() + 2*q1.z()*q1.x()*point.z() - q1.z()*q1.z()*point.x() - q1.y()*q1.y()*point.x()); BOOST_CHECK(transformed.y() == 2*q1.x()*q1.y()*point.x() + q1.y()*q1.y()*point.y() + 2*q1.z()*q1.y()*point.z() + 2*q1.w()*q1.z()*point.x() - q1.z()*q1.z()*point.y() + q1.w()*q1.w()*point.y() - 2*q1.x()*q1.w()*point.z() - q1.x()*q1.x()*point.y()); BOOST_CHECK(transformed.z() == 2*q1.x()*q1.z()*point.x() + 2*q1.y()*q1.z()*point.y() + q1.z()*q1.z()*point.z() - 2*q1.w()*q1.y()*point.x() - q1.y()*q1.y()*point.z() + 2*q1.w()*q1.x()*point.y() - q1.x()*q1.x()*point.z() + q1.w()*q1.w()*point.z()); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/math/test/vectorTest.cpp�����������������������������������������������������0000664�0000000�0000000�00000000474�13217505464�0021425�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE vectorTest #include #include BOOST_AUTO_TEST_CASE(constructVector3) { Vector3 vec(1.0, 2.0, 3.5); BOOST_CHECK_EQUAL(vec.x(), 1.0); BOOST_CHECK_EQUAL(vec.y(), 2.0); BOOST_CHECK_EQUAL(vec.z(), 3.5); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/moduleobservers.h������������������������������������������������������������0000664�0000000�0000000�00000004733�13217505464�0020242�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(INCLUDED_MODULEOBSERVERS_H) #define INCLUDED_MODULEOBSERVERS_H #include "debugging/debugging.h" #include #include "imodule.h" #ifdef _DEBUG #include "itextstream.h" #endif #include class ModuleObservers { typedef std::set Observers; Observers m_observers; // greebo: Added this to make debugging easier, so that // the warning in the destructor provides more information. std::string _ownerName; public: // Default constructor, for backwards compatibility ModuleObservers() : _ownerName("Owner unknown.") {} ModuleObservers(const std::string& ownerName) : _ownerName(ownerName) {} #ifdef _DEBUG // Warning if observers still attached in destructor (may not be necessary, // replaces previous ASSERT. ~ModuleObservers() { if (!m_observers.empty()) { rConsole() << "Warning: destroying ModuleObservers with " << m_observers.size() << " observers attached." << " (Owner: " << _ownerName << ")" << std::endl; } } #endif void attach(ModuleObserver& observer) { ASSERT_MESSAGE(m_observers.find(&observer) == m_observers.end(), "ModuleObservers::attach: cannot attach observer"); m_observers.insert(&observer); } void detach(ModuleObserver& observer) { ASSERT_MESSAGE(m_observers.find(&observer) != m_observers.end(), "ModuleObservers::detach: cannot detach observer"); m_observers.erase(&observer); } void realise() { for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) { (*i)->realise(); } } void unrealise() { for(Observers::reverse_iterator i = m_observers.rbegin(); i != m_observers.rend(); ++i) { (*i)->unrealise(); } } }; #endif �������������������������������������DarkRadiant-2.5.0/libs/os/��������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13217505464�0015263�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/os/dir.h���������������������������������������������������������������������0000664�0000000�0000000�00000004307�13217505464�0016216�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once /// \file /// \brief OS directory-listing object. #include "fs.h" #include "itextstream.h" #include #include #include namespace os { /** * Exception thrown by foreachItemInDirectory to indicate that the given directory * does not exist. */ class DirectoryNotFoundException : public std::runtime_error { public: DirectoryNotFoundException(const std::string& what) : std::runtime_error(what) {} }; // Invoke functor for all items in a directory inline void foreachItemInDirectory(const std::string& path, const std::function& functor) { fs::path start(path); if (!fs::exists(start)) { throw DirectoryNotFoundException("foreachItemInDirectory(): invalid directory '" + path + "'"); } for (fs::directory_iterator it(start); it != fs::directory_iterator(); ++it) { functor(*it); } } /** * \brief * Create the named directory if it doesn't exist * * \return * true if the directory was created or already exists, false if there was an * error. */ inline bool makeDirectory(const std::string& name) { try { fs::path dirPath(name); // Create the directory if (fs::create_directory(dirPath)) { // Directory has been created, set permissions rConsole() << "Directory " << dirPath << " created succesfully." << std::endl; #ifdef DR_USE_STD_FILESYSTEM // Set permissions to rwxrwxr_x fs::permissions(dirPath, fs::perms::add_perms | fs::perms::owner_exec | fs::perms::owner_write | fs::perms::owner_read | fs::perms::group_exec | fs::perms::group_write | fs::perms::group_read | fs::perms::others_exec | fs::perms::others_read); #else // Set permissions to rwxrwxr_x fs::permissions(dirPath, fs::add_perms | fs::owner_exe | fs::owner_write | fs::owner_read | fs::group_exe | fs::group_write | fs::group_read | fs::others_exe | fs::others_read); #endif } // Directory already exists or has been created successfully return true; } catch (fs::filesystem_error& ex) { rConsoleError() << "os::makeDirectory(" << name << ") failed with error " << ex.what() << " (" << ex.code().value() << ")" << std::endl; // Directory creation failed return false; } } } // namespace os �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/os/file.h��������������������������������������������������������������������0000664�0000000�0000000�00000005316�13217505464�0016360�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once #include "itextstream.h" #include "fs.h" #include "debugging/debugging.h" /// \file /// \brief OS file-system querying and manipulation. #ifdef WIN32 #include // _access() #ifdef _MSC_VER // greebo: The Windows API doesn't define R_OK et al, but luckily // the _access method takes the same integer/bit values as the POSIX access() #define F_OK 0x00 #define W_OK 0x02 #define R_OK 0x04 #endif #else #include // access() #endif namespace os { namespace detail { enum class FileAccess { Read = R_OK, Write = W_OK, ReadWrite = Read | Write, Exists = F_OK }; /// \brief Returns true if the file or directory identified by \p path exists and/or may be /// accessed for reading, writing or both, depending on the value of \p mode. inline bool checkFileAccess(const std::string& path, FileAccess mode) { #ifdef WIN32 return ::_access(path.c_str(), static_cast(mode)) == 0; #else return ::access(path.c_str(), static_cast(mode)) == 0; #endif } } /// \brief Returns true if the file or directory identified by \p path exists and may be opened for reading. inline bool fileIsReadable(const std::string& path) { return detail::checkFileAccess(path, detail::FileAccess::Read); } /// \brief Returns true if the file or directory identified by \p path exists and may be opened for reading. inline bool fileIsReadable(const fs::path& path) { return detail::checkFileAccess(path.string(), detail::FileAccess::Read); } /// \brief Returns true if the file or directory identified by \p path exists and may be opened for writing. inline bool fileIsWritable(const std::string& path) { return detail::checkFileAccess(path, detail::FileAccess::Write); } /// \brief Returns true if the file or directory identified by \p path exists and may be opened for writing. inline bool fileIsWritable(const fs::path& path) { return detail::checkFileAccess(path.string(), detail::FileAccess::Write); } /// \brief Returns true if the file or directory identified by \p path exists. inline bool fileOrDirExists(const std::string& path) { try { return fs::exists(path); } catch (fs::filesystem_error&) { return false; } } /// \brief Returns true if the file or directory identified by \p path exists. inline bool fileOrDirExists(const fs::path& path) { try { return fs::exists(path); } catch (fs::filesystem_error&) { return false; } } // Returns the file size in bytes, or static_cast(-1) inline std::size_t getFileSize(const std::string& path) { try { return static_cast(fs::file_size(path)); } catch (fs::filesystem_error& err) { rError() << "Error checking filesize: " << err.what() << std::endl; return static_cast(-1); } } } // namespace ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/os/fs.h����������������������������������������������������������������������0000664�0000000�0000000�00000005412�13217505464�0016046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file * Helper header for std::filesystem feature set. Includes the relevant headers * and defines the common "fs" namespace alias. * If the compiler library supports the C++17 feature std::filesystem, it includes * the corresponding headers, or fall back to std::experimental::filesystem::v1. * All other compilers will refer to the headers provided by boost::filesystem. */ #pragma once // We need the HAVE_* symbols created by the configure script #ifdef HAVE_CONFIG_H #include #endif // If C++17 is available, use that one #ifdef HAVE_STD_FILESYSTEM #include namespace fs = std::filesystem; #define DR_USE_STD_FILESYSTEM // At the time of writing C++17 is still in draft state, but some compilers // provide the features through the std::experimental namespace. // In Linux, the configure script will check for the header and define the // HAVE_EXPERIMENTAL_FILESYSTEM symbol for us. #elif _MSC_VER >= 1900 || defined(HAVE_EXPERIMENTAL_FILESYSTEM) // Visual Studio 2015+ and GCC 5.3+ supply the experimental/filesystem header #include namespace fs = std::experimental::filesystem::v1; #define DR_USE_STD_FILESYSTEM #else // All other compilers will use the boost headers #include #include #define DR_USE_BOOST_FILESYSTEM namespace fs = boost::filesystem; #endif #include "string/predicate.h" namespace os { /// Overload of standardPathWithSlash that accepts a fs::path inline std::string standardPathWithSlash(const fs::path& p) { std::string genString = p.generic_string(); // Just add slash if needed, we don't need to convert intermediate // slashes since string_from_path will already have done that. if (!string::ends_with(genString, "/")) { genString += "/"; } return genString; } // Wrapper method to return the depth of a recursive iterator, // supporting std::experimental::filesystem as well as boost::filesystem inline int getDepth(fs::recursive_directory_iterator& it) { #ifdef DR_USE_STD_FILESYSTEM return it.depth(); #else return it.level(); #endif } // Wrapper method to call no_push / disable_recursion_pending on iterators // Since the C++17 call has been named differently than the boost one, here we go inline void disableRecursionPending(fs::recursive_directory_iterator& it) { #ifdef DR_USE_STD_FILESYSTEM it.disable_recursion_pending(); #else it.no_push(); #endif } // Replaces the extension of the given filename with newExt inline std::string replaceExtension(const std::string& input, const std::string& newExt) { #ifdef DR_USE_STD_FILESYSTEM return fs::path(input).replace_extension(newExt).string(); #else return fs::change_extension(input, newExt).string(); #endif } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/os/path.h��������������������������������������������������������������������0000664�0000000�0000000�00000014710�13217505464�0016373�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once /// \file /// \brief OS file-system path comparison, decomposition and manipulation. /// /// - Paths are c-style null-terminated-character-arrays. /// - Path separators must be forward slashes (unix style). /// - Directory paths must end in a separator. /// - Paths must not contain the ascii characters \\ : * ? " < > or |. /// - Paths may be encoded in UTF-8 or any extended-ascii character set. #include "string/string.h" #include "string/predicate.h" #include "string/replace.h" #if defined(WIN32) #define OS_CASE_INSENSITIVE #endif /// \brief Returns true if the first \p n bytes of \p path and \p other form paths that refer to the same file or directory. /// If the paths are UTF-8 encoded, [\p path, \p path + \p n) must be a complete path. /// O(n) inline bool path_equal_n(const char* path, const char* other, std::size_t n) { #if defined(OS_CASE_INSENSITIVE) return string_equal_nocase_n(path, other, n); #else return string_equal_n(path, other, n); #endif } /// \brief Returns true if \p path is a fully qualified file-system path. /// O(1) inline bool path_is_absolute(const char* path) { #if defined(WIN32) return path[0] == '/' || (path[0] != '\0' && path[1] == ':'); // local drive #elif defined(POSIX) return path[0] == '/'; #endif } /// \brief Returns a pointer to the first character of the component of \p path following the first directory component. /// O(n) inline const char* path_remove_directory(const char* path) { const char* first_separator = strchr(path, '/'); if(first_separator != 0) { return ++first_separator; } return ""; } /** General utility functions for OS-related tasks */ namespace os { /** Convert the slashes in a Doom 3 path to forward-slashes. Doom 3 accepts either * forward or backslashes in its definitions */ inline std::string standardPath(const std::string& inPath) { return string::replace_all_copy(inPath, "\\", "/"); } /** greebo: OS Folder names have forward slashes and a trailing slash * at the end by convention. Empty strings are returned unchanged. */ inline std::string standardPathWithSlash(const std::string& input) { std::string output = standardPath(input); // Append a slash at the end, if there isn't already one if (!output.empty() && !string::ends_with(output, "/")) { output += "/"; } return output; } /** * Return the path of fullPath relative to basePath, as long as fullPath * is contained within basePath. If not, fullPath is returned unchanged. */ inline std::string getRelativePath(const std::string& fullPath, const std::string& basePath) { #ifdef OS_CASE_INSENSITIVE if (string::istarts_with(fullPath, basePath)) #else if (string::starts_with(fullPath, basePath)) #endif { return fullPath.substr(basePath.length()); } else { return fullPath; } } /** * stifu: Does the same as getRelativePath, but also strips the filename. */ inline std::string getRelativePathMinusFilename(const std::string& fullPath, const std::string& basePath) { #ifdef OS_CASE_INSENSITIVE if (string::istarts_with(fullPath, basePath)) #else if (string::starts_with(fullPath, basePath)) #endif { return fullPath.substr(basePath.length(), fullPath.rfind('/') - basePath.length()); } else { return fullPath; } } /** * greebo: Get the filename contained in the given path (the part after the last slash). * If there is no filename, an empty string is returned. * * Note: The input string is expected to be standardised (forward slashes). */ inline std::string getFilename(const std::string& path) { std::size_t slashPos = path.rfind('/'); if (slashPos == std::string::npos) { return ""; } else { return path.substr(slashPos + 1); } } /** * Get the extension of the given filename. If there is no extension, an * empty string is returned. */ inline std::string getExtension(const std::string& path) { std::size_t dotPos = path.rfind('.'); if (dotPos == std::string::npos) { return ""; } else { return path.substr(dotPos + 1); } } /** * Get the containing folder of the specified object. This is calculated * as the directory before the rightmost slash (which will be the object * itself, if the pathname ends in a slash). * * If the path does not contain a slash, the empty string will be returned. * * E.g. * blah/bleh/file -> "bleh" * blah/bloog/ -> "bloog" */ inline std::string getContainingDir(const std::string& path) { std::size_t lastSlash = path.rfind('/'); if (lastSlash == std::string::npos) { return ""; } std::string trimmed = path.substr(0, lastSlash); lastSlash = trimmed.rfind('/'); return trimmed.substr(lastSlash + 1); } /** * Returns the directory part of the given path, cutting off the filename * right after the last slash. Use standard paths (forward slashes) only! * * E.g. * blah/bleh/file.ext -> "blah/bleh/" * blah/bloog -> "blah/" */ inline std::string getDirectory(const std::string& path) { std::size_t lastSlash = path.rfind('/'); if (lastSlash == std::string::npos) { return path; } return path.substr(0, lastSlash + 1); } /** * Returns true if the given string qualifies as path to a directory, * which is equal to the path string ending with the slash '/' character. */ inline bool isDirectory(const std::string& path) { return !path.empty() && path.back() == '/'; } } ��������������������������������������������������������DarkRadiant-2.5.0/libs/parser/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�13217505464�0016136�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/parser/CodeTokeniser.h�������������������������������������������������������0000664�0000000�0000000�00000064473�13217505464�0021063�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once #include "iarchive.h" #include "ifilesystem.h" #include "DefTokeniser.h" #include #include #include #include #include "string/trim.h" #include "string/predicate.h" #include "Tokeniser.h" namespace parser { // Code tokeniser function, with special treatment for #define statements class CodeTokeniserFunc { // Enumeration of states enum { SEARCHING, // haven't found anything yet TOKEN_STARTED, // found the start of a possible multi-char token AFTER_DEFINE, // after parsing a #define command AFTER_DEFINE_BACKSLASH, // after a #define token, encountering a backslash AFTER_DEFINE_SEARCHING_FOR_EOL, // after a #define token, after encountering a backslash AFTER_DEFINE_FORWARDSLASH, // after parsing when encountering a forward slash QUOTED, // inside quoted text, no tokenising AFTER_CLOSING_QUOTE, // right after a quoted text, checking for backslash SEARCHING_FOR_QUOTE, // searching for continuation of quoted string (after a backslash was found) FORWARDSLASH, // forward slash found, possible comment coming COMMENT_EOL, // double-forwardslash comment COMMENT_DELIM, // inside delimited comment (/*) STAR // asterisk, possibly indicates end of comment (*/) } _state; // List of delimiters to skip const char* _delims; // List of delimiters to keep const char* _keptDelims; // Test if a character is a delimiter bool isDelim(char c) { const char* curDelim = _delims; while (*curDelim != 0) { if (*(curDelim++) == c) { return true; } } return false; } // Test if a character is a kept delimiter bool isKeptDelim(char c) { const char* curDelim = _keptDelims; while (*curDelim != 0) { if (*(curDelim++) == c) { return true; } } return false; } public: // Constructor CodeTokeniserFunc(const char* delims, const char* keptDelims) : _state(SEARCHING), _delims(delims), _keptDelims(keptDelims) {} /* REQUIRED. Operator() is called by the Tokeniser. This function * must search for a token between the two iterators next and end, and if * a token is found, set tok to the token, set next to position to start * parsing on the next call, and return true. */ template bool operator() (InputIterator& next, InputIterator end, Token& tok) { // Initialise state, no persistence between calls _state = SEARCHING; // Clear out the token, no guarantee that it is empty tok = ""; enum class QuoteType { Single = 0, Double = 1, }; QuoteType quoteType = QuoteType::Single; while (next != end) { switch (_state) { case SEARCHING: // If we have a delimiter, just advance to the next character if (isDelim(*next)) { ++next; continue; } // If we have a KEPT delimiter, this is the token to return. if (isKeptDelim(*next)) { tok = *(next++); return true; } // Otherwise fall through into TOKEN_STARTED, saving the state for the // next iteration _state = TOKEN_STARTED; case TOKEN_STARTED: // Here a delimiter indicates a successful token match if (isDelim(*next) || isKeptDelim(*next)) { // Check the token for possible preprocessor #define if (tok == "#define") { _state = AFTER_DEFINE; continue; } return true; } // Now next is pointing at a non-delimiter. Switch on this // character. switch (*next) { // Found a quote, enter QUOTED state, or return the // current token if we are in the process of building // one. case '\"': if (tok != "") { return true; } else { quoteType = QuoteType::Double; _state = QUOTED; ++next; continue; // skip the quote } case '\'': if (tok != "") { return true; } else { quoteType = QuoteType::Single; _state = QUOTED; ++next; continue; // skip the quote } // Found a slash, possibly start of comment case '/': _state = FORWARDSLASH; ++next; continue; // skip slash, will need to add it back if this is not a comment // General case. Token lasts until next delimiter. default: tok += *next; ++next; continue; } case AFTER_DEFINE: // Collect token until EOL is found if (*next == '\r' || *next == '\n') { _state = SEARCHING; ++next; return true; } else if (*next == '\\') { // Found a backslash, this can be used to connect lines _state = AFTER_DEFINE_BACKSLASH; ++next; continue; } else if (*next == '/') { // This could be a (line) comment starting here _state = AFTER_DEFINE_FORWARDSLASH; ++next; continue; } else { tok += *next; ++next; continue; // do nothing } case AFTER_DEFINE_BACKSLASH: // Skip delimiters if (!isDelim(*next)) { // False alarm, not a delimiter after the backslash tok += '\\'; tok += *next; _state = AFTER_DEFINE; ++next; continue; } // Skip delimiters until next line break _state = AFTER_DEFINE_SEARCHING_FOR_EOL; // FALL THROUGH to AFTER_DEFINE_SEARCHING_FOR_EOL case AFTER_DEFINE_SEARCHING_FOR_EOL: // Search for EOL if (*next == '\r' || *next == '\n') { tok += '\n'; // add the line break to the token _state = AFTER_DEFINE; } ++next; continue; case AFTER_DEFINE_FORWARDSLASH: if (*next == '/') { // This is the second forward slash, we're in line comment mode now _state = COMMENT_EOL; ++next; continue; } else if (*next == '*') { // This is a star, we're in block comment mode now _state = COMMENT_DELIM; ++next; continue; } else { // False alarm, add the first slash and this character tok += '/'; // Switch back to DEFINE mode _state = AFTER_DEFINE; continue; } case QUOTED: // In the quoted state, just advance until the closing // quote. No delimiter splitting is required. if ((*next == '\"' && quoteType == QuoteType::Double) || (*next == '\'' && quoteType == QuoteType::Single)) { ++next; // greebo: We've found a closing quote, but there might be a backslash indicating // a multi-line string constant "" \ "", so switch to AFTER_CLOSING_QUOTE mode _state = AFTER_CLOSING_QUOTE; continue; } else if (*next == '\\') { // Escape found, check next character ++next; if (next != end) { if (*next == 'n') // Linebreak { tok += '\n'; } else if (*next == 't') // Tab { tok += '\t'; } else if (*next == '"' && quoteType == QuoteType::Double) // Escape Double Quote { tok += '"'; } else if (*next == '\'' && quoteType == QuoteType::Single) // Escaped Single Quote { tok += '"'; } else { // No special escape sequence, add the backslash tok += '\\'; // Plus the character itself tok += *next; } ++next; } continue; } else { tok += *next; ++next; continue; } case AFTER_CLOSING_QUOTE: // We already have a valid string token in our hands, but it might be continued // if one of the next tokens is a backslash if (*next == '\\') { // We've found a backslash right after a closing quote, this indicates we could // proceed with parsing quoted content ++next; _state = SEARCHING_FOR_QUOTE; continue; } // Ignore delimiters if (isDelim(*next)) { ++next; continue; } // Everything except delimiters and backslashes indicates that // the quoted content is not continued, so break the loop. // This returns the token and parsing continues. // Return TRUE in any case, even if the parsed token is empty (""). return true; case SEARCHING_FOR_QUOTE: // We have found a backslash after a closing quote, search for an opening quote // Step over delimiters if (isDelim(*next)) { ++next; continue; } if ((*next == '\"' && quoteType == QuoteType::Double) || (*next == '\'' && quoteType == QuoteType::Single)) { // Found the desired opening quote, switch to QUOTED ++next; _state = QUOTED; continue; } // Everything except delimiters or opening quotes indicates an error throw ParseException("Could not find opening double quote after backslash."); case FORWARDSLASH: // If we have a forward slash we may be entering a comment. The forward slash // will NOT YET have been added to the token, so we must add it manually if // this proves not to be a comment. switch (*next) { case '*': _state = COMMENT_DELIM; ++next; continue; case '/': _state = COMMENT_EOL; ++next; continue; default: // false alarm, add the slash and carry on _state = SEARCHING; tok += "/"; // Do not increment next here continue; } case COMMENT_DELIM: // Inside a delimited comment, we add nothing to the token but check for // the "*/" sequence. if (*next == '*') { _state = STAR; ++next; continue; } else { ++next; continue; // ignore and carry on } case COMMENT_EOL: // This comment lasts until the end of the line. if (*next == '\r' || *next == '\n') { _state = SEARCHING; ++next; // If we have a token after a line comment, return it if (tok != "") { return true; } else { continue; } } else { ++next; continue; // do nothing } case STAR: // The star may indicate the end of a delimited comment. // This state will only be entered if we are inside a // delimited comment. if (*next == '/') { // End of comment _state = SEARCHING; ++next; continue; } else if (*next == '*') { // Another star, remain in the STAR state in case we // have a "**/" end of comment. _state = STAR; ++next; continue; } else { // No end of comment _state = COMMENT_DELIM; ++next; continue; } } // end of state switch } // end of for loop // Return true if we have added anything to the token if (tok != "") return true; else return false; } }; // Represents a #DEFINE'd macro in a code file, which may have 0 to n arguments // #define MYDEF( y ) set "cmd" y; class Macro { public: Macro() {} Macro(const std::string& name_) : name(name_) {} // Name of the #DEFINE'd macro std::string name; // The arguments of this macro std::list arguments; // The macro body std::list tokens; }; class SingleCodeFileTokeniser : public DefTokeniser { private: // Istream iterator type typedef std::istream_iterator CharStreamIterator; // Internal tokeniser and its iterator typedef string::Tokeniser CharTokeniser; CharTokeniser _tok; CharTokeniser::Iterator _tokIter; private: // Helper function to set noskipws on the input stream. static std::istream& setNoskipws(std::istream& is) { is >> std::noskipws; return is; } public: /** * Construct a SingleCodeFileTokeniser with the given input stream, and optionally * a list of separators. * * @param str * The std::istream to tokenise. This is a non-const parameter, since tokens * will be extracted from the stream. * * @param delims * The list of characters to use as delimiters. * * @param keptDelims * String of characters to treat as delimiters but return as tokens in their * own right. */ SingleCodeFileTokeniser(std::istream& str, const char* delims = WHITESPACE, const char* keptDelims = "{}(),") : _tok(CharStreamIterator(setNoskipws(str)), // start iterator CharStreamIterator(), // end (null) iterator CodeTokeniserFunc(delims, keptDelims)), _tokIter(_tok.getIterator()) { } /** * Test if this StringTokeniser has more tokens to return. * * @returns * true if there are further tokens, false otherwise */ bool hasMoreTokens() const override { return !_tokIter.isExhausted(); } /** * Return the next token in the sequence. This function consumes * the returned token and advances the internal state to the following * token. * * @returns * std::string containing the next token in the sequence. * * @pre * hasMoreTokens() must be true, otherwise an exception will be thrown. */ std::string nextToken() override { if (hasMoreTokens()) { return *(_tokIter++); } throw ParseException("SingleCodeFileTokeniser: no more tokens"); } std::string peek() const override { if (hasMoreTokens()) { return *_tokIter; } throw ParseException("SingleCodeFileTokeniser: no more tokens"); } }; /** * High-level tokeniser taking a specific VFS file as input. * It is able to handle preprocessor statements like #include * by maintaining several child tokenisers. This can be used * to parse code-like files as Doom 3 Scripts or GUIs. * * Note: Don't expect this tokeniser to be particularly fast. */ class CodeTokeniser : public DefTokeniser { private: struct ParseNode { ArchiveTextFilePtr archive; std::istream inputStream; SingleCodeFileTokeniser tokeniser; ParseNode(const ArchiveTextFilePtr& archive_, const char* delims, const char* keptDelims) : archive(archive_), inputStream(&archive->getInputStream()), tokeniser(inputStream, delims, keptDelims) {} }; typedef std::shared_ptr ParseNodePtr; // The stack of child tokenisers typedef std::list NodeList; NodeList _nodes; NodeList::iterator _curNode; // A set of visited files to catch infinite include loops typedef std::list FileNameStack; FileNameStack _fileStack; typedef std::list StringList; // A map associating names to #define'd macros typedef std::map Macros; Macros _macros; // A small local buffer which is needed to properly resolve #define statements // which could consist of several tokens themselves StringList _tokenBuffer; const char* _delims; const char* _keptDelims; public: /** * Construct a CodeTokeniser with the given text file from the VFS. */ CodeTokeniser(const ArchiveTextFilePtr& file, const char* delims = " \t\n\v\r", const char* keptDelims = "{}(),;") : _delims(delims), _keptDelims(keptDelims) { _nodes.push_back(ParseNodePtr(new ParseNode(file, _delims, _keptDelims))); _curNode = _nodes.begin(); _fileStack.push_back(file->getName()); fillTokenBuffer(); } bool hasMoreTokens() const { return !_tokenBuffer.empty(); } std::string nextToken() { if (_tokenBuffer.empty()) { throw ParseException("No more tokens."); } std::string temp = _tokenBuffer.front(); _tokenBuffer.pop_front(); // Keep the buffer filled if (_tokenBuffer.empty()) { fillTokenBuffer(); } return temp; } std::string peek() const { if (_tokenBuffer.empty()) { throw ParseException("No more tokens."); } return _tokenBuffer.front(); } private: void fillTokenBuffer() { while (_curNode != _nodes.end()) { if (!(*_curNode)->tokeniser.hasMoreTokens()) { _fileStack.pop_back(); ++_curNode; continue; } std::string token = (*_curNode)->tokeniser.nextToken(); // Don't treat #strNNNN as preprocessor tokens if (!token.empty() && token[0] == '#' && !string::starts_with(token, "#str")) { // A pre-processor token is ahead handlePreprocessorToken(token); continue; } _tokenBuffer.push_front(token); // Found a non-preprocessor token, // check if this is matching a preprocessor definition Macros::const_iterator found = _macros.find(_tokenBuffer.front()); if (found != _macros.end()) { // Expand this macro, new tokens are acquired from the currently active tokeniser StringList expanded = expandMacro(found->second, [this]() { return (*_curNode)->tokeniser.nextToken(); }); if (!expanded.empty()) { // Replace the token in the buffer with the expanded string _tokenBuffer.pop_front(); _tokenBuffer.insert(_tokenBuffer.begin(), expanded.begin(), expanded.end()); } } return; // got a token } } // Expands the given macro, returns the beginning and end of a range StringList expandMacro(const Macro& macro, const std::function& nextTokenFunc) { StringList argumentValues; // Acquire the macro argument values if applicable if (!macro.arguments.empty()) { // Assert an opening parenthesis if (nextTokenFunc() != "(") { throw ParseException(fmt::format("Error expanding macro {0}, expected '('", macro.name)); } for (StringList::const_iterator i = macro.arguments.begin(); i != macro.arguments.end(); ++i) { std::string argumentToken = nextTokenFunc(); std::string argumentValue = ""; while (argumentToken != "," && argumentToken != ")") { argumentValue += argumentToken; argumentValue.append(" "); argumentToken = nextTokenFunc(); } argumentValues.push_back(argumentValue); } } // Allocate a new list for the expanded tokens StringList expandedTokens; // Insert the macro contents into the buffer, expanding sub-macros along the way for (StringList::const_iterator t = macro.tokens.begin(); t != macro.tokens.end(); ++t) { // Replace macro variable with its value std::string token = getMacroToken(*t, macro, argumentValues); // check if this is matching a preprocessor definition Macros::const_iterator found = _macros.find(token); if (found == _macros.end()) { // Not a macro expandedTokens.push_back(token); continue; } // Enter recursion to expand this sub-macro, new tokens are acquired from the current iterator t StringList subMacro = expandMacro(found->second, [&]() { if (t == macro.tokens.end()) { throw ParseException(fmt::format("Running out of tokens expanding sub-macro {0}", token)); } // Advance iterator and dereference // When delivering tokens to sub-macro expansions we still need to replace any argument values return getMacroToken(*(++t), macro, argumentValues); }); if (!subMacro.empty()) { // Insert the expanded macro contents, don't insert *t itself expandedTokens.insert(expandedTokens.end(), subMacro.begin(), subMacro.end()); } else { rWarning() << "Macro expansion yields empty token list: " << *t << " in " << (*_curNode)->archive->getName() << std::endl; } } return expandedTokens; } std::string getMacroToken(std::string token, const Macro& macro, const StringList& argumentValues) { // Check if the current token is referring to a macro argument and replace it with its value for (StringList::const_iterator arg = macro.arguments.begin(), val = argumentValues.begin(); arg != macro.arguments.end() && val != argumentValues.end(); ++arg, ++val) { if (token == *arg) { return *val; } } return token; // leave token unchanged } void handlePreprocessorToken(const std::string& token) { if (token == "#include") { std::string includeFile = (*_curNode)->tokeniser.nextToken(); ArchiveTextFilePtr file = GlobalFileSystem().openTextFile(includeFile); if (file != NULL) { // Catch infinite recursions FileNameStack::const_iterator found = std::find(_fileStack.begin(), _fileStack.end(), file->getName()); if (found == _fileStack.end()) { // Push a new parse node and switch _fileStack.push_back(file->getName()); _curNode = _nodes.insert( _curNode, ParseNodePtr(new ParseNode(file, _delims, _keptDelims)) ); } else { rError() << "Caught infinite loop on parsing #include token: " << includeFile << " in " << (*_curNode)->archive->getName() << std::endl; } } else { rWarning() << "Couldn't find include file: " << includeFile << " in " << (*_curNode)->archive->getName() << std::endl; } } else if (string::starts_with(token, "#define")) { parseMacro(token); } else if (token == "#undef") { std::string key = (*_curNode)->tokeniser.nextToken(); _macros.erase(key); } else if (token == "#ifdef") { std::string key = (*_curNode)->tokeniser.nextToken(); Macros::const_iterator found = _macros.find(key); if (found == _macros.end()) { skipInactivePreprocessorBlock(); } } else if (token == "#ifndef") { Macros::const_iterator found = _macros.find( (*_curNode)->tokeniser.nextToken()); if (found != _macros.end()) { skipInactivePreprocessorBlock(); } } else if (token == "#else") { // We have an #else during active parsing, an inactive preprocessor block is ahead skipInactivePreprocessorBlock(); } else if (token == "#if") { (*_curNode)->tokeniser.skipTokens(1); } } void parseMacro(const std::string& token) { std::string defineToken = token; if (defineToken.length() <= 7) { rWarning() << "Invalid #define statement in " << (*_curNode)->archive->getName() << std::endl; return; } // Replace tabs with spaces std::replace(defineToken.begin(), defineToken.end(), '\t', ' '); // Cut off the "#define " (including space) defineToken = defineToken.substr(8); // Parse the entire macro std::istringstream macroStream(defineToken); SingleCodeFileTokeniser macroParser(macroStream); std::string name = macroParser.nextToken(); bool paramsStarted = false; if (string::ends_with(name, "(")) { string::trim_right(name, "("); paramsStarted = true; } std::pair result = _macros.insert( Macros::value_type(name, Macro(name)) ); if (!result.second) { rWarning() << "Redefinition of " << name << " in " << (*_curNode)->archive->getName() << std::endl; result.first->second = Macro(name); } Macro& macro = result.first->second; while (macroParser.hasMoreTokens()) { std::string macroToken = macroParser.nextToken(); // An opening parenthesis might be an argument list, but // only if we're still at the beginning of the macro if (macroToken == "(" && !paramsStarted && macro.tokens.empty()) { paramsStarted = true; } else if (macroToken == ")" && paramsStarted) { paramsStarted = false; } else if (macroToken == ",") { if (paramsStarted) { continue; } // Treat the comma as part of the macro value macro.tokens.push_back(macroToken); } else { if (paramsStarted) { // Token is an argument macro.arguments.push_back(macroToken); } else { // Ordinary macro value macro.tokens.push_back(macroToken); } } } } void skipInactivePreprocessorBlock() { // Not defined, skip everything until matching #endif for (std::size_t level = 1; level > 0;) { if (!(*_curNode)->tokeniser.hasMoreTokens()) { rWarning() << "No matching #endif for #if(n)def in " << (*_curNode)->archive->getName() << std::endl; } std::string token = (*_curNode)->tokeniser.nextToken(); if (token == "#endif") { level--; } else if (token == "#ifdef" || token == "#ifndef" || token == "#if") { level++; } else if (token == "#else" && level == 1) { // Matching #else, break the loop break; } } } }; } // namespace parser �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/parser/DefBlockTokeniser.h���������������������������������������������������0000664�0000000�0000000�00000027727�13217505464�0021663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once #include "ParseException.h" #include #include #include #include #include "string/tokeniser.h" namespace parser { /** * Abstract base class BlockTokeniser. This class inspects a given input block * or stream and returns definition blocks (including name). * * C and C++-style comments are properly ignored. */ class BlockTokeniser { public: struct Block { // The name of this block std::string name; // The block contents (excluding braces) std::string contents; void clear() { name.clear(); contents.clear(); } }; /** Destructor */ virtual ~BlockTokeniser() {} /** * Test if this DefTokeniser has more blocks to return. * * @returns * true if there are further blocks, false otherwise */ virtual bool hasMoreBlocks() = 0; /** * Return the next block in the sequence. This function consumes * the returned block and advances the internal state to the following * block. * * @returns * A named Block structure. * * @pre * hasMoreBlocks() must be true, otherwise an exception will be thrown. */ virtual Block nextBlock() = 0; }; /* Model of a tokeniser function which splits tokens on whitespace with additional * protection of quoted content. */ class DefBlockTokeniserFunc { // Enumeration of states enum State { SEARCHING_NAME, // haven't found anything yet TOKEN_STARTED, // first non-delimiter character found SEARCHING_BLOCK, // searching for block opening char BLOCK_CONTENT, // within a block FORWARDSLASH, // forward slash found, possible comment coming COMMENT_EOL, // double-forwardslash comment COMMENT_DELIM, // inside delimited comment (/*) STAR // asterisk, possibly indicates end of comment (*/) } _state; const char* _delims; // whitespace const char _blockStartChar; // "{" const char _blockEndChar; // "}" // Test if a character is a delimiter bool isDelim(char c) { const char* curDelim = _delims; while (*curDelim != 0) { if (*(curDelim++) == c) { return true; } } return false; } public: // Constructor DefBlockTokeniserFunc(const char* delims, char blockStartChar, char blockEndChar) : _state(SEARCHING_NAME), _delims(delims), _blockStartChar(blockStartChar), _blockEndChar(blockEndChar) {} /* REQUIRED. Operator() is called by the Tokeniser. This function * must search for a token between the two iterators next and end, and if * a token is found, set tok to the token, set next to position to start * parsing on the next call, and return true. */ template bool operator() (InputIterator& next, const InputIterator& end, BlockTokeniser::Block& tok) { // Initialise state, no persistence between calls _state = SEARCHING_NAME; // Clear out the token, no guarantee that it is empty tok.clear(); char ch = '\0'; std::size_t blockLevel = 0; while (next != end) { ch = *next; switch (_state) { case SEARCHING_NAME: // Ignore delimiters if (isDelim(ch)) { ++next; continue; } // We have a non-delimiter, examine it _state = TOKEN_STARTED; // Fall through case TOKEN_STARTED: // Here a delimiter indicates a successful token match if (isDelim(ch)) { _state = SEARCHING_BLOCK; continue; } // The character is pointing at a non-delimiter. Switch on it. switch (ch) { // Found a slash, possibly start of comment case '/': _state = FORWARDSLASH; ++next; continue; // skip slash, will need to add it back if this is not a comment // General case. Token lasts until next delimiter. default: tok.name += ch; ++next; continue; } break; case SEARCHING_BLOCK: if (isDelim(ch)) { ++next; // keep on searching continue; } else if (ch == _blockStartChar) { // Found an opening brace _state = BLOCK_CONTENT; blockLevel++; ++next; continue; } else if (ch == '/') { // Forward slash, possible comment start _state = FORWARDSLASH; ++next; continue; } else { // Not a delimiter, not an opening brace, must be // an "extension" for the name tok.name += ' '; tok.name += ch; // Switch back to name _state = TOKEN_STARTED; ++next; continue; } case BLOCK_CONTENT: // Check for another opening brace if (ch == _blockEndChar) { blockLevel--; if (blockLevel == 0) { // End of block content, we're done here, // don't add this last character either ++next; return true; } else { // Still within a block, add to contents tok.contents += ch; ++next; continue; } } else if (ch == _blockStartChar) { // another block within this block, ignore this blockLevel++; tok.contents += ch; ++next; continue; } else { tok.contents += ch; ++next; continue; } case FORWARDSLASH: // If we have a forward slash we may be entering a comment. The forward slash // will NOT YET have been added to the token, so we must add it manually if // this proves not to be a comment. switch (ch) { case '*': _state = COMMENT_DELIM; ++next; continue; case '/': _state = COMMENT_EOL; ++next; continue; default: // false alarm, add the slash and carry on _state = TOKEN_STARTED; tok.name += '/'; // Do not increment next here continue; } case COMMENT_DELIM: // Inside a delimited comment, we add nothing to the token but check for // the "*/" sequence. if (ch == '*') { _state = STAR; ++next; continue; } else { ++next; continue; // ignore and carry on } case COMMENT_EOL: // This comment lasts until the end of the line. if (ch == '\r' || ch == '\n') { // An EOL comment with non-empty name means searching for block _state = (tok.name.empty()) ? SEARCHING_NAME : SEARCHING_BLOCK; ++next; continue; } else { ++next; continue; // do nothing } case STAR: // The star may indicate the end of a delimited comment. // This state will only be entered if we are inside a // delimited comment. if (ch == '/') { // End of comment _state = (tok.name.empty()) ? SEARCHING_NAME : SEARCHING_BLOCK; ++next; continue; } else if (ch == '*') { // Another star, remain in the STAR state in case we // have a "**/" end of comment. _state = STAR; ++next; continue; } else { // No end of comment _state = COMMENT_DELIM; ++next; continue; } } } // Return true if we have found a named block return !tok.name.empty(); } }; /** * Tokenise a DEF file. * * This class provides a similar interface to Java's StringTokenizer class. It accepts * an input stream and provides a simple interface to return the next block in the stream. * It also protects quoted content and ignores both C and C++ style comments. */ template class BasicDefBlockTokeniser : public BlockTokeniser { private: // Internal tokeniser and its iterator typedef string::Tokeniser Tokeniser; Tokeniser _tok; Tokeniser::Iterator _tokIter; public: /** * Construct a BasicDefBlockTokeniser with the given input type. * * @param str * The container to tokenise. */ BasicDefBlockTokeniser(const ContainerT& str, const char* delims = " \t\n\v\r", const char blockStartChar = '{', const char blockEndChar = '}') : _tok(str, DefBlockTokeniserFunc(delims, blockStartChar, blockEndChar)), _tokIter(_tok.getIterator()) {} /** Test if this StringTokeniser has more blocks to return. * * @returns * true if there are further blocks, false otherwise */ bool hasMoreBlocks() override { return !_tokIter.isExhausted(); } /** Return the next token in the sequence. This function consumes * the returned token and advances the internal state to the following * token. * * @returns * std::string containing the next token in the sequence. * * @pre * hasMoreTokens() must be true, otherwise an exception will be thrown. */ Block nextBlock() override { if (hasMoreBlocks()) { return *(_tokIter++); } throw ParseException("BlockTokeniser: no more blocks"); } }; /** * Specialisation of DefTokeniser to work with std::istream objects. This is * needed because an std::istream does not provide begin() and end() methods * to get an iterator, but needs a separate istream_iterator<> to be constructed * for it. */ template<> class BasicDefBlockTokeniser : public BlockTokeniser { private: // Istream iterator type typedef std::istream_iterator CharStreamIterator; // Internal tokeniser and its iterator typedef string::Tokeniser Tokeniser; Tokeniser _tok; Tokeniser::Iterator _tokIter; // Helper function to set noskipws on the input stream. static std::istream& setNoskipws(std::istream& is) { is >> std::noskipws; return is; } public: /** * Construct a BasicDefBlockTokeniser with the given input stream. * * @param str * The std::istream to tokenise. This is a non-const parameter, since tokens * will be extracted from the stream. */ BasicDefBlockTokeniser(std::istream& str, const char* delims = " \t\n\v\r", const char blockStartChar = '{', const char blockEndChar = '}') : _tok(CharStreamIterator(setNoskipws(str)), // start iterator CharStreamIterator(), // end (null) iterator DefBlockTokeniserFunc(delims, blockStartChar, blockEndChar)), _tokIter(_tok.getIterator()) {} /** * Test if this BlockTokeniser has more blocks to return. * * @returns * true if there are further blocks, false otherwise */ bool hasMoreBlocks() override { return !_tokIter.isExhausted(); } /** * Return the next block in the sequence. This function consumes * the returned block and advances the internal state to the following * block. * * @returns * The named block. * * @pre * hasMoreBlocks() must be true, otherwise an exception will be thrown. */ Block nextBlock() override { if (hasMoreBlocks()) { return *(_tokIter++); } throw ParseException("BlockTokeniser: no more tokens"); } }; } // namespace parser �����������������������������������������DarkRadiant-2.5.0/libs/parser/DefTokeniser.h��������������������������������������������������������0000664�0000000�0000000�00000043075�13217505464�0020702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once #include "ParseException.h" #include #include #include #include #include "string/tokeniser.h" namespace parser { /* TokenizerFunction which splits tokens on whitespace with additional * protection of quoted content. */ class DefTokeniserFunc { // Enumeration of states enum { SEARCHING, // haven't found anything yet TOKEN_STARTED, // found the start of a possible multi-char token QUOTED, // inside quoted text, no tokenising AFTER_CLOSING_QUOTE, // right after a quoted text, checking for backslash SEARCHING_FOR_QUOTE, // searching for continuation of quoted string (after a backslash was found) FORWARDSLASH, // forward slash found, possible comment coming COMMENT_EOL, // double-forwardslash comment COMMENT_DELIM, // inside delimited comment (/*) STAR // asterisk, possibly indicates end of comment (*/) } _state; // List of delimiters to skip const char* _delims; // List of delimiters to keep const char* _keptDelims; // Test if a character is a delimiter bool isDelim(char c) { const char* curDelim = _delims; while (*curDelim != 0) { if (*(curDelim++) == c) { return true; } } return false; } // Test if a character is a kept delimiter bool isKeptDelim(char c) { const char* curDelim = _keptDelims; while (*curDelim != 0) { if (*(curDelim++) == c) { return true; } } return false; } public: // Constructor DefTokeniserFunc(const char* delims, const char* keptDelims) : _state(SEARCHING), _delims(delims), _keptDelims(keptDelims) {} /* REQUIRED. Operator() is called by the tokeniser. This function * must search for a token between the two iterators next and end, and if * a token is found, set tok to the token, set next to position to start * parsing on the next call, and return true. */ template bool operator() (InputIterator& next, const InputIterator& end, std::string& tok) { // Initialise state, no persistence between calls _state = SEARCHING; // Clear out the token, no guarantee that it is empty tok = ""; while (next != end) { switch (_state) { case SEARCHING: // If we have a delimiter, just advance to the next character if (isDelim(*next)) { ++next; continue; } // If we have a KEPT delimiter, this is the token to return. if (isKeptDelim(*next)) { tok = *(next++); return true; } // Otherwise fall through into TOKEN_STARTED, saving the state for the // next iteration _state = TOKEN_STARTED; case TOKEN_STARTED: // Here a delimiter indicates a successful token match if (isDelim(*next) || isKeptDelim(*next)) { return true; } // Now next is pointing at a non-delimiter. Switch on this // character. switch (*next) { // Found a quote, enter QUOTED state, or return the // current token if we are in the process of building // one. case '\"': if (tok != "") { return true; } else { _state = QUOTED; ++next; continue; // skip the quote } // Found a slash, possibly start of comment case '/': _state = FORWARDSLASH; ++next; continue; // skip slash, will need to add it back if this is not a comment // General case. Token lasts until next delimiter. default: tok += *next; ++next; continue; } case QUOTED: // In the quoted state, just advance until the closing // quote. No delimiter splitting is required. if (*next == '\"') { ++next; // greebo: We've found a closing quote, but there might be a backslash indicating // a multi-line string constant "" \ "", so switch to AFTER_CLOSING_QUOTE mode _state = AFTER_CLOSING_QUOTE; continue; } else if (*next == '\\') { // Escape found, check next character ++next; if (next != end) { if (*next == 'n') // Linebreak { tok += '\n'; } else if (*next == 't') // Tab { tok += '\t'; } else if (*next == '"') // Quote { tok += '"'; } else { // No special escape sequence, add the backslash tok += '\\'; // Plus the character itself tok += *next; } ++next; } continue; } else { tok += *next; ++next; continue; } case AFTER_CLOSING_QUOTE: // We already have a valid string token in our hands, but it might be continued // if one of the next tokens is a backslash if (*next == '\\') { // We've found a backslash right after a closing quote, this indicates we could // proceed with parsing quoted content ++next; _state = SEARCHING_FOR_QUOTE; continue; } // Ignore delimiters if (isDelim(*next)) { ++next; continue; } // Everything except delimiters and backslashes indicates that // the quoted content is not continued, so break the loop. // This returns the token and parsing continues. // Return TRUE in any case, even if the parsed token is empty (""). return true; case SEARCHING_FOR_QUOTE: // We have found a backslash after a closing quote, search for an opening quote // Step over delimiters if (isDelim(*next)) { ++next; continue; } if (*next == '\"') { // Found the desired opening quote, switch to QUOTED ++next; _state = QUOTED; continue; } // Everything except delimiters or opening quotes indicates an error throw ParseException("Could not find opening double quote after backslash."); case FORWARDSLASH: // If we have a forward slash we may be entering a comment. The forward slash // will NOT YET have been added to the token, so we must add it manually if // this proves not to be a comment. switch (*next) { case '*': _state = COMMENT_DELIM; ++next; continue; case '/': _state = COMMENT_EOL; ++next; continue; default: // false alarm, add the slash and carry on // greebo: switch to TOKEN_STARTED, the SEARCHING state might // overwrite this if the next token is a kept delimiter _state = TOKEN_STARTED; tok += "/"; // Do not increment next here continue; } case COMMENT_DELIM: // Inside a delimited comment, we add nothing to the token but check for // the "*/" sequence. if (*next == '*') { _state = STAR; ++next; continue; } else { ++next; continue; // ignore and carry on } case COMMENT_EOL: // This comment lasts until the end of the line. if (*next == '\r' || *next == '\n') { ++next; // If we have a token at this point, return it if (tok != "") { return true; } else { _state = SEARCHING; continue; } } else { ++next; continue; // do nothing } case STAR: // The star may indicate the end of a delimited comment. // This state will only be entered if we are inside a // delimited comment. if (*next == '/') { // End of comment ++next; // If we have a token at this point, return it if (tok != "") { return true; } else { _state = SEARCHING; continue; } continue; } else if (*next == '*') { // Another star, remain in the STAR state in case we // have a "**/" end of comment. _state = STAR; ++next; continue; } else { // No end of comment _state = COMMENT_DELIM; ++next; continue; } } // end of state switch } // end of for loop // Return true if we have added anything to the token if (tok != "") return true; else return false; } }; const char* const WHITESPACE = " \t\n\v\r"; /** * DefTokeniser abstract base class. This class provides a unified interface to * DefTokeniser subclasses, so that calling code can retrieve a stream of tokens * without needing knowledge of the underlying container type (std::string, * std::istream etc). * * Each subclass MUST implement hasMoreTokens() and nextToken() appropriately, * while default implementations of assertNextToken() and skipTokens() are * provided that make use of the former two methods. */ class DefTokeniser { public: /** * Destructor */ virtual ~DefTokeniser() {} /** * Test if this DefTokeniser has more tokens to return. * * @returns * true if there are further tokens, false otherwise */ virtual bool hasMoreTokens() const = 0; /** * Return the next token in the sequence. This function consumes * the returned token and advances the internal state to the following * token. * * @returns * std::string containing the next token in the sequence. * * @pre * hasMoreTokens() must be true, otherwise an exception will be thrown. */ virtual std::string nextToken() = 0; /** * Assert that the next token in the sequence must be equal to the provided * value. A ParseException is thrown if the assert fails. * * @param val * The expected value of the token. */ virtual void assertNextToken(const std::string& val) { const std::string tok = nextToken(); if (tok != val) throw ParseException("DefTokeniser: Assertion failed: Required \"" + val + "\", found \"" + tok + "\""); } /** * Skip the next n tokens. This method provides a convenient way to dispose * of a number of tokens without returning them. * * @param n * The number of tokens to consume. */ virtual void skipTokens(unsigned int n) { for (unsigned int i = 0; i < n; i++) { nextToken(); } } /** * Returns the next token without incrementing the internal * iterator. Use this if you want to take a look at what is coming * next without actually changing the tokeniser's state. */ virtual std::string peek() const = 0; }; /** * Tokenise a DEF file. * * This class provides a similar interface to Java's StringTokenizer class. It accepts * and std::string and a list of separators, and provides a simple interface to return * the next token in the string. It also protects quoted content and ignores both C and * C++ style comments. */ template class BasicDefTokeniser : public DefTokeniser { // Internal tokenizer and its iterator typedef string::Tokeniser CharTokeniser; CharTokeniser _tok; CharTokeniser::Iterator _tokIter; public: /** * Construct a DefTokeniser with the given input type, and optionally * a list of separators. * * @param str * The container to tokenise. * * @param delims * The list of characters to use as delimiters. * * @param keptDelims * String of characters to treat as delimiters but return as tokens in their * own right. */ BasicDefTokeniser(const ContainerT& str, const char* delims = WHITESPACE, const char* keptDelims = "{}()") : _tok(str, DefTokeniserFunc(delims, keptDelims)), _tokIter(_tok.getIterator()) { } /** Test if this StringTokeniser has more tokens to return. * * @returns * true if there are further tokens, false otherwise */ bool hasMoreTokens() const override { return !_tokIter.isExhausted(); } /** Return the next token in the sequence. This function consumes * the returned token and advances the internal state to the following * token. * * @returns * std::string containing the next token in the sequence. * * @pre * hasMoreTokens() must be true, otherwise an exception will be thrown. */ std::string nextToken() override { if (hasMoreTokens()) { return *(_tokIter++); } throw ParseException("DefTokeniser: no more tokens"); } /** * Returns the next token without incrementing the internal * iterator. Use this if you want to take a look at what is coming * next without actually changing the tokeniser's state. */ std::string peek() const override { if (hasMoreTokens()) { return *_tokIter; } throw ParseException("DefTokeniser: no more tokens"); } }; /** * Specialisation of DefTokeniser to work with std::istream objects. This is * needed because an std::istream does not provide begin() and end() methods * to get an iterator, but needs a separate istream_iterator<> to be constructed * for it. */ template<> class BasicDefTokeniser : public DefTokeniser { private: // Istream iterator type typedef std::istream_iterator CharStreamIterator; // Internal tokenizer and its iterator typedef string::Tokeniser CharTokeniser; CharTokeniser _tok; CharTokeniser::Iterator _tokIter; // Helper function to set noskipws on the input stream. static std::istream& setNoskipws(std::istream& is) { is >> std::noskipws; return is; } public: /** * Construct a DefTokeniser with the given input stream, and optionally * a list of separators. * * @param str * The std::istream to tokenise. This is a non-const parameter, since tokens * will be extracted from the stream. * * @param delims * The list of characters to use as delimiters. * * @param keptDelims * String of characters to treat as delimiters but return as tokens in their * own right. */ BasicDefTokeniser(std::istream& str, const char* delims = WHITESPACE, const char* keptDelims = "{}()") : _tok(CharStreamIterator(setNoskipws(str)), // start iterator CharStreamIterator(), // end (null) iterator DefTokeniserFunc(delims, keptDelims)), _tokIter(_tok.getIterator()) { } /** * Test if this StringTokeniser has more tokens to return. * * @returns * true if there are further tokens, false otherwise */ bool hasMoreTokens() const override { return !_tokIter.isExhausted(); } /** * Return the next token in the sequence. This function consumes * the returned token and advances the internal state to the following * token. * * @returns * std::string containing the next token in the sequence. * * @pre * hasMoreTokens() must be true, otherwise an exception will be thrown. */ std::string nextToken() override { if (hasMoreTokens()) { return *(_tokIter++); } throw ParseException("DefTokeniser: no more tokens"); } /** * Returns the next token without incrementing the internal * iterator. Use this if you want to take a look at what is coming * next without actually changing the tokeniser's state. */ std::string peek() const override { if (hasMoreTokens()) { return *_tokIter; } throw ParseException("DefTokeniser: no more tokens"); } }; } // namespace parser �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/parser/ParseException.h������������������������������������������������������0000664�0000000�0000000�00000000455�13217505464�0021244�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once #include #include namespace parser { /** Exception raised by parser classes when an unrecoverable error occurs. */ class ParseException : public std::runtime_error { public: ParseException(const std::string& what) : std::runtime_error(what) {} }; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/parser/Tokeniser.h�����������������������������������������������������������0000664�0000000�0000000�00000010112�13217505464�0020245�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once #include #include "string/tokeniser.h" #include "ParseException.h" namespace parser { /** * greebo: Abstract type of a StringTokeniser, which splits a given * input string into pieces. It must provide a basic set of methods * to retrieve the tokens one by one. */ class StringTokeniser { public: /** * Destructor */ virtual ~StringTokeniser() {} /** Test if this StringTokeniser has more tokens to return. * * @returns * true if there are further tokens, false otherwise */ virtual bool hasMoreTokens() = 0; /** Return the next token in the sequence. This function consumes * the returned token and advances the internal state to the following * token. * * @returns * std::string containing the next token in the sequence. * * @pre * hasMoreTokens() must be true, otherwise an exception will be thrown. */ virtual std::string nextToken() = 0; /** Assert that the next token in the sequence must be equal to the provided * value. A ParseException is thrown if the assert fails. * * @param val * The expected value of the token. */ virtual void assertNextToken(const std::string& val) = 0; /** Skip the next n tokens. This method provides a convenient way to dispose * of a number of tokens without returning them. * * @param n * The number of tokens to consume. */ virtual void skipTokens(unsigned int n) = 0; }; /** Base class of a tokeniser wrapping around a string::tokeniser * * Standard delimiters are initialised to whitespace: " \t\n\v\r" */ class BasicStringTokeniser : public StringTokeniser { private: // Internal tokenizer helper typedef string::CharTokeniserFunc CharSeparator; typedef string::Tokeniser CharTokeniser; CharSeparator _separator; CharTokeniser _tok; CharTokeniser::Iterator _tokIter; public: /** Construct a Tokeniser with the given input string, and optionally * a list of separators. * * @param str * The string to tokenise. * * @param delims * The list of characters to use as delimiters. * * @param keptDelims * The list of delimiters to keep */ BasicStringTokeniser(const std::string& str, const char* delimiters = " \t\n\v\r") : _separator(delimiters), _tok(str, _separator), _tokIter(_tok.getIterator()) {} /** Test if this StringTokeniser has more tokens to return. * * @returns * true if there are further tokens, false otherwise */ bool hasMoreTokens() override { return !_tokIter.isExhausted(); } /** Return the next token in the sequence. This function consumes * the returned token and advances the internal state to the following * token. * * @returns * std::string containing the next token in the sequence. * * @pre * hasMoreTokens() must be true, otherwise an exception will be thrown. */ std::string nextToken() override { if (hasMoreTokens()) { return *(_tokIter++); } throw ParseException("Tokeniser: no more tokens"); } /** Assert that the next token in the sequence must be equal to the provided * value. A ParseException is thrown if the assert fails. * * @param val * The expected value of the token. */ void assertNextToken(const std::string& val) override { const std::string tok = nextToken(); if (tok != val) throw ParseException("Tokeniser: Assertion failed: Required \"" + val + "\", found \"" + tok + "\""); } /** Skip the next n tokens. This method provides a convenient way to dispose * of a number of tokens without returning them. * * @param n * The number of tokens to consume. */ void skipTokens(unsigned int n) override { for (unsigned int i = 0; i < n; i++) { if (hasMoreTokens()) { _tokIter++; } throw ParseException("Tokeniser: no more tokens"); } } }; // class BasicStringTokeniser } // namespace parser ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel.h������������������������������������������������������������������0000664�0000000�0000000�00000032141�13217505464�0016767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #ifndef PICOMODEL_H #define PICOMODEL_H #ifdef __cplusplus extern "C" { #endif #include /* version */ #define PICOMODEL_VERSION "0.8.20" /* constants */ #define PICO_GROW_SHADERS 16 #define PICO_GROW_SURFACES 16 #define PICO_GROW_VERTEXES 1024 #define PICO_GROW_INDEXES 1024 #define PICO_GROW_ARRAYS 8 #define PICO_GROW_FACES 256 #define PICO_MAX_SPECIAL 8 #define PICO_MAX_DEFAULT_EXTS 4 /* max default extensions per module */ /* types */ typedef unsigned char picoByte_t; typedef double picoVec_t; typedef double picoVec2_t[ 2 ]; typedef double picoVec3_t[ 3 ]; typedef double picoVec4_t[ 4 ]; typedef picoByte_t picoColor_t[ 4 ]; typedef int picoIndex_t; typedef enum { PICO_BAD, PICO_TRIANGLES, PICO_PATCH } picoSurfaceType_t; typedef enum { PICO_NORMAL, PICO_VERBOSE, PICO_WARNING, PICO_ERROR, PICO_FATAL } picoPrintLevel_t; typedef struct picoSurface_s picoSurface_t; typedef struct picoShader_s picoShader_t; typedef struct picoModel_s picoModel_t; typedef struct picoModule_s picoModule_t; struct picoSurface_s { void *data; picoModel_t *model; /* owner model */ picoSurfaceType_t type; char *name; /* sea: surface name */ picoShader_t *shader; /* ydnar: changed to ptr */ int numVertexes, maxVertexes; picoVec3_t *xyz; picoVec3_t *normal; picoIndex_t *smoothingGroup; int numSTArrays, maxSTArrays; picoVec2_t **st; int numColorArrays, maxColorArrays; picoColor_t **color; int numIndexes, maxIndexes; picoIndex_t *index; int numFaceNormals, maxFaceNormals; picoVec3_t *faceNormal; int special[ PICO_MAX_SPECIAL ]; }; /* seaw0lf */ struct picoShader_s { picoModel_t *model; /* owner model */ char *name; /* shader name */ char *mapName; /* shader file name (name of diffuse texturemap) */ picoColor_t ambientColor; /* ambient color of mesh (rgba) */ picoColor_t diffuseColor; /* diffuse color of mesh (rgba) */ picoColor_t specularColor; /* specular color of mesh (rgba) */ float transparency; /* transparency (0..1; 1 = 100% transparent) */ float shininess; /* shininess (0..128; 128 = 100% shiny) */ }; struct picoModel_s { void *data; char *name; /* model name */ char *fileName; /* sea: model file name */ int frameNum; /* sea: renamed to frameNum */ int numFrames; /* sea: number of frames */ picoVec3_t mins; picoVec3_t maxs; int numShaders, maxShaders; picoShader_t **shader; int numSurfaces, maxSurfaces; picoSurface_t **surface; const picoModule_t *module; /* sea */ }; /* seaw0lf */ /* return codes used by the validation callbacks; pmv is short */ /* for 'pico module validation'. everything >PICO_PMV_OK means */ /* that there was an error. */ enum { PICO_PMV_OK, /* file valid */ PICO_PMV_ERROR, /* file not valid */ PICO_PMV_ERROR_IDENT, /* unknown file magic (aka ident) */ PICO_PMV_ERROR_VERSION, /* unsupported file version */ PICO_PMV_ERROR_SIZE, /* file size error */ PICO_PMV_ERROR_MEMORY, /* out of memory error */ }; /* convenience (makes it easy to add new params to the callbacks) */ #define PM_PARAMS_CANLOAD \ char *fileName, const void *buffer, int bufSize #define PM_PARAMS_LOAD \ char *fileName, int frameNum, const void *buffer, int bufSize #define PM_PARAMS_CANSAVE \ void #define PM_PARAMS_SAVE \ char *fileName, picoModel_t *model /* pico file format module structure */ struct picoModule_s { char *version; /* internal module version (e.g. '1.5-b2') */ char *displayName; /* string used to display in guis, etc. */ char *authorName; /* author name (eg. 'My Real Name') */ char *copyright; /* copyright year and holder (eg. '2002 My Company') */ char *defaultExts[ PICO_MAX_DEFAULT_EXTS ]; /* default file extensions used by this file type */ int (*canload)( PM_PARAMS_CANLOAD ); /* checks whether module can load given file (returns PMVR_*) */ picoModel_t *(*load)( PM_PARAMS_LOAD ); /* parses model file data */ int (*cansave)( PM_PARAMS_CANSAVE ); /* checks whether module can save (returns 1 or 0 and might spit out a message) */ int (*save)( PM_PARAMS_SAVE ); /* saves a pico model in module's native model format */ }; /* general functions */ int PicoInit( void ); void PicoShutdown( void ); int PicoError( void ); void PicoSetMallocFunc( void *(*func)( size_t ) ); void PicoSetFreeFunc( void (*func)( void* ) ); void PicoSetLoadFileFunc( void (*func)( char*, unsigned char**, int* ) ); void PicoSetFreeFileFunc( void (*func)( void* ) ); void PicoSetPrintFunc( void (*func)( int, const char* ) ); const picoModule_t **PicoModuleList( int *numModules ); picoModel_t *PicoLoadModel( char *name, int frameNum ); typedef size_t (*PicoInputStreamReadFunc)(void* inputStream, unsigned char* buffer, size_t length); picoModel_t* PicoModuleLoadModelStream( const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum ); /* model functions */ picoModel_t *PicoNewModel( void ); void PicoFreeModel( picoModel_t *model ); int PicoAdjustModel( picoModel_t *model, int numShaders, int numSurfaces ); /* shader functions */ picoShader_t *PicoNewShader( picoModel_t *model ); void PicoFreeShader( picoShader_t *shader ); picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive ); /* surface functions */ picoSurface_t *PicoNewSurface( picoModel_t *model ); void PicoFreeSurface( picoSurface_t *surface ); picoSurface_t *PicoFindSurface( picoModel_t *model, char *name, int caseSensitive ); int PicoAdjustSurface( picoSurface_t *surface, int numVertexes, int numSTArrays, int numColorArrays, int numIndexes, int numFaceNormals ); /* setter functions */ void PicoSetModelName( picoModel_t *model, char *name ); void PicoSetModelFileName( picoModel_t *model, char *fileName ); void PicoSetModelFrameNum( picoModel_t *model, int frameNum ); void PicoSetModelNumFrames( picoModel_t *model, int numFrames ); void PicoSetModelData( picoModel_t *model, void *data ); void PicoSetShaderName( picoShader_t *shader, char *name ); void PicoSetShaderMapName( picoShader_t *shader, char *mapName ); void PicoSetShaderAmbientColor( picoShader_t *shader, picoColor_t color ); void PicoSetShaderDiffuseColor( picoShader_t *shader, picoColor_t color ); void PicoSetShaderSpecularColor( picoShader_t *shader, picoColor_t color ); void PicoSetShaderTransparency( picoShader_t *shader, float value ); void PicoSetShaderShininess( picoShader_t *shader, float value ); void PicoSetSurfaceData( picoSurface_t *surface, void *data ); void PicoSetSurfaceType( picoSurface_t *surface, picoSurfaceType_t type ); void PicoSetSurfaceName( picoSurface_t *surface, char *name ); void PicoSetSurfaceShader( picoSurface_t *surface, picoShader_t *shader ); void PicoSetSurfaceXYZ( picoSurface_t *surface, int num, picoVec3_t xyz ); void PicoSetSurfaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ); void PicoSetSurfaceST( picoSurface_t *surface, int array, int num, picoVec2_t st ); void PicoSetSurfaceColor( picoSurface_t *surface, int array, int num, picoColor_t color ); void PicoSetSurfaceIndex( picoSurface_t *surface, int num, picoIndex_t index ); void PicoSetSurfaceIndexes( picoSurface_t *surface, int num, picoIndex_t *index, int count ); void PicoSetFaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ); void PicoSetSurfaceSpecial( picoSurface_t *surface, int num, int special ); void PicoSetSurfaceSmoothingGroup( picoSurface_t *surface, int num, picoIndex_t smoothingGroup ); /* getter functions */ char *PicoGetModelName( picoModel_t *model ); char *PicoGetModelFileName( picoModel_t *model ); int PicoGetModelFrameNum( picoModel_t *model ); int PicoGetModelNumFrames( picoModel_t *model ); void *PicoGetModelData( picoModel_t *model ); int PicoGetModelNumShaders( picoModel_t *model ); picoShader_t *PicoGetModelShader( picoModel_t *model, int num ); /* sea */ int PicoGetModelNumSurfaces( picoModel_t *model ); picoSurface_t *PicoGetModelSurface( picoModel_t *model, int num ); int PicoGetModelTotalVertexes( picoModel_t *model ); int PicoGetModelTotalIndexes( picoModel_t *model ); char *PicoGetShaderName( picoShader_t *shader ); char *PicoGetShaderMapName( picoShader_t *shader ); picoByte_t *PicoGetShaderAmbientColor( picoShader_t *shader ); picoByte_t *PicoGetShaderDiffuseColor( picoShader_t *shader ); picoByte_t *PicoGetShaderSpecularColor( picoShader_t *shader ); float PicoGetShaderTransparency( picoShader_t *shader ); float PicoGetShaderShininess( picoShader_t *shader ); void *PicoGetSurfaceData( picoSurface_t *surface ); char *PicoGetSurfaceName( picoSurface_t *surface ); /* sea */ picoSurfaceType_t PicoGetSurfaceType( picoSurface_t *surface ); char *PicoGetSurfaceName( picoSurface_t *surface ); picoShader_t *PicoGetSurfaceShader( picoSurface_t *surface ); /* sea */ int PicoGetSurfaceNumVertexes( picoSurface_t *surface ); picoVec_t *PicoGetSurfaceXYZ( picoSurface_t *surface, int num ); picoVec_t *PicoGetSurfaceNormal( picoSurface_t *surface, int num ); picoVec_t *PicoGetSurfaceST( picoSurface_t *surface, int array, int num ); picoByte_t *PicoGetSurfaceColor( picoSurface_t *surface, int array, int num ); int PicoGetSurfaceNumIndexes( picoSurface_t *surface ); picoIndex_t PicoGetSurfaceIndex( picoSurface_t *surface, int num ); picoIndex_t *PicoGetSurfaceIndexes( picoSurface_t *surface, int num ); picoVec_t *PicoGetFaceNormal( picoSurface_t *surface, int num ); int PicoGetSurfaceSpecial( picoSurface_t *surface, int num ); /* hashtable related functions */ typedef struct picoVertexCombinationData_s { picoVec3_t xyz, normal; picoVec2_t st; picoColor_t color; } picoVertexCombinationData_t; typedef struct picoVertexCombinationHash_s { picoVertexCombinationData_t vcd; picoIndex_t index; void *data; struct picoVertexCombinationHash_s *next; } picoVertexCombinationHash_t; int PicoGetHashTableSize( void ); unsigned int PicoVertexCoordGenerateHash( picoVec3_t xyz ); picoVertexCombinationHash_t **PicoNewVertexCombinationHashTable( void ); void PicoFreeVertexCombinationHashTable( picoVertexCombinationHash_t **hashTable ); picoVertexCombinationHash_t *PicoFindVertexCombinationInHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color ); picoVertexCombinationHash_t *PicoAddVertexCombinationToHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color, picoIndex_t index ); /* specialized functions */ int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color, picoIndex_t smoothingGroup ); void PicoFixSurfaceNormals( picoSurface_t *surface ); int PicoRemapModel( picoModel_t *model, char *remapFile ); void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors, picoShader_t* shader, picoIndex_t* smoothingGroup); /* end marker */ #ifdef __cplusplus } #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�13217505464�0016615�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/Makefile.am��������������������������������������������������������0000664�0000000�0000000�00000002034�13217505464�0020650�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs AM_CFLAGS = -fPIC pkglib_LTLIBRARIES = libpicomodel.la libpicomodel_la_LDFLAGS = -release @PACKAGE_VERSION@ -lm libpicomodel_la_SOURCES = picointernal.c \ lwo/vecmath.c \ lwo/clip.c \ lwo/surface.c \ lwo/pntspols.c \ lwo/list.c \ lwo/envelope.c \ lwo/vmap.c \ lwo/lwio.c \ lwo/lwo2.c \ lwo/lwob.c \ pm_mdc.c \ pm_terrain.c \ pm_3ds.c \ pm_md3.c \ picomodel.c \ pm_ms3d.c \ pm_obj.c \ pm_lwo.c \ pm_fm.c \ pm_md2.c \ pm_ase.c \ picomodules.c ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/���������������������������������������������������������������0000775�0000000�0000000�00000000000�13217505464�0017416�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/clip.c���������������������������������������������������������0000664�0000000�0000000�00000014762�13217505464�0020523�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== clip.c Functions for LWO2 image references. Ernie Wright 17 Sep 00 ====================================================================== */ #include "../picointernal.h" #include "lwo2.h" /* ====================================================================== lwFreeClip() Free memory used by an lwClip. ====================================================================== */ void lwFreeClip( lwClip *clip ) { if ( clip ) { lwListFree( clip->ifilter, (void *) lwFreePlugin ); lwListFree( clip->pfilter, (void *) lwFreePlugin ); switch ( clip->type ) { case ID_STIL: _pico_free( clip->source.still.name); break; case ID_ISEQ: _pico_free( clip->source.seq.prefix ); _pico_free( clip->source.seq.suffix ); break; case ID_ANIM: _pico_free( clip->source.anim.name ); _pico_free( clip->source.anim.server ); _pico_free( clip->source.anim.data ); break; case ID_XREF: _pico_free( clip->source.xref.string ); break; case ID_STCC: _pico_free( clip->source.cycle.name ); break; default: break; } _pico_free( clip ); } } /* ====================================================================== lwGetClip() Read image references from a CLIP chunk in an LWO2 file. ====================================================================== */ lwClip *lwGetClip( picoMemStream_t *fp, int cksize ) { lwClip *clip; lwPlugin *filt; unsigned int id; unsigned short sz; int pos, rlen; /* allocate the Clip structure */ clip = _pico_calloc( 1, sizeof( lwClip )); if ( !clip ) goto Fail; clip->contrast.val = 1.0f; clip->brightness.val = 1.0f; clip->saturation.val = 1.0f; clip->gamma.val = 1.0f; /* remember where we started */ set_flen( 0 ); pos = (int)_pico_memstream_tell( fp ); /* index */ clip->index = getI4( fp ); /* first subchunk header */ clip->type = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) goto Fail; sz += sz & 1; set_flen( 0 ); switch ( clip->type ) { case ID_STIL: clip->source.still.name = getS0( fp ); break; case ID_ISEQ: clip->source.seq.digits = getU1( fp ); clip->source.seq.flags = getU1( fp ); clip->source.seq.offset = getI2( fp ); getU2( fp ); /* not sure what this is yet */ clip->source.seq.start = getI2( fp ); clip->source.seq.end = getI2( fp ); clip->source.seq.prefix = getS0( fp ); clip->source.seq.suffix = getS0( fp ); break; case ID_ANIM: clip->source.anim.name = getS0( fp ); clip->source.anim.server = getS0( fp ); rlen = get_flen(); clip->source.anim.data = getbytes( fp, sz - rlen ); break; case ID_XREF: clip->source.xref.index = getI4( fp ); clip->source.xref.string = getS0( fp ); break; case ID_STCC: clip->source.cycle.lo = getI2( fp ); clip->source.cycle.hi = getI2( fp ); clip->source.cycle.name = getS0( fp ); break; default: break; } /* error while reading current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) goto Fail; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the CLIP chunk? */ rlen = (int)_pico_memstream_tell( fp ) - pos; if ( cksize < rlen ) goto Fail; if ( cksize == rlen ) return clip; /* process subchunks as they're encountered */ id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) goto Fail; while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_TIME: clip->start_time = getF4( fp ); clip->duration = getF4( fp ); clip->frame_rate = getF4( fp ); break; case ID_CONT: clip->contrast.val = getF4( fp ); clip->contrast.eindex = getVX( fp ); break; case ID_BRIT: clip->brightness.val = getF4( fp ); clip->brightness.eindex = getVX( fp ); break; case ID_SATR: clip->saturation.val = getF4( fp ); clip->saturation.eindex = getVX( fp ); break; case ID_HUE: clip->hue.val = getF4( fp ); clip->hue.eindex = getVX( fp ); break; case ID_GAMM: clip->gamma.val = getF4( fp ); clip->gamma.eindex = getVX( fp ); break; case ID_NEGA: clip->negative = getU2( fp ); break; case ID_IFLT: case ID_PFLT: filt = _pico_calloc( 1, sizeof( lwPlugin )); if ( !filt ) goto Fail; filt->name = getS0( fp ); filt->flags = getU2( fp ); rlen = get_flen(); filt->data = getbytes( fp, sz - rlen ); if ( id == ID_IFLT ) { lwListAdd( (void *) &clip->ifilter, filt ); clip->nifilters++; } else { lwListAdd( (void *) &clip->pfilter, filt ); clip->npfilters++; } break; default: break; } /* error while reading current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) goto Fail; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the CLIP chunk? */ rlen = (int)_pico_memstream_tell( fp ) - pos; if ( cksize < rlen ) goto Fail; if ( cksize == rlen ) break; /* get the next chunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) goto Fail; } return clip; Fail: lwFreeClip( clip ); return NULL; } /* ====================================================================== lwFindClip() Returns an lwClip pointer, given a clip index. ====================================================================== */ lwClip *lwFindClip( lwClip *list, int index ) { lwClip *clip; clip = list; while ( clip ) { if ( clip->index == index ) break; clip = clip->next; } return clip; } ��������������DarkRadiant-2.5.0/libs/picomodel/lwo/envelope.c�����������������������������������������������������0000664�0000000�0000000�00000035166�13217505464�0021412�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== envelope.c Envelope functions for an LWO2 reader. Ernie Wright 16 Nov 00 ====================================================================== */ #include "../picointernal.h" #include "lwo2.h" /* ====================================================================== lwFreeEnvelope() Free the memory used by an lwEnvelope. ====================================================================== */ void lwFreeEnvelope( lwEnvelope *env ) { if ( env ) { if ( env->name ) _pico_free( env->name ); lwListFree( env->key, _pico_free ); lwListFree( env->cfilter, (void *) lwFreePlugin ); _pico_free( env ); } } static int compare_keys( lwKey *k1, lwKey *k2 ) { return k1->time > k2->time ? 1 : k1->time < k2->time ? -1 : 0; } /* ====================================================================== lwGetEnvelope() Read an ENVL chunk from an LWO2 file. ====================================================================== */ lwEnvelope *lwGetEnvelope( picoMemStream_t *fp, int cksize ) { lwEnvelope *env; lwKey *key; lwPlugin *plug; unsigned int id; unsigned short sz; float f[ 4 ]; int i, nparams, pos, rlen; key = NULL; /* allocate the Envelope structure */ env = _pico_calloc( 1, sizeof( lwEnvelope )); if ( !env ) goto Fail; /* remember where we started */ set_flen( 0 ); pos = (int)_pico_memstream_tell( fp ); /* index */ env->index = getVX( fp ); /* first subchunk header */ id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) goto Fail; /* process subchunks as they're encountered */ while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_TYPE: env->type = getU2( fp ); break; case ID_NAME: env->name = getS0( fp ); break; case ID_PRE: env->behavior[ 0 ] = getU2( fp ); break; case ID_POST: env->behavior[ 1 ] = getU2( fp ); break; case ID_KEY: key = _pico_calloc( 1, sizeof( lwKey )); if ( !key ) goto Fail; key->time = getF4( fp ); key->value = getF4( fp ); lwListInsert( (void **) &env->key, key, (void *) compare_keys ); env->nkeys++; break; case ID_SPAN: if ( !key ) goto Fail; key->shape = getU4( fp ); nparams = ( sz - 4 ) / 4; if ( nparams > 4 ) nparams = 4; for ( i = 0; i < nparams; i++ ) f[ i ] = getF4( fp ); switch ( key->shape ) { case ID_TCB: key->tension = f[ 0 ]; key->continuity = f[ 1 ]; key->bias = f[ 2 ]; break; case ID_BEZI: case ID_HERM: case ID_BEZ2: for ( i = 0; i < nparams; i++ ) key->param[ i ] = f[ i ]; break; } break; case ID_CHAN: plug = _pico_calloc( 1, sizeof( lwPlugin )); if ( !plug ) goto Fail; plug->name = getS0( fp ); plug->flags = getU2( fp ); plug->data = getbytes( fp, sz - get_flen() ); lwListAdd( (void *) &env->cfilter, plug ); env->ncfilters++; break; default: break; } /* error while reading current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) goto Fail; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the ENVL chunk? */ rlen = (int)_pico_memstream_tell( fp ) - pos; if ( cksize < rlen ) goto Fail; if ( cksize == rlen ) break; /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) goto Fail; } return env; Fail: lwFreeEnvelope( env ); return NULL; } /* ====================================================================== lwFindEnvelope() Returns an lwEnvelope pointer, given an envelope index. ====================================================================== */ lwEnvelope *lwFindEnvelope( lwEnvelope *list, int index ) { lwEnvelope *env; env = list; while ( env ) { if ( env->index == index ) break; env = env->next; } return env; } /* ====================================================================== range() Given the value v of a periodic function, returns the equivalent value v2 in the principal interval [lo, hi]. If i isn't NULL, it receives the number of wavelengths between v and v2. v2 = v - i * (hi - lo) For example, range( 3 pi, 0, 2 pi, i ) returns pi, with i = 1. ====================================================================== */ static float range( float v, float lo, float hi, int *i ) { float v2, r = hi - lo; if ( r == 0.0 ) { if ( i ) *i = 0; return lo; } v2 = lo + v - r * ( float ) floor(( double ) v / r ); if ( i ) *i = -( int )(( v2 - v ) / r + ( v2 > v ? 0.5 : -0.5 )); return v2; } /* ====================================================================== hermite() Calculate the Hermite coefficients. ====================================================================== */ static void hermite( float t, float *h1, float *h2, float *h3, float *h4 ) { float t2, t3; t2 = t * t; t3 = t * t2; *h2 = 3.0f * t2 - t3 - t3; *h1 = 1.0f - *h2; *h4 = t3 - t2; *h3 = *h4 - t2 + t; } /* ====================================================================== bezier() Interpolate the value of a 1D Bezier curve. ====================================================================== */ static float bezier( float x0, float x1, float x2, float x3, float t ) { float a, b, c, t2, t3; t2 = t * t; t3 = t2 * t; c = 3.0f * ( x1 - x0 ); b = 3.0f * ( x2 - x1 ) - c; a = x3 - x0 - c - b; return a * t3 + b * t2 + c * t + x0; } /* ====================================================================== bez2_time() Find the t for which bezier() returns the input time. The handle endpoints of a BEZ2 curve represent the control points, and these have (time, value) coordinates, so time is used as both a coordinate and a parameter for this curve type. ====================================================================== */ static float bez2_time( float x0, float x1, float x2, float x3, float time, float *t0, float *t1 ) { float v, t; t = *t0 + ( *t1 - *t0 ) * 0.5f; v = bezier( x0, x1, x2, x3, t ); if ( fabs( time - v ) > .0001f ) { if ( v > time ) *t1 = t; else *t0 = t; return bez2_time( x0, x1, x2, x3, time, t0, t1 ); } else return t; } /* ====================================================================== bez2() Interpolate the value of a BEZ2 curve. ====================================================================== */ static float bez2( lwKey *key0, lwKey *key1, float time ) { float x, y, t, t0 = 0.0f, t1 = 1.0f; if ( key0->shape == ID_BEZ2 ) x = key0->time + key0->param[ 2 ]; else x = key0->time + ( key1->time - key0->time ) / 3.0f; t = bez2_time( key0->time, x, key1->time + key1->param[ 0 ], key1->time, time, &t0, &t1 ); if ( key0->shape == ID_BEZ2 ) y = key0->value + key0->param[ 3 ]; else y = key0->value + key0->param[ 1 ] / 3.0f; return bezier( key0->value, y, key1->param[ 1 ] + key1->value, key1->value, t ); } /* ====================================================================== outgoing() Return the outgoing tangent to the curve at key0. The value returned for the BEZ2 case is used when extrapolating a linear pre behavior and when interpolating a non-BEZ2 span. ====================================================================== */ static float outgoing( lwKey *key0, lwKey *key1 ) { float a, b, d, t, out; switch ( key0->shape ) { case ID_TCB: a = ( 1.0f - key0->tension ) * ( 1.0f + key0->continuity ) * ( 1.0f + key0->bias ); b = ( 1.0f - key0->tension ) * ( 1.0f - key0->continuity ) * ( 1.0f - key0->bias ); d = key1->value - key0->value; if ( key0->prev ) { t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time ); out = t * ( a * ( key0->value - key0->prev->value ) + b * d ); } else out = b * d; break; case ID_LINE: d = key1->value - key0->value; if ( key0->prev ) { t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time ); out = t * ( key0->value - key0->prev->value + d ); } else out = d; break; case ID_BEZI: case ID_HERM: out = key0->param[ 1 ]; if ( key0->prev ) out *= ( key1->time - key0->time ) / ( key1->time - key0->prev->time ); break; case ID_BEZ2: out = key0->param[ 3 ] * ( key1->time - key0->time ); if ( fabs( key0->param[ 2 ] ) > 1e-5f ) out /= key0->param[ 2 ]; else out *= 1e5f; break; case ID_STEP: default: out = 0.0f; break; } return out; } /* ====================================================================== incoming() Return the incoming tangent to the curve at key1. The value returned for the BEZ2 case is used when extrapolating a linear post behavior. ====================================================================== */ static float incoming( lwKey *key0, lwKey *key1 ) { float a, b, d, t, in; switch ( key1->shape ) { case ID_LINE: d = key1->value - key0->value; if ( key1->next ) { t = ( key1->time - key0->time ) / ( key1->next->time - key0->time ); in = t * ( key1->next->value - key1->value + d ); } else in = d; break; case ID_TCB: a = ( 1.0f - key1->tension ) * ( 1.0f - key1->continuity ) * ( 1.0f + key1->bias ); b = ( 1.0f - key1->tension ) * ( 1.0f + key1->continuity ) * ( 1.0f - key1->bias ); d = key1->value - key0->value; if ( key1->next ) { t = ( key1->time - key0->time ) / ( key1->next->time - key0->time ); in = t * ( b * ( key1->next->value - key1->value ) + a * d ); } else in = a * d; break; case ID_BEZI: case ID_HERM: in = key1->param[ 0 ]; if ( key1->next ) in *= ( key1->time - key0->time ) / ( key1->next->time - key0->time ); break; return in; case ID_BEZ2: in = key1->param[ 1 ] * ( key1->time - key0->time ); if ( fabs( key1->param[ 0 ] ) > 1e-5f ) in /= key1->param[ 0 ]; else in *= 1e5f; break; case ID_STEP: default: in = 0.0f; break; } return in; } /* ====================================================================== evalEnvelope() Given a list of keys and a time, returns the interpolated value of the envelope at that time. ====================================================================== */ float evalEnvelope( lwEnvelope *env, float time ) { lwKey *key0, *key1, *skey, *ekey; float t, h1, h2, h3, h4, in, out, offset = 0.0f; int noff; /* if there's no key, the value is 0 */ if ( env->nkeys == 0 ) return 0.0f; /* if there's only one key, the value is constant */ if ( env->nkeys == 1 ) return env->key->value; /* find the first and last keys */ skey = ekey = env->key; while ( ekey->next ) ekey = ekey->next; /* use pre-behavior if time is before first key time */ if ( time < skey->time ) { switch ( env->behavior[ 0 ] ) { case BEH_RESET: return 0.0f; case BEH_CONSTANT: return skey->value; case BEH_REPEAT: time = range( time, skey->time, ekey->time, NULL ); break; case BEH_OSCILLATE: time = range( time, skey->time, ekey->time, &noff ); if ( noff % 2 ) time = ekey->time - skey->time - time; break; case BEH_OFFSET: time = range( time, skey->time, ekey->time, &noff ); offset = noff * ( ekey->value - skey->value ); break; case BEH_LINEAR: out = outgoing( skey, skey->next ) / ( skey->next->time - skey->time ); return out * ( time - skey->time ) + skey->value; } } /* use post-behavior if time is after last key time */ else if ( time > ekey->time ) { switch ( env->behavior[ 1 ] ) { case BEH_RESET: return 0.0f; case BEH_CONSTANT: return ekey->value; case BEH_REPEAT: time = range( time, skey->time, ekey->time, NULL ); break; case BEH_OSCILLATE: time = range( time, skey->time, ekey->time, &noff ); if ( noff % 2 ) time = ekey->time - skey->time - time; break; case BEH_OFFSET: time = range( time, skey->time, ekey->time, &noff ); offset = noff * ( ekey->value - skey->value ); break; case BEH_LINEAR: in = incoming( ekey->prev, ekey ) / ( ekey->time - ekey->prev->time ); return in * ( time - ekey->time ) + ekey->value; } } /* get the endpoints of the interval being evaluated */ key0 = env->key; while ( time > key0->next->time ) key0 = key0->next; key1 = key0->next; /* check for singularities first */ if ( time == key0->time ) return key0->value + offset; else if ( time == key1->time ) return key1->value + offset; /* get interval length, time in [0, 1] */ t = ( time - key0->time ) / ( key1->time - key0->time ); /* interpolate */ switch ( key1->shape ) { case ID_TCB: case ID_BEZI: case ID_HERM: out = outgoing( key0, key1 ); in = incoming( key0, key1 ); hermite( t, &h1, &h2, &h3, &h4 ); return h1 * key0->value + h2 * key1->value + h3 * out + h4 * in + offset; case ID_BEZ2: return bez2( key0, key1, time ) + offset; case ID_LINE: return key0->value + t * ( key1->value - key0->value ) + offset; case ID_STEP: return key0->value + offset; default: return offset; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/list.c���������������������������������������������������������0000664�0000000�0000000�00000004010�13217505464�0020530�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== list.c Generic linked list operations. Ernie Wright 17 Sep 00 ====================================================================== */ #include "../picointernal.h" #include "lwo2.h" /* ====================================================================== lwListFree() Free the items in a list. ====================================================================== */ void lwListFree( void *list, void ( *freeNode )( void * )) { lwNode *node, *next; node = ( lwNode * ) list; while ( node ) { next = node->next; freeNode( node ); node = next; } } /* ====================================================================== lwListAdd() Append a node to a list. ====================================================================== */ void lwListAdd( void **list, void *node ) { lwNode *head, *tail = NULL; head = *(( lwNode ** ) list ); if ( !head ) { *list = node; return; } while ( head ) { tail = head; head = head->next; } tail->next = ( lwNode * ) node; (( lwNode * ) node )->prev = tail; } /* ====================================================================== lwListInsert() Insert a node into a list in sorted order. ====================================================================== */ void lwListInsert( void **vlist, void *vitem, int ( *compare )( void *, void * )) { lwNode **list, *item, *node, *prev; if ( !*vlist ) { *vlist = vitem; return; } list = ( lwNode ** ) vlist; item = ( lwNode * ) vitem; node = *list; prev = NULL; while ( node ) { if ( 0 < compare( node, item )) break; prev = node; node = node->next; } if ( !prev ) { *list = item; node->prev = item; item->next = node; } else if ( !node ) { prev->next = item; item->prev = prev; } else { item->next = node; item->prev = prev; prev->next = item; node->prev = item; } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/lwio.c���������������������������������������������������������0000664�0000000�0000000�00000017167�13217505464�0020550�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== lwio.c Functions for reading basic LWO2 data types. Ernie Wright 17 Sep 00 ====================================================================== */ #include "../picointernal.h" #include "lwo2.h" /* ====================================================================== flen This accumulates a count of the number of bytes read. Callers can set it at the beginning of a sequence of reads and then retrieve it to get the number of bytes actually read. If one of the I/O functions fails, flen is set to an error code, after which the I/O functions ignore read requests until flen is reset. ====================================================================== */ #define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */ #define FLEN_ERROR INT_MIN static int flen; void set_flen( int i ) { flen = i; } int get_flen( void ) { return flen; } #ifndef __BIG_ENDIAN__ /* ===================================================================== revbytes() Reverses byte order in place. INPUTS bp bytes to reverse elsize size of the underlying data type elcount number of elements to swap RESULTS Reverses the byte order in each of elcount elements. This only needs to be defined on little-endian platforms, most notably Windows. lwo2.h replaces this with a #define on big-endian platforms. ===================================================================== */ void revbytes( void *bp, int elsize, int elcount ) { register unsigned char *p, *q; p = ( unsigned char * ) bp; if ( elsize == 2 ) { q = p + 1; while ( elcount-- ) { *p ^= *q; *q ^= *p; *p ^= *q; p += 2; q += 2; } return; } while ( elcount-- ) { q = p + elsize - 1; while ( p < q ) { *p ^= *q; *q ^= *p; *p ^= *q; ++p; --q; } p += elsize >> 1; } } #endif void *getbytes( picoMemStream_t *fp, int size ) { void *data; if ( flen == FLEN_ERROR ) return NULL; if ( size < 0 ) { flen = FLEN_ERROR; return NULL; } data = _pico_alloc( size ); if ( !data ) { flen = FLEN_ERROR; return NULL; } if ( 1 != _pico_memstream_read( fp, data, size )) { flen = FLEN_ERROR; _pico_free( data ); return NULL; } flen += size; return data; } void skipbytes( picoMemStream_t *fp, int n ) { if ( flen == FLEN_ERROR ) return; if ( _pico_memstream_seek( fp, n, PICO_SEEK_CUR )) flen = FLEN_ERROR; else flen += n; } int getI1( picoMemStream_t *fp ) { int i; if ( flen == FLEN_ERROR ) return 0; i = _pico_memstream_getc( fp ); if ( i < 0 ) { flen = FLEN_ERROR; return 0; } if ( i > 127 ) i -= 256; flen += 1; return i; } short getI2( picoMemStream_t *fp ) { short i; if ( flen == FLEN_ERROR ) return 0; if ( 1 != _pico_memstream_read( fp, &i, 2 )) { flen = FLEN_ERROR; return 0; } revbytes( &i, 2, 1 ); flen += 2; return i; } int getI4( picoMemStream_t *fp ) { int i; if ( flen == FLEN_ERROR ) return 0; if ( 1 != _pico_memstream_read( fp, &i, 4 )) { flen = FLEN_ERROR; return 0; } revbytes( &i, 4, 1 ); flen += 4; return i; } unsigned char getU1( picoMemStream_t *fp ) { int i; if ( flen == FLEN_ERROR ) return 0; i = _pico_memstream_getc( fp ); if ( i < 0 ) { flen = FLEN_ERROR; return 0; } flen += 1; return i; } unsigned short getU2( picoMemStream_t *fp ) { unsigned short i; if ( flen == FLEN_ERROR ) return 0; if ( 1 != _pico_memstream_read( fp, &i, 2 )) { flen = FLEN_ERROR; return 0; } revbytes( &i, 2, 1 ); flen += 2; return i; } unsigned int getU4( picoMemStream_t *fp ) { unsigned int i; if ( flen == FLEN_ERROR ) return 0; if ( 1 != _pico_memstream_read( fp, &i, 4 )) { flen = FLEN_ERROR; return 0; } revbytes( &i, 4, 1 ); flen += 4; return i; } int getVX( picoMemStream_t *fp ) { int i, c; if ( flen == FLEN_ERROR ) return 0; c = _pico_memstream_getc( fp ); if ( c != 0xFF ) { i = c << 8; c = _pico_memstream_getc( fp ); i |= c; flen += 2; } else { c = _pico_memstream_getc( fp ); i = c << 16; c = _pico_memstream_getc( fp ); i |= c << 8; c = _pico_memstream_getc( fp ); i |= c; flen += 4; } if ( _pico_memstream_error( fp )) { flen = FLEN_ERROR; return 0; } return i; } float getF4( picoMemStream_t *fp ) { float f; if ( flen == FLEN_ERROR ) return 0.0f; if ( 1 != _pico_memstream_read( fp, &f, 4 )) { flen = FLEN_ERROR; return 0.0f; } revbytes( &f, 4, 1 ); flen += 4; return f; } char *getS0( picoMemStream_t *fp ) { char *s; int i, c, len, pos; if ( flen == FLEN_ERROR ) return NULL; pos = (int)_pico_memstream_tell( fp ); for ( i = 1; ; i++ ) { c = _pico_memstream_getc( fp ); if ( c <= 0 ) break; } if ( c < 0 ) { flen = FLEN_ERROR; return NULL; } if ( i == 1 ) { if ( _pico_memstream_seek( fp, pos + 2, PICO_SEEK_SET )) flen = FLEN_ERROR; else flen += 2; return NULL; } len = i + ( i & 1 ); s = _pico_alloc( len ); if ( !s ) { flen = FLEN_ERROR; return NULL; } if ( _pico_memstream_seek( fp, pos, PICO_SEEK_SET )) { flen = FLEN_ERROR; return NULL; } if ( 1 != _pico_memstream_read( fp, s, len )) { flen = FLEN_ERROR; return NULL; } flen += len; return s; } int sgetI1( unsigned char **bp ) { int i; if ( flen == FLEN_ERROR ) return 0; i = **bp; if ( i > 127 ) i -= 256; flen += 1; (*bp)++; return i; } short sgetI2( unsigned char **bp ) { short i; if ( flen == FLEN_ERROR ) return 0; memcpy( &i, *bp, 2 ); revbytes( &i, 2, 1 ); flen += 2; *bp += 2; return i; } int sgetI4( unsigned char **bp ) { int i; if ( flen == FLEN_ERROR ) return 0; memcpy( &i, *bp, 4 ); revbytes( &i, 4, 1 ); flen += 4; *bp += 4; return i; } unsigned char sgetU1( unsigned char **bp ) { unsigned char c; if ( flen == FLEN_ERROR ) return 0; c = **bp; flen += 1; (*bp)++; return c; } unsigned short sgetU2( unsigned char **bp ) { unsigned char *buf = *bp; unsigned short i; if ( flen == FLEN_ERROR ) return 0; i = ( buf[ 0 ] << 8 ) | buf[ 1 ]; flen += 2; *bp += 2; return i; } unsigned int sgetU4( unsigned char **bp ) { unsigned int i; if ( flen == FLEN_ERROR ) return 0; memcpy( &i, *bp, 4 ); revbytes( &i, 4, 1 ); flen += 4; *bp += 4; return i; } int sgetVX( unsigned char **bp ) { unsigned char *buf = *bp; int i; if ( flen == FLEN_ERROR ) return 0; if ( buf[ 0 ] != 0xFF ) { i = buf[ 0 ] << 8 | buf[ 1 ]; flen += 2; *bp += 2; } else { i = ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ]; flen += 4; *bp += 4; } return i; } float sgetF4( unsigned char **bp ) { float f; if ( flen == FLEN_ERROR ) return 0.0f; memcpy( &f, *bp, 4 ); revbytes( &f, 4, 1 ); flen += 4; *bp += 4; return f; } char *sgetS0( unsigned char **bp ) { char *s; unsigned char *buf = *bp; size_t len; if ( flen == FLEN_ERROR ) return NULL; len = strlen( (char*)buf ) + 1; if ( len == 1 ) { flen += 2; *bp += 2; return NULL; } len += len & 1; s = _pico_alloc( len ); if ( !s ) { flen = FLEN_ERROR; return NULL; } memcpy( s, buf, len ); flen += (int)len; *bp += len; return s; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/lwo2.c���������������������������������������������������������0000664�0000000�0000000�00000020443�13217505464�0020450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== lwo2.c The entry point for loading LightWave object files. Ernie Wright 17 Sep 00 ====================================================================== */ #include "../picointernal.h" #include "lwo2.h" /* disable warnings */ #ifdef WIN32 #pragma warning( disable:4018 ) /* signed/unsigned mismatch */ #endif /* ====================================================================== lwFreeLayer() Free memory used by an lwLayer. ====================================================================== */ void lwFreeLayer( lwLayer *layer ) { if ( layer ) { if ( layer->name ) _pico_free( layer->name ); lwFreePoints( &layer->point ); lwFreePolygons( &layer->polygon ); lwListFree( layer->vmap, (void *) lwFreeVMap ); _pico_free( layer ); } } /* ====================================================================== lwFreeObject() Free memory used by an lwObject. ====================================================================== */ void lwFreeObject( lwObject *object ) { if ( object ) { lwListFree( object->layer, (void *) lwFreeLayer ); lwListFree( object->env, (void *) lwFreeEnvelope ); lwListFree( object->clip, (void *) lwFreeClip ); lwListFree( object->surf, (void *) lwFreeSurface ); lwFreeTags( &object->taglist ); _pico_free( object ); } } /* ====================================================================== lwGetObject() Returns the contents of a LightWave object, given its filename, or NULL if the file couldn't be loaded. On failure, failID and failpos can be used to diagnose the cause. 1. If the file isn't an LWO2 or an LWOB, failpos will contain 12 and failID will be unchanged. 2. If an error occurs while reading, failID will contain the most recently read IFF chunk ID, and failpos will contain the value returned by _pico_memstream_tell() at the time of the failure. 3. If the file couldn't be opened, or an error occurs while reading the first 12 bytes, both failID and failpos will be unchanged. If you don't need this information, failID and failpos can be NULL. ====================================================================== */ lwObject *lwGetObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ) { lwObject *object; lwLayer *layer; lwNode *node; unsigned int id, formsize, type, cksize; int i, rlen; /* open the file */ if ( !fp ) return NULL; /* read the first 12 bytes */ set_flen( 0 ); id = getU4( fp ); formsize = getU4( fp ); type = getU4( fp ); if ( 12 != get_flen() ) { return NULL; } /* is this a LW object? */ if ( id != ID_FORM ) { if ( failpos ) *failpos = 12; return NULL; } if ( type != ID_LWO2 ) { if ( type == ID_LWOB ) return lwGetObject5( filename, fp, failID, failpos ); else { if ( failpos ) *failpos = 12; return NULL; } } /* allocate an object and a default layer */ object = _pico_calloc( 1, sizeof( lwObject )); if ( !object ) goto Fail; layer = _pico_calloc( 1, sizeof( lwLayer )); if ( !layer ) goto Fail; object->layer = layer; /* get the first chunk header */ id = getU4( fp ); cksize = getU4( fp ); if ( 0 > get_flen() ) goto Fail; /* process chunks as they're encountered */ while ( 1 ) { cksize += cksize & 1; switch ( id ) { case ID_LAYR: if ( object->nlayers > 0 ) { layer = _pico_calloc( 1, sizeof( lwLayer )); if ( !layer ) goto Fail; lwListAdd( (void **) &object->layer, layer ); } object->nlayers++; set_flen( 0 ); layer->index = getU2( fp ); layer->flags = getU2( fp ); layer->pivot[ 0 ] = getF4( fp ); layer->pivot[ 1 ] = getF4( fp ); layer->pivot[ 2 ] = getF4( fp ); layer->name = getS0( fp ); rlen = get_flen(); if ( rlen < 0 || rlen > cksize ) goto Fail; if ( rlen <= cksize - 2 ) layer->parent = getU2( fp ); rlen = get_flen(); if ( rlen < cksize ) _pico_memstream_seek( fp, cksize - rlen, PICO_SEEK_CUR ); break; case ID_PNTS: if ( !lwGetPoints( fp, cksize, &layer->point )) goto Fail; break; case ID_POLS: if ( !lwGetPolygons( fp, cksize, &layer->polygon, layer->point.offset )) goto Fail; break; case ID_VMAP: case ID_VMAD: node = ( lwNode * ) lwGetVMap( fp, cksize, layer->point.offset, layer->polygon.offset, id == ID_VMAD ); if ( !node ) goto Fail; lwListAdd( (void **) &layer->vmap, node ); layer->nvmaps++; break; case ID_PTAG: if ( !lwGetPolygonTags( fp, cksize, &object->taglist, &layer->polygon )) goto Fail; break; case ID_BBOX: set_flen( 0 ); for ( i = 0; i < 6; i++ ) layer->bbox[ i ] = getF4( fp ); rlen = get_flen(); if ( rlen < 0 || rlen > cksize ) goto Fail; if ( rlen < cksize ) _pico_memstream_seek( fp, cksize - rlen, PICO_SEEK_CUR ); break; case ID_TAGS: if ( !lwGetTags( fp, cksize, &object->taglist )) goto Fail; break; case ID_ENVL: node = ( lwNode * ) lwGetEnvelope( fp, cksize ); if ( !node ) goto Fail; lwListAdd( (void **) &object->env, node ); object->nenvs++; break; case ID_CLIP: node = ( lwNode * ) lwGetClip( fp, cksize ); if ( !node ) goto Fail; lwListAdd( (void **) &object->clip, node ); object->nclips++; break; case ID_SURF: node = ( lwNode * ) lwGetSurface( fp, cksize ); if ( !node ) goto Fail; lwListAdd( (void **) &object->surf, node ); object->nsurfs++; break; case ID_DESC: case ID_TEXT: case ID_ICON: default: _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR ); break; } /* end of the file? */ if ( formsize <= _pico_memstream_tell( fp ) - 8 ) break; /* get the next chunk header */ set_flen( 0 ); id = getU4( fp ); cksize = getU4( fp ); if ( 8 != get_flen() ) goto Fail; } if ( object->nlayers == 0 ) object->nlayers = 1; layer = object->layer; while ( layer ) { lwGetBoundingBox( &layer->point, layer->bbox ); lwGetPolyNormals( &layer->point, &layer->polygon ); if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail; if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist, &object->surf, &object->nsurfs )) goto Fail; lwGetVertNormals( &layer->point, &layer->polygon ); if ( !lwGetPointVMaps( &layer->point, layer->vmap )) goto Fail; if ( !lwGetPolyVMaps( &layer->polygon, layer->vmap )) goto Fail; layer = layer->next; } return object; Fail: if ( failID ) *failID = id; if ( fp ) { if ( failpos ) *failpos = (int)_pico_memstream_tell( fp ); } lwFreeObject( object ); return NULL; } int lwValidateObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ) { unsigned int id, formsize, type; /* open the file */ if ( !fp ) return PICO_PMV_ERROR_MEMORY; /* read the first 12 bytes */ set_flen( 0 ); id = getU4( fp ); formsize = getU4( fp ); type = getU4( fp ); if ( 12 != get_flen() ) { return PICO_PMV_ERROR_SIZE; } /* is this a LW object? */ if ( id != ID_FORM ) { if ( failpos ) *failpos = 12; formsize++; /* silence compiler warning about unused variable */ return PICO_PMV_ERROR_SIZE; } if ( type != ID_LWO2 ) { if ( type == ID_LWOB ) return lwValidateObject5( filename, fp, failID, failpos ); else { if ( failpos ) *failpos = 12; return PICO_PMV_ERROR_IDENT; } } return PICO_PMV_OK; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/lwo2.h���������������������������������������������������������0000664�0000000�0000000�00000045410�13217505464�0020456�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== lwo2.h Definitions and typedefs for LWO2 files. Ernie Wright 17 Sep 00 ====================================================================== */ #ifndef LWO2_H #define LWO2_H /* chunk and subchunk IDs */ #define LWID_(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) #define ID_FORM LWID_('F','O','R','M') #define ID_LWO2 LWID_('L','W','O','2') #define ID_LWOB LWID_('L','W','O','B') /* top-level chunks */ #define ID_LAYR LWID_('L','A','Y','R') #define ID_TAGS LWID_('T','A','G','S') #define ID_PNTS LWID_('P','N','T','S') #define ID_BBOX LWID_('B','B','O','X') #define ID_VMAP LWID_('V','M','A','P') #define ID_VMAD LWID_('V','M','A','D') #define ID_POLS LWID_('P','O','L','S') #define ID_PTAG LWID_('P','T','A','G') #define ID_ENVL LWID_('E','N','V','L') #define ID_CLIP LWID_('C','L','I','P') #define ID_SURF LWID_('S','U','R','F') #define ID_DESC LWID_('D','E','S','C') #define ID_TEXT LWID_('T','E','X','T') #define ID_ICON LWID_('I','C','O','N') /* polygon types */ #define ID_FACE LWID_('F','A','C','E') #define ID_CURV LWID_('C','U','R','V') #define ID_PTCH LWID_('P','T','C','H') #define ID_MBAL LWID_('M','B','A','L') #define ID_BONE LWID_('B','O','N','E') /* polygon tags */ #define ID_SURF LWID_('S','U','R','F') #define ID_PART LWID_('P','A','R','T') #define ID_SMGP LWID_('S','M','G','P') /* envelopes */ #define ID_PRE LWID_('P','R','E',' ') #define ID_POST LWID_('P','O','S','T') #define ID_KEY LWID_('K','E','Y',' ') #define ID_SPAN LWID_('S','P','A','N') #define ID_TCB LWID_('T','C','B',' ') #define ID_HERM LWID_('H','E','R','M') #define ID_BEZI LWID_('B','E','Z','I') #define ID_BEZ2 LWID_('B','E','Z','2') #define ID_LINE LWID_('L','I','N','E') #define ID_STEP LWID_('S','T','E','P') /* clips */ #define ID_STIL LWID_('S','T','I','L') #define ID_ISEQ LWID_('I','S','E','Q') #define ID_ANIM LWID_('A','N','I','M') #define ID_XREF LWID_('X','R','E','F') #define ID_STCC LWID_('S','T','C','C') #define ID_TIME LWID_('T','I','M','E') #define ID_CONT LWID_('C','O','N','T') #define ID_BRIT LWID_('B','R','I','T') #define ID_SATR LWID_('S','A','T','R') #define ID_HUE LWID_('H','U','E',' ') #define ID_GAMM LWID_('G','A','M','M') #define ID_NEGA LWID_('N','E','G','A') #define ID_IFLT LWID_('I','F','L','T') #define ID_PFLT LWID_('P','F','L','T') /* surfaces */ #define ID_COLR LWID_('C','O','L','R') #define ID_LUMI LWID_('L','U','M','I') #define ID_DIFF LWID_('D','I','F','F') #define ID_SPEC LWID_('S','P','E','C') #define ID_GLOS LWID_('G','L','O','S') #define ID_REFL LWID_('R','E','F','L') #define ID_RFOP LWID_('R','F','O','P') #define ID_RIMG LWID_('R','I','M','G') #define ID_RSAN LWID_('R','S','A','N') #define ID_TRAN LWID_('T','R','A','N') #define ID_TROP LWID_('T','R','O','P') #define ID_TIMG LWID_('T','I','M','G') #define ID_RIND LWID_('R','I','N','D') #define ID_TRNL LWID_('T','R','N','L') #define ID_BUMP LWID_('B','U','M','P') #define ID_SMAN LWID_('S','M','A','N') #define ID_SIDE LWID_('S','I','D','E') #define ID_CLRH LWID_('C','L','R','H') #define ID_CLRF LWID_('C','L','R','F') #define ID_ADTR LWID_('A','D','T','R') #define ID_SHRP LWID_('S','H','R','P') #define ID_LINE LWID_('L','I','N','E') #define ID_LSIZ LWID_('L','S','I','Z') #define ID_ALPH LWID_('A','L','P','H') #define ID_AVAL LWID_('A','V','A','L') #define ID_GVAL LWID_('G','V','A','L') #define ID_BLOK LWID_('B','L','O','K') /* texture layer */ #define ID_TYPE LWID_('T','Y','P','E') #define ID_CHAN LWID_('C','H','A','N') #define ID_NAME LWID_('N','A','M','E') #define ID_ENAB LWID_('E','N','A','B') #define ID_OPAC LWID_('O','P','A','C') #define ID_FLAG LWID_('F','L','A','G') #define ID_PROJ LWID_('P','R','O','J') #define ID_STCK LWID_('S','T','C','K') #define ID_TAMP LWID_('T','A','M','P') /* texture coordinates */ #define ID_TMAP LWID_('T','M','A','P') #define ID_AXIS LWID_('A','X','I','S') #define ID_CNTR LWID_('C','N','T','R') #define ID_SIZE LWID_('S','I','Z','E') #define ID_ROTA LWID_('R','O','T','A') #define ID_OREF LWID_('O','R','E','F') #define ID_FALL LWID_('F','A','L','L') #define ID_CSYS LWID_('C','S','Y','S') /* image map */ #define ID_IMAP LWID_('I','M','A','P') #define ID_IMAG LWID_('I','M','A','G') #define ID_WRAP LWID_('W','R','A','P') #define ID_WRPW LWID_('W','R','P','W') #define ID_WRPH LWID_('W','R','P','H') #define ID_VMAP LWID_('V','M','A','P') #define ID_AAST LWID_('A','A','S','T') #define ID_PIXB LWID_('P','I','X','B') /* procedural */ #define ID_PROC LWID_('P','R','O','C') #define ID_COLR LWID_('C','O','L','R') #define ID_VALU LWID_('V','A','L','U') #define ID_FUNC LWID_('F','U','N','C') #define ID_FTPS LWID_('F','T','P','S') #define ID_ITPS LWID_('I','T','P','S') #define ID_ETPS LWID_('E','T','P','S') /* gradient */ #define ID_GRAD LWID_('G','R','A','D') #define ID_GRST LWID_('G','R','S','T') #define ID_GREN LWID_('G','R','E','N') #define ID_PNAM LWID_('P','N','A','M') #define ID_INAM LWID_('I','N','A','M') #define ID_GRPT LWID_('G','R','P','T') #define ID_FKEY LWID_('F','K','E','Y') #define ID_IKEY LWID_('I','K','E','Y') /* shader */ #define ID_SHDR LWID_('S','H','D','R') #define ID_DATA LWID_('D','A','T','A') /* generic linked list */ typedef struct st_lwNode { struct st_lwNode *next, *prev; void *data; } lwNode; /* plug-in reference */ typedef struct st_lwPlugin { struct st_lwPlugin *next, *prev; char *ord; char *name; int flags; void *data; } lwPlugin; /* envelopes */ typedef struct st_lwKey { struct st_lwKey *next, *prev; float value; float time; unsigned int shape; /* ID_TCB, ID_BEZ2, etc. */ float tension; float continuity; float bias; float param[ 4 ]; } lwKey; typedef struct st_lwEnvelope { struct st_lwEnvelope *next, *prev; int index; int type; char *name; lwKey *key; /* linked list of keys */ int nkeys; int behavior[ 2 ]; /* pre and post (extrapolation) */ lwPlugin *cfilter; /* linked list of channel filters */ int ncfilters; } lwEnvelope; #define BEH_RESET 0 #define BEH_CONSTANT 1 #define BEH_REPEAT 2 #define BEH_OSCILLATE 3 #define BEH_OFFSET 4 #define BEH_LINEAR 5 /* values that can be enveloped */ typedef struct st_lwEParam { float val; int eindex; } lwEParam; typedef struct st_lwVParam { float val[ 3 ]; int eindex; } lwVParam; /* clips */ typedef struct st_lwClipStill { char *name; } lwClipStill; typedef struct st_lwClipSeq { char *prefix; /* filename before sequence digits */ char *suffix; /* after digits, e.g. extensions */ int digits; int flags; int offset; int start; int end; } lwClipSeq; typedef struct st_lwClipAnim { char *name; char *server; /* anim loader plug-in */ void *data; } lwClipAnim; typedef struct st_lwClipXRef { char *string; int index; struct st_lwClip *clip; } lwClipXRef; typedef struct st_lwClipCycle { char *name; int lo; int hi; } lwClipCycle; typedef struct st_lwClip { struct st_lwClip *next, *prev; int index; unsigned int type; /* ID_STIL, ID_ISEQ, etc. */ union { lwClipStill still; lwClipSeq seq; lwClipAnim anim; lwClipXRef xref; lwClipCycle cycle; } source; float start_time; float duration; float frame_rate; lwEParam contrast; lwEParam brightness; lwEParam saturation; lwEParam hue; lwEParam gamma; int negative; lwPlugin *ifilter; /* linked list of image filters */ int nifilters; lwPlugin *pfilter; /* linked list of pixel filters */ int npfilters; } lwClip; /* textures */ typedef struct st_lwTMap { lwVParam size; lwVParam center; lwVParam rotate; lwVParam falloff; int fall_type; char *ref_object; int coord_sys; } lwTMap; typedef struct st_lwImageMap { int cindex; int projection; char *vmap_name; int axis; int wrapw_type; int wraph_type; lwEParam wrapw; lwEParam wraph; float aa_strength; int aas_flags; int pblend; lwEParam stck; lwEParam amplitude; } lwImageMap; #define PROJ_PLANAR 0 #define PROJ_CYLINDRICAL 1 #define PROJ_SPHERICAL 2 #define PROJ_CUBIC 3 #define PROJ_FRONT 4 #define WRAP_NONE 0 #define WRAP_EDGE 1 #define WRAP_REPEAT 2 #define WRAP_MIRROR 3 typedef struct st_lwProcedural { int axis; float value[ 3 ]; char *name; void *data; } lwProcedural; typedef struct st_lwGradKey { struct st_lwGradKey *next, *prev; float value; float rgba[ 4 ]; } lwGradKey; typedef struct st_lwGradient { char *paramname; char *itemname; float start; float end; int repeat; lwGradKey *key; /* array of gradient keys */ short *ikey; /* array of interpolation codes */ } lwGradient; typedef struct st_lwTexture { struct st_lwTexture *next, *prev; char *ord; unsigned int type; unsigned int chan; lwEParam opacity; short opac_type; short enabled; short negative; short axis; union { lwImageMap imap; lwProcedural proc; lwGradient grad; } param; lwTMap tmap; } lwTexture; /* values that can be textured */ typedef struct st_lwTParam { float val; int eindex; lwTexture *tex; /* linked list of texture layers */ } lwTParam; typedef struct st_lwCParam { float rgb[ 3 ]; int eindex; lwTexture *tex; /* linked list of texture layers */ } lwCParam; /* surfaces */ typedef struct st_lwGlow { short enabled; short type; lwEParam intensity; lwEParam size; } Glow; typedef struct st_lwRMap { lwTParam val; int options; int cindex; float seam_angle; } lwRMap; typedef struct st_lwLine { short enabled; unsigned short flags; lwEParam size; } lwLine; typedef struct st_lwSurface { struct st_lwSurface *next, *prev; char *name; char *srcname; lwCParam color; lwTParam luminosity; lwTParam diffuse; lwTParam specularity; lwTParam glossiness; lwRMap reflection; lwRMap transparency; lwTParam eta; lwTParam translucency; lwTParam bump; float smooth; int sideflags; float alpha; int alpha_mode; lwEParam color_hilite; lwEParam color_filter; lwEParam add_trans; lwEParam dif_sharp; lwEParam glow; lwLine line; lwPlugin *shader; /* linked list of shaders */ int nshaders; } lwSurface; /* vertex maps */ typedef struct st_lwVMap { struct st_lwVMap *next, *prev; char *name; unsigned int type; int dim; int nverts; int perpoly; int *vindex; /* array of point indexes */ int *pindex; /* array of polygon indexes */ float **val; } lwVMap; typedef struct st_lwVMapPt { lwVMap *vmap; int index; /* vindex or pindex element */ } lwVMapPt; /* points and polygons */ typedef struct st_lwPoint { float pos[ 3 ]; int npols; /* number of polygons sharing the point */ int *pol; /* array of polygon indexes */ int nvmaps; lwVMapPt *vm; /* array of vmap references */ } lwPoint; typedef struct st_lwPolVert { int index; /* index into the point array */ float norm[ 3 ]; int nvmaps; lwVMapPt *vm; /* array of vmap references */ } lwPolVert; typedef struct st_lwPolygon { lwSurface *surf; int part; /* part index */ int smoothgrp; /* smoothing group */ int flags; unsigned int type; float norm[ 3 ]; int nverts; lwPolVert *v; /* array of vertex records */ } lwPolygon; typedef struct st_lwPointList { int count; int offset; /* only used during reading */ lwPoint *pt; /* array of points */ } lwPointList; typedef struct st_lwPolygonList { int count; int offset; /* only used during reading */ int vcount; /* total number of vertices */ int voffset; /* only used during reading */ lwPolygon *pol; /* array of polygons */ } lwPolygonList; /* geometry layers */ typedef struct st_lwLayer { struct st_lwLayer *next, *prev; char *name; int index; int parent; int flags; float pivot[ 3 ]; float bbox[ 6 ]; lwPointList point; lwPolygonList polygon; int nvmaps; lwVMap *vmap; /* linked list of vmaps */ } lwLayer; /* tag strings */ typedef struct st_lwTagList { int count; int offset; /* only used during reading */ char **tag; /* array of strings */ } lwTagList; /* an object */ typedef struct st_lwObject { lwLayer *layer; /* linked list of layers */ lwEnvelope *env; /* linked list of envelopes */ lwClip *clip; /* linked list of clips */ lwSurface *surf; /* linked list of surfaces */ lwTagList taglist; int nlayers; int nenvs; int nclips; int nsurfs; } lwObject; /* lwo2.c */ void lwFreeLayer( lwLayer *layer ); void lwFreeObject( lwObject *object ); lwObject *lwGetObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ); int lwValidateObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ); /* pntspols.c */ void lwFreePoints( lwPointList *point ); void lwFreePolygons( lwPolygonList *plist ); int lwGetPoints( picoMemStream_t *fp, int cksize, lwPointList *point ); void lwGetBoundingBox( lwPointList *point, float bbox[] ); int lwAllocPolygons( lwPolygonList *plist, int npols, int nverts ); int lwGetPolygons( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset ); void lwGetPolyNormals( lwPointList *point, lwPolygonList *polygon ); int lwGetPointPolygons( lwPointList *point, lwPolygonList *polygon ); int lwResolvePolySurfaces( lwPolygonList *polygon, lwTagList *tlist, lwSurface **surf, int *nsurfs ); void lwGetVertNormals( lwPointList *point, lwPolygonList *polygon ); void lwFreeTags( lwTagList *tlist ); int lwGetTags( picoMemStream_t *fp, int cksize, lwTagList *tlist ); int lwGetPolygonTags( picoMemStream_t *fp, int cksize, lwTagList *tlist, lwPolygonList *plist ); /* vmap.c */ void lwFreeVMap( lwVMap *vmap ); lwVMap *lwGetVMap( picoMemStream_t *fp, int cksize, int ptoffset, int poloffset, int perpoly ); int lwGetPointVMaps( lwPointList *point, lwVMap *vmap ); int lwGetPolyVMaps( lwPolygonList *polygon, lwVMap *vmap ); /* clip.c */ void lwFreeClip( lwClip *clip ); lwClip *lwGetClip( picoMemStream_t *fp, int cksize ); lwClip *lwFindClip( lwClip *list, int index ); /* envelope.c */ void lwFreeEnvelope( lwEnvelope *env ); lwEnvelope *lwGetEnvelope( picoMemStream_t *fp, int cksize ); lwEnvelope *lwFindEnvelope( lwEnvelope *list, int index ); float lwEvalEnvelope( lwEnvelope *env, float time ); /* surface.c */ void lwFreePlugin( lwPlugin *p ); void lwFreeTexture( lwTexture *t ); void lwFreeSurface( lwSurface *surf ); int lwGetTHeader( picoMemStream_t *fp, int hsz, lwTexture *tex ); int lwGetTMap( picoMemStream_t *fp, int tmapsz, lwTMap *tmap ); int lwGetImageMap( picoMemStream_t *fp, int rsz, lwTexture *tex ); int lwGetProcedural( picoMemStream_t *fp, int rsz, lwTexture *tex ); int lwGetGradient( picoMemStream_t *fp, int rsz, lwTexture *tex ); lwTexture *lwGetTexture( picoMemStream_t *fp, int bloksz, unsigned int type ); lwPlugin *lwGetShader( picoMemStream_t *fp, int bloksz ); lwSurface *lwGetSurface( picoMemStream_t *fp, int cksize ); lwSurface *lwDefaultSurface( void ); /* lwob.c */ lwSurface *lwGetSurface5( picoMemStream_t *fp, int cksize, lwObject *obj ); int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset ); lwObject *lwGetObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ); int lwValidateObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ); /* list.c */ void lwListFree( void *list, void ( *freeNode )( void * )); void lwListAdd( void **list, void *node ); void lwListInsert( void **vlist, void *vitem, int ( *compare )( void *, void * )); /* vecmath.c */ float dot( float a[], float b[] ); void cross( float a[], float b[], float c[] ); void normalize( float v[] ); #define vecangle( a, b ) ( float ) acos( dot( a, b )) /* lwio.c */ void set_flen( int i ); int get_flen( void ); void *getbytes( picoMemStream_t *fp, int size ); void skipbytes( picoMemStream_t *fp, int n ); int getI1( picoMemStream_t *fp ); short getI2( picoMemStream_t *fp ); int getI4( picoMemStream_t *fp ); unsigned char getU1( picoMemStream_t *fp ); unsigned short getU2( picoMemStream_t *fp ); unsigned int getU4( picoMemStream_t *fp ); int getVX( picoMemStream_t *fp ); float getF4( picoMemStream_t *fp ); char *getS0( picoMemStream_t *fp ); int sgetI1( unsigned char **bp ); short sgetI2( unsigned char **bp ); int sgetI4( unsigned char **bp ); unsigned char sgetU1( unsigned char **bp ); unsigned short sgetU2( unsigned char **bp ); unsigned int sgetU4( unsigned char **bp ); int sgetVX( unsigned char **bp ); float sgetF4( unsigned char **bp ); char *sgetS0( unsigned char **bp ); #ifndef __BIG_ENDIAN__ void revbytes( void *bp, int elsize, int elcount ); #else #define revbytes( b, s, c ) #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/lwob.c���������������������������������������������������������0000664�0000000�0000000�00000044551�13217505464�0020536�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== lwob.c Functions for an LWOB reader. LWOB is the LightWave object format for versions of LW prior to 6.0. Ernie Wright 17 Sep 00 ====================================================================== */ #include "../picointernal.h" #include "lwo2.h" #include /* disable warnings */ #ifdef WIN32 #pragma warning( disable:4018 ) /* signed/unsigned mismatch */ #endif /* IDs specific to LWOB */ #define ID_SRFS LWID_('S','R','F','S') #define ID_FLAG LWID_('F','L','A','G') #define ID_VLUM LWID_('V','L','U','M') #define ID_VDIF LWID_('V','D','I','F') #define ID_VSPC LWID_('V','S','P','C') #define ID_RFLT LWID_('R','F','L','T') #define ID_BTEX LWID_('B','T','E','X') #define ID_CTEX LWID_('C','T','E','X') #define ID_DTEX LWID_('D','T','E','X') #define ID_LTEX LWID_('L','T','E','X') #define ID_RTEX LWID_('R','T','E','X') #define ID_STEX LWID_('S','T','E','X') #define ID_TTEX LWID_('T','T','E','X') #define ID_TFLG LWID_('T','F','L','G') #define ID_TSIZ LWID_('T','S','I','Z') #define ID_TCTR LWID_('T','C','T','R') #define ID_TFAL LWID_('T','F','A','L') #define ID_TVEL LWID_('T','V','E','L') #define ID_TCLR LWID_('T','C','L','R') #define ID_TVAL LWID_('T','V','A','L') #define ID_TAMP LWID_('T','A','M','P') #define ID_TIMG LWID_('T','I','M','G') #define ID_TAAS LWID_('T','A','A','S') #define ID_TREF LWID_('T','R','E','F') #define ID_TOPC LWID_('T','O','P','C') #define ID_SDAT LWID_('S','D','A','T') #define ID_TFP0 LWID_('T','F','P','0') #define ID_TFP1 LWID_('T','F','P','1') /* ====================================================================== add_clip() Add a clip to the clip list. Used to store the contents of an RIMG or TIMG surface subchunk. ====================================================================== */ static int add_clip( char *s, lwClip **clist, int *nclips ) { lwClip *clip; char *p; clip = _pico_calloc( 1, sizeof( lwClip )); if ( !clip ) return 0; clip->contrast.val = 1.0f; clip->brightness.val = 1.0f; clip->saturation.val = 1.0f; clip->gamma.val = 1.0f; if ( (p = strstr( s, "(sequence)" ))) { p[ -1 ] = 0; clip->type = ID_ISEQ; clip->source.seq.prefix = s; clip->source.seq.digits = 3; } else { clip->type = ID_STIL; clip->source.still.name = s; } (*nclips)++; clip->index = *nclips; lwListAdd( (void *) clist, clip ); return clip->index; } /* ====================================================================== add_tvel() Add a triple of envelopes to simulate the old texture velocity parameters. ====================================================================== */ static int add_tvel( float pos[], float vel[], lwEnvelope **elist, int *nenvs ) { lwEnvelope *env = NULL; lwKey *key0, *key1; int i; for ( i = 0; i < 3; i++ ) { env = _pico_calloc( 1, sizeof( lwEnvelope )); key0 = _pico_calloc( 1, sizeof( lwKey )); key1 = _pico_calloc( 1, sizeof( lwKey )); if ( !env || !key0 || !key1 ) return 0; key0->next = key1; key0->value = pos[ i ]; key0->time = 0.0f; key1->prev = key0; key1->value = pos[ i ] + vel[ i ] * 30.0f; key1->time = 1.0f; key0->shape = key1->shape = ID_LINE; env->index = *nenvs + i + 1; env->type = 0x0301 + i; env->name = _pico_alloc( 11 ); if ( env->name ) { strcpy( env->name, "Position.X" ); env->name[ 9 ] += i; } env->key = key0; env->nkeys = 2; env->behavior[ 0 ] = BEH_LINEAR; env->behavior[ 1 ] = BEH_LINEAR; lwListAdd( (void *) elist, env ); } *nenvs += 3; return env->index - 2; } /* ====================================================================== get_texture() Create a new texture for BTEX, CTEX, etc. subchunks. ====================================================================== */ static lwTexture *get_texture( char *s ) { lwTexture *tex; tex = _pico_calloc( 1, sizeof( lwTexture )); if ( !tex ) return NULL; tex->tmap.size.val[ 0 ] = tex->tmap.size.val[ 1 ] = tex->tmap.size.val[ 2 ] = 1.0f; tex->opacity.val = 1.0f; tex->enabled = 1; if ( strstr( s, "Image Map" )) { tex->type = ID_IMAP; if ( strstr( s, "Planar" )) tex->param.imap.projection = 0; else if ( strstr( s, "Cylindrical" )) tex->param.imap.projection = 1; else if ( strstr( s, "Spherical" )) tex->param.imap.projection = 2; else if ( strstr( s, "Cubic" )) tex->param.imap.projection = 3; else if ( strstr( s, "Front" )) tex->param.imap.projection = 4; tex->param.imap.aa_strength = 1.0f; tex->param.imap.amplitude.val = 1.0f; _pico_free( s ); } else { tex->type = ID_PROC; tex->param.proc.name = s; } return tex; } /* ====================================================================== lwGetSurface5() Read an lwSurface from an LWOB file. ====================================================================== */ lwSurface *lwGetSurface5( picoMemStream_t *fp, int cksize, lwObject *obj ) { lwSurface *surf; lwTexture *tex; lwPlugin *shdr; char *s; float v[ 3 ]; unsigned int id, flags; unsigned short sz; int pos, rlen, i; tex = NULL; shdr = NULL; /* allocate the Surface structure */ surf = _pico_calloc( 1, sizeof( lwSurface )); if ( !surf ) goto Fail; /* non-zero defaults */ surf->color.rgb[ 0 ] = 0.78431f; surf->color.rgb[ 1 ] = 0.78431f; surf->color.rgb[ 2 ] = 0.78431f; surf->diffuse.val = 1.0f; surf->glossiness.val = 0.4f; surf->bump.val = 1.0f; surf->eta.val = 1.0f; surf->sideflags = 1; /* remember where we started */ set_flen( 0 ); pos = (int)_pico_memstream_tell( fp ); /* name */ surf->name = getS0( fp ); /* first subchunk header */ id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) goto Fail; /* process subchunks as they're encountered */ while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_COLR: surf->color.rgb[ 0 ] = getU1( fp ) / 255.0f; surf->color.rgb[ 1 ] = getU1( fp ) / 255.0f; surf->color.rgb[ 2 ] = getU1( fp ) / 255.0f; break; case ID_FLAG: flags = getU2( fp ); if ( flags & 4 ) surf->smooth = 1.56207f; if ( flags & 8 ) surf->color_hilite.val = 1.0f; if ( flags & 16 ) surf->color_filter.val = 1.0f; if ( flags & 128 ) surf->dif_sharp.val = 0.5f; if ( flags & 256 ) surf->sideflags = 3; if ( flags & 512 ) surf->add_trans.val = 1.0f; break; case ID_LUMI: surf->luminosity.val = getI2( fp ) / 256.0f; break; case ID_VLUM: surf->luminosity.val = getF4( fp ); break; case ID_DIFF: surf->diffuse.val = getI2( fp ) / 256.0f; break; case ID_VDIF: surf->diffuse.val = getF4( fp ); break; case ID_SPEC: surf->specularity.val = getI2( fp ) / 256.0f; break; case ID_VSPC: surf->specularity.val = getF4( fp ); break; case ID_GLOS: surf->glossiness.val = ( float ) log( getU2( fp )) / 20.7944f; break; case ID_SMAN: surf->smooth = getF4( fp ); break; case ID_REFL: surf->reflection.val.val = getI2( fp ) / 256.0f; break; case ID_RFLT: surf->reflection.options = getU2( fp ); break; case ID_RIMG: s = getS0( fp ); surf->reflection.cindex = add_clip( s, &obj->clip, &obj->nclips ); surf->reflection.options = 3; break; case ID_RSAN: surf->reflection.seam_angle = getF4( fp ); break; case ID_TRAN: surf->transparency.val.val = getI2( fp ) / 256.0f; break; case ID_RIND: surf->eta.val = getF4( fp ); break; case ID_BTEX: s = getbytes( fp, sz ); tex = get_texture( s ); lwListAdd( (void *) &surf->bump.tex, tex ); break; case ID_CTEX: s = getbytes( fp, sz ); tex = get_texture( s ); lwListAdd( (void *) &surf->color.tex, tex ); break; case ID_DTEX: s = getbytes( fp, sz ); tex = get_texture( s ); lwListAdd( (void *) &surf->diffuse.tex, tex ); break; case ID_LTEX: s = getbytes( fp, sz ); tex = get_texture( s ); lwListAdd( (void *) &surf->luminosity.tex, tex ); break; case ID_RTEX: s = getbytes( fp, sz ); tex = get_texture( s ); lwListAdd( (void *) &surf->reflection.val.tex, tex ); break; case ID_STEX: s = getbytes( fp, sz ); tex = get_texture( s ); lwListAdd( (void *) &surf->specularity.tex, tex ); break; case ID_TTEX: s = getbytes( fp, sz ); tex = get_texture( s ); lwListAdd( (void *) &surf->transparency.val.tex, tex ); break; case ID_TFLG: flags = getU2( fp ); i = 0; // greebo: initialise to fix compiler warnings if ( flags & 1 ) i = 0; if ( flags & 2 ) i = 1; if ( flags & 4 ) i = 2; tex->axis = i; if ( tex->type == ID_IMAP ) tex->param.imap.axis = i; else tex->param.proc.axis = i; if ( flags & 8 ) tex->tmap.coord_sys = 1; if ( flags & 16 ) tex->negative = 1; if ( flags & 32 ) tex->param.imap.pblend = 1; if ( flags & 64 ) { tex->param.imap.aa_strength = 1.0f; tex->param.imap.aas_flags = 1; } break; case ID_TSIZ: for ( i = 0; i < 3; i++ ) tex->tmap.size.val[ i ] = getF4( fp ); break; case ID_TCTR: for ( i = 0; i < 3; i++ ) tex->tmap.center.val[ i ] = getF4( fp ); break; case ID_TFAL: for ( i = 0; i < 3; i++ ) tex->tmap.falloff.val[ i ] = getF4( fp ); break; case ID_TVEL: for ( i = 0; i < 3; i++ ) v[ i ] = getF4( fp ); tex->tmap.center.eindex = add_tvel( tex->tmap.center.val, v, &obj->env, &obj->nenvs ); break; case ID_TCLR: if ( tex->type == ID_PROC ) for ( i = 0; i < 3; i++ ) tex->param.proc.value[ i ] = getU1( fp ) / 255.0f; break; case ID_TVAL: tex->param.proc.value[ 0 ] = getI2( fp ) / 256.0f; break; case ID_TAMP: if ( tex->type == ID_IMAP ) tex->param.imap.amplitude.val = getF4( fp ); break; case ID_TIMG: s = getS0( fp ); tex->param.imap.cindex = add_clip( s, &obj->clip, &obj->nclips ); break; case ID_TAAS: tex->param.imap.aa_strength = getF4( fp ); tex->param.imap.aas_flags = 1; break; case ID_TREF: tex->tmap.ref_object = getbytes( fp, sz ); break; case ID_TOPC: tex->opacity.val = getF4( fp ); break; case ID_TFP0: if ( tex->type == ID_IMAP ) tex->param.imap.wrapw.val = getF4( fp ); break; case ID_TFP1: if ( tex->type == ID_IMAP ) tex->param.imap.wraph.val = getF4( fp ); break; case ID_SHDR: shdr = _pico_calloc( 1, sizeof( lwPlugin )); if ( !shdr ) goto Fail; shdr->name = getbytes( fp, sz ); lwListAdd( (void *) &surf->shader, shdr ); surf->nshaders++; break; case ID_SDAT: shdr->data = getbytes( fp, sz ); break; default: break; } /* error while reading current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) goto Fail; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the SURF chunk? */ if ( cksize <= _pico_memstream_tell( fp ) - pos ) break; /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) goto Fail; } return surf; Fail: if ( surf ) lwFreeSurface( surf ); return NULL; } /* ====================================================================== lwGetPolygons5() Read polygon records from a POLS chunk in an LWOB file. The polygons are added to the array in the lwPolygonList. ====================================================================== */ int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset ) { lwPolygon *pp; lwPolVert *pv; unsigned char *buf, *bp; int i, j, nv, nverts, npols; if ( cksize == 0 ) return 1; /* read the whole chunk */ set_flen( 0 ); buf = getbytes( fp, cksize ); if ( !buf ) goto Fail; /* count the polygons and vertices */ nverts = 0; npols = 0; bp = buf; while ( bp < buf + cksize ) { nv = sgetU2( &bp ); nverts += nv; npols++; bp += 2 * nv; i = sgetI2( &bp ); if ( i < 0 ) bp += 2; /* detail polygons */ } if ( !lwAllocPolygons( plist, npols, nverts )) goto Fail; /* fill in the new polygons */ bp = buf; pp = plist->pol + plist->offset; pv = plist->pol[ 0 ].v + plist->voffset; for ( i = 0; i < npols; i++ ) { nv = sgetU2( &bp ); pp->nverts = nv; pp->type = ID_FACE; if ( !pp->v ) pp->v = pv; for ( j = 0; j < nv; j++ ) pv[ j ].index = sgetU2( &bp ) + ptoffset; j = sgetI2( &bp ); if ( j < 0 ) { j = -j; bp += 2; } j -= 1; pp->surf = ( lwSurface * ) (intptr_t) j; pp++; pv += nv; } _pico_free( buf ); return 1; Fail: if ( buf ) _pico_free( buf ); lwFreePolygons( plist ); return 0; } /* ====================================================================== getLWObject5() Returns the contents of an LWOB, given its filename, or NULL if the file couldn't be loaded. On failure, failID and failpos can be used to diagnose the cause. 1. If the file isn't an LWOB, failpos will contain 12 and failID will be unchanged. 2. If an error occurs while reading an LWOB, failID will contain the most recently read IFF chunk ID, and failpos will contain the value returned by _pico_memstream_tell() at the time of the failure. 3. If the file couldn't be opened, or an error occurs while reading the first 12 bytes, both failID and failpos will be unchanged. If you don't need this information, failID and failpos can be NULL. ====================================================================== */ lwObject *lwGetObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ) { lwObject *object; lwLayer *layer; lwNode *node; unsigned int id, formsize, type, cksize; /* open the file */ if ( !fp ) return NULL; /* read the first 12 bytes */ set_flen( 0 ); id = getU4( fp ); formsize = getU4( fp ); type = getU4( fp ); if ( 12 != get_flen() ) { return NULL; } /* LWOB? */ if ( id != ID_FORM || type != ID_LWOB ) { if ( failpos ) *failpos = 12; return NULL; } /* allocate an object and a default layer */ object = _pico_calloc( 1, sizeof( lwObject )); if ( !object ) goto Fail; layer = _pico_calloc( 1, sizeof( lwLayer )); if ( !layer ) goto Fail; object->layer = layer; object->nlayers = 1; /* get the first chunk header */ id = getU4( fp ); cksize = getU4( fp ); if ( 0 > get_flen() ) goto Fail; /* process chunks as they're encountered */ while ( 1 ) { cksize += cksize & 1; switch ( id ) { case ID_PNTS: if ( !lwGetPoints( fp, cksize, &layer->point )) goto Fail; break; case ID_POLS: if ( !lwGetPolygons5( fp, cksize, &layer->polygon, layer->point.offset )) goto Fail; break; case ID_SRFS: if ( !lwGetTags( fp, cksize, &object->taglist )) goto Fail; break; case ID_SURF: node = ( lwNode * ) lwGetSurface5( fp, cksize, object ); if ( !node ) goto Fail; lwListAdd( (void *) &object->surf, node ); object->nsurfs++; break; default: _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR ); break; } /* end of the file? */ if ( formsize <= _pico_memstream_tell( fp ) - 8 ) break; /* get the next chunk header */ set_flen( 0 ); id = getU4( fp ); cksize = getU4( fp ); if ( 8 != get_flen() ) goto Fail; } lwGetBoundingBox( &layer->point, layer->bbox ); lwGetPolyNormals( &layer->point, &layer->polygon ); if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail; if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist, &object->surf, &object->nsurfs )) goto Fail; lwGetVertNormals( &layer->point, &layer->polygon ); return object; Fail: if ( failID ) *failID = id; if ( fp ) { if ( failpos ) *failpos = (int)_pico_memstream_tell( fp ); } lwFreeObject( object ); return NULL; } int lwValidateObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ) { unsigned int id, formsize, type; /* open the file */ if ( !fp ) return PICO_PMV_ERROR_MEMORY; /* read the first 12 bytes */ set_flen( 0 ); id = getU4( fp ); formsize = getU4( fp ); type = getU4( fp ); if ( 12 != get_flen() ) { return PICO_PMV_ERROR_SIZE; } /* LWOB? */ if ( id != ID_FORM || type != ID_LWOB ) { if ( failpos ) *failpos = 12; formsize++; /* silence compiler warning about unused variable */ return PICO_PMV_ERROR_IDENT; } return PICO_PMV_OK; } �������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/pntspols.c�����������������������������������������������������0000664�0000000�0000000�00000034435�13217505464�0021455�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== pntspols.c Point and polygon functions for an LWO2 reader. Ernie Wright 17 Sep 00 ====================================================================== */ #include "../picointernal.h" #include "lwo2.h" #include /* ====================================================================== lwFreePoints() Free the memory used by an lwPointList. ====================================================================== */ void lwFreePoints( lwPointList *point ) { int i; if ( point ) { if ( point->pt ) { for ( i = 0; i < point->count; i++ ) { if ( point->pt[ i ].pol ) _pico_free( point->pt[ i ].pol ); if ( point->pt[ i ].vm ) _pico_free( point->pt[ i ].vm ); } _pico_free( point->pt ); } memset( point, 0, sizeof( lwPointList )); } } /* ====================================================================== lwFreePolygons() Free the memory used by an lwPolygonList. ====================================================================== */ void lwFreePolygons( lwPolygonList *plist ) { int i, j; if ( plist ) { if ( plist->pol ) { for ( i = 0; i < plist->count; i++ ) { if ( plist->pol[ i ].v ) { for ( j = 0; j < plist->pol[ i ].nverts; j++ ) if ( plist->pol[ i ].v[ j ].vm ) _pico_free( plist->pol[ i ].v[ j ].vm ); } } if ( plist->pol[ 0 ].v ) _pico_free( plist->pol[ 0 ].v ); _pico_free( plist->pol ); } memset( plist, 0, sizeof( lwPolygonList )); } } /* ====================================================================== lwGetPoints() Read point records from a PNTS chunk in an LWO2 file. The points are added to the array in the lwPointList. ====================================================================== */ int lwGetPoints( picoMemStream_t *fp, int cksize, lwPointList *point ) { float *f; int np, i, j; if ( cksize == 1 ) return 1; /* extend the point array to hold the new points */ np = cksize / 12; point->offset = point->count; point->count += np; if ( !_pico_realloc( (void *) &point->pt, (point->count - np) * sizeof( lwPoint ), point->count * sizeof( lwPoint )) ) return 0; memset( &point->pt[ point->offset ], 0, np * sizeof( lwPoint )); /* read the whole chunk */ f = ( float * ) getbytes( fp, cksize ); if ( !f ) return 0; revbytes( f, 4, np * 3 ); /* assign position values */ for ( i = 0, j = 0; i < np; i++, j += 3 ) { point->pt[ i ].pos[ 0 ] = f[ j ]; point->pt[ i ].pos[ 1 ] = f[ j + 1 ]; point->pt[ i ].pos[ 2 ] = f[ j + 2 ]; } _pico_free( f ); return 1; } /* ====================================================================== lwGetBoundingBox() Calculate the bounding box for a point list, but only if the bounding box hasn't already been initialized. ====================================================================== */ void lwGetBoundingBox( lwPointList *point, float bbox[] ) { int i, j; if ( point->count == 0 ) return; for ( i = 0; i < 6; i++ ) if ( bbox[ i ] != 0.0f ) return; bbox[ 0 ] = bbox[ 1 ] = bbox[ 2 ] = 1e20f; bbox[ 3 ] = bbox[ 4 ] = bbox[ 5 ] = -1e20f; for ( i = 0; i < point->count; i++ ) { for ( j = 0; j < 3; j++ ) { if ( bbox[ j ] > point->pt[ i ].pos[ j ] ) bbox[ j ] = point->pt[ i ].pos[ j ]; if ( bbox[ j + 3 ] < point->pt[ i ].pos[ j ] ) bbox[ j + 3 ] = point->pt[ i ].pos[ j ]; } } } /* ====================================================================== lwAllocPolygons() Allocate or extend the polygon arrays to hold new records. ====================================================================== */ int lwAllocPolygons( lwPolygonList *plist, int npols, int nverts ) { int i; plist->offset = plist->count; plist->count += npols; if ( !_pico_realloc( (void *) &plist->pol, (plist->count - npols) * sizeof( lwPolygon ), plist->count * sizeof( lwPolygon )) ) return 0; memset( plist->pol + plist->offset, 0, npols * sizeof( lwPolygon )); plist->voffset = plist->vcount; plist->vcount += nverts; if ( !_pico_realloc( (void *) &plist->pol[ 0 ].v, (plist->vcount - nverts) * sizeof( lwPolVert ), plist->vcount * sizeof( lwPolVert )) ) return 0; memset( plist->pol[ 0 ].v + plist->voffset, 0, nverts * sizeof( lwPolVert )); /* fix up the old vertex pointers */ for ( i = 1; i < plist->offset; i++ ) plist->pol[ i ].v = plist->pol[ i - 1 ].v + plist->pol[ i - 1 ].nverts; return 1; } /* ====================================================================== lwGetPolygons() Read polygon records from a POLS chunk in an LWO2 file. The polygons are added to the array in the lwPolygonList. ====================================================================== */ int lwGetPolygons( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset ) { lwPolygon *pp; lwPolVert *pv; unsigned char *buf, *bp; int i, j, flags, nv, nverts, npols; unsigned int type; if ( cksize == 0 ) return 1; /* read the whole chunk */ set_flen( 0 ); type = getU4( fp ); buf = getbytes( fp, cksize - 4 ); if ( cksize != get_flen() ) goto Fail; /* count the polygons and vertices */ nverts = 0; npols = 0; bp = buf; while ( bp < buf + cksize - 4 ) { nv = sgetU2( &bp ); nv &= 0x03FF; nverts += nv; npols++; for ( i = 0; i < nv; i++ ) j = sgetVX( &bp ); } if ( !lwAllocPolygons( plist, npols, nverts )) goto Fail; /* fill in the new polygons */ bp = buf; pp = plist->pol + plist->offset; pv = plist->pol[ 0 ].v + plist->voffset; for ( i = 0; i < npols; i++ ) { nv = sgetU2( &bp ); flags = nv & 0xFC00; nv &= 0x03FF; pp->nverts = nv; pp->flags = flags; pp->type = type; if ( !pp->v ) pp->v = pv; for ( j = 0; j < nv; j++ ) pp->v[ j ].index = sgetVX( &bp ) + ptoffset; pp++; pv += nv; } _pico_free( buf ); return 1; Fail: if ( buf ) _pico_free( buf ); lwFreePolygons( plist ); return 0; } /* ====================================================================== lwGetPolyNormals() Calculate the polygon normals. By convention, LW's polygon normals are found as the cross product of the first and last edges. It's undefined for one- and two-point polygons. ====================================================================== */ void lwGetPolyNormals( lwPointList *point, lwPolygonList *polygon ) { int i, j; float p1[ 3 ], p2[ 3 ], pn[ 3 ], v1[ 3 ], v2[ 3 ]; for ( i = 0; i < polygon->count; i++ ) { if ( polygon->pol[ i ].nverts < 3 ) continue; for ( j = 0; j < 3; j++ ) { p1[ j ] = point->pt[ polygon->pol[ i ].v[ 0 ].index ].pos[ j ]; p2[ j ] = point->pt[ polygon->pol[ i ].v[ 1 ].index ].pos[ j ]; pn[ j ] = point->pt[ polygon->pol[ i ].v[ polygon->pol[ i ].nverts - 1 ].index ].pos[ j ]; } for ( j = 0; j < 3; j++ ) { v1[ j ] = p2[ j ] - p1[ j ]; v2[ j ] = pn[ j ] - p1[ j ]; } cross( v1, v2, polygon->pol[ i ].norm ); normalize( polygon->pol[ i ].norm ); } } /* ====================================================================== lwGetPointPolygons() For each point, fill in the indexes of the polygons that share the point. Returns 0 if any of the memory allocations fail, otherwise returns 1. ====================================================================== */ int lwGetPointPolygons( lwPointList *point, lwPolygonList *polygon ) { int i, j, k; /* count the number of polygons per point */ for ( i = 0; i < polygon->count; i++ ) for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) ++point->pt[ polygon->pol[ i ].v[ j ].index ].npols; /* alloc per-point polygon arrays */ for ( i = 0; i < point->count; i++ ) { if ( point->pt[ i ].npols == 0 ) continue; point->pt[ i ].pol = _pico_calloc( point->pt[ i ].npols, sizeof( int )); if ( !point->pt[ i ].pol ) return 0; point->pt[ i ].npols = 0; } /* fill in polygon array for each point */ for ( i = 0; i < polygon->count; i++ ) { for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) { k = polygon->pol[ i ].v[ j ].index; point->pt[ k ].pol[ point->pt[ k ].npols ] = i; ++point->pt[ k ].npols; } } return 1; } /* ====================================================================== lwResolvePolySurfaces() Convert tag indexes into actual lwSurface pointers. If any polygons point to tags for which no corresponding surface can be found, a default surface is created. ====================================================================== */ int lwResolvePolySurfaces( lwPolygonList *polygon, lwTagList *tlist, lwSurface **surf, int *nsurfs ) { lwSurface **s, *st; int i; intptr_t index; if ( tlist->count == 0 ) return 1; s = _pico_calloc( tlist->count, sizeof( lwSurface * )); if ( !s ) return 0; for ( i = 0; i < tlist->count; i++ ) { st = *surf; while ( st ) { if ( !strcmp( st->name, tlist->tag[ i ] )) { s[ i ] = st; break; } st = st->next; } } for ( i = 0; i < polygon->count; i++ ) { index = ( intptr_t ) polygon->pol[ i ].surf; if ( index < 0 || index > tlist->count ) return 0; if ( !s[ index ] ) { s[ index ] = lwDefaultSurface(); if ( !s[ index ] ) return 0; s[ index ]->name = _pico_alloc( strlen( tlist->tag[ index ] ) + 1 ); if ( !s[ index ]->name ) return 0; strcpy( s[ index ]->name, tlist->tag[ index ] ); lwListAdd( (void *) surf, s[ index ] ); *nsurfs = *nsurfs + 1; } polygon->pol[ i ].surf = s[ index ]; } _pico_free( s ); return 1; } /* ====================================================================== lwGetVertNormals() Calculate the vertex normals. For each polygon vertex, sum the normals of the polygons that share the point. If the normals of the current and adjacent polygons form an angle greater than the max smoothing angle for the current polygon's surface, the normal of the adjacent polygon is excluded from the sum. It's also excluded if the polygons aren't in the same smoothing group. Assumes that lwGetPointPolygons(), lwGetPolyNormals() and lwResolvePolySurfaces() have already been called. ====================================================================== */ void lwGetVertNormals( lwPointList *point, lwPolygonList *polygon ) { int j, k, n, g, h, p; float a; for ( j = 0; j < polygon->count; j++ ) { for ( n = 0; n < polygon->pol[ j ].nverts; n++ ) { for ( k = 0; k < 3; k++ ) polygon->pol[ j ].v[ n ].norm[ k ] = polygon->pol[ j ].norm[ k ]; if ( polygon->pol[ j ].surf->smooth <= 0 ) continue; p = polygon->pol[ j ].v[ n ].index; for ( g = 0; g < point->pt[ p ].npols; g++ ) { h = point->pt[ p ].pol[ g ]; if ( h == j ) continue; if ( polygon->pol[ j ].smoothgrp != polygon->pol[ h ].smoothgrp ) continue; a = vecangle( polygon->pol[ j ].norm, polygon->pol[ h ].norm ); if ( a > polygon->pol[ j ].surf->smooth ) continue; for ( k = 0; k < 3; k++ ) polygon->pol[ j ].v[ n ].norm[ k ] += polygon->pol[ h ].norm[ k ]; } normalize( polygon->pol[ j ].v[ n ].norm ); } } } /* ====================================================================== lwFreeTags() Free memory used by an lwTagList. ====================================================================== */ void lwFreeTags( lwTagList *tlist ) { int i; if ( tlist ) { if ( tlist->tag ) { for ( i = 0; i < tlist->count; i++ ) if ( tlist->tag[ i ] ) _pico_free( tlist->tag[ i ] ); _pico_free( tlist->tag ); } memset( tlist, 0, sizeof( lwTagList )); } } /* ====================================================================== lwGetTags() Read tag strings from a TAGS chunk in an LWO2 file. The tags are added to the lwTagList array. ====================================================================== */ int lwGetTags( picoMemStream_t *fp, int cksize, lwTagList *tlist ) { char *buf, *bp; int i, ntags; size_t len; if ( cksize == 0 ) return 1; /* read the whole chunk */ set_flen( 0 ); buf = getbytes( fp, cksize ); if ( !buf ) return 0; /* count the strings */ ntags = 0; bp = buf; while ( bp < buf + cksize ) { len = strlen( bp ) + 1; len += len & 1; bp += len; ++ntags; } /* expand the string array to hold the new tags */ tlist->offset = tlist->count; tlist->count += ntags; if ( !_pico_realloc( (void *) &tlist->tag, (tlist->count - ntags) * sizeof( char * ), tlist->count * sizeof( char * )) ) goto Fail; memset( &tlist->tag[ tlist->offset ], 0, ntags * sizeof( char * )); /* copy the new tags to the tag array */ bp = buf; for ( i = 0; i < ntags; i++ ) tlist->tag[ i + tlist->offset ] = sgetS0( (unsigned char **) &bp ); _pico_free( buf ); return 1; Fail: if ( buf ) _pico_free( buf ); return 0; } /* ====================================================================== lwGetPolygonTags() Read polygon tags from a PTAG chunk in an LWO2 file. ====================================================================== */ int lwGetPolygonTags( picoMemStream_t *fp, int cksize, lwTagList *tlist, lwPolygonList *plist ) { unsigned int type; int rlen = 0, i, j; set_flen( 0 ); type = getU4( fp ); rlen = get_flen(); if ( rlen < 0 ) return 0; if ( type != ID_SURF && type != ID_PART && type != ID_SMGP ) { _pico_memstream_seek( fp, cksize - 4, PICO_SEEK_CUR ); return 1; } while ( rlen < cksize ) { i = getVX( fp ) + plist->offset; j = getVX( fp ) + tlist->offset; rlen = get_flen(); if ( rlen < 0 || rlen > cksize ) return 0; switch ( type ) { case ID_SURF: plist->pol[ i ].surf = ( lwSurface * ) (intptr_t) j; break; case ID_PART: plist->pol[ i ].part = j; break; case ID_SMGP: plist->pol[ i ].smoothgrp = j; break; } } return 1; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/surface.c������������������������������������������������������0000664�0000000�0000000�00000062121�13217505464�0021214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== surface.c Surface functions for an LWO2 reader. Ernie Wright 17 Sep 00 ====================================================================== */ #include "../picointernal.h" #include "lwo2.h" /* ====================================================================== lwFreePlugin() Free the memory used by an lwPlugin. ====================================================================== */ void lwFreePlugin( lwPlugin *p ) { if ( p ) { if ( p->ord ) _pico_free( p->ord ); if ( p->name ) _pico_free( p->name ); if ( p->data ) _pico_free( p->data ); _pico_free( p ); } } /* ====================================================================== lwFreeTexture() Free the memory used by an lwTexture. ====================================================================== */ void lwFreeTexture( lwTexture *t ) { if ( t ) { if ( t->ord ) _pico_free( t->ord ); switch ( t->type ) { case ID_IMAP: if ( t->param.imap.vmap_name ) _pico_free( t->param.imap.vmap_name ); if ( t->tmap.ref_object ) _pico_free( t->tmap.ref_object ); break; case ID_PROC: if ( t->param.proc.name ) _pico_free( t->param.proc.name ); if ( t->param.proc.data ) _pico_free( t->param.proc.data ); break; case ID_GRAD: if ( t->param.grad.key ) _pico_free( t->param.grad.key ); if ( t->param.grad.ikey ) _pico_free( t->param.grad.ikey ); break; } _pico_free( t ); } } /* ====================================================================== lwFreeSurface() Free the memory used by an lwSurface. ====================================================================== */ void lwFreeSurface( lwSurface *surf ) { if ( surf ) { if ( surf->name ) _pico_free( surf->name ); if ( surf->srcname ) _pico_free( surf->srcname ); lwListFree( surf->shader, (void *) lwFreePlugin ); lwListFree( surf->color.tex, (void *) lwFreeTexture ); lwListFree( surf->luminosity.tex, (void *) lwFreeTexture ); lwListFree( surf->diffuse.tex, (void *) lwFreeTexture ); lwListFree( surf->specularity.tex, (void *) lwFreeTexture ); lwListFree( surf->glossiness.tex, (void *) lwFreeTexture ); lwListFree( surf->reflection.val.tex, (void *) lwFreeTexture ); lwListFree( surf->transparency.val.tex, (void *) lwFreeTexture ); lwListFree( surf->eta.tex, (void *) lwFreeTexture ); lwListFree( surf->translucency.tex, (void *) lwFreeTexture ); lwListFree( surf->bump.tex, (void *) lwFreeTexture ); _pico_free( surf ); } } /* ====================================================================== lwGetTHeader() Read a texture map header from a SURF.BLOK in an LWO2 file. This is the first subchunk in a BLOK, and its contents are common to all three texture types. ====================================================================== */ int lwGetTHeader( picoMemStream_t *fp, int hsz, lwTexture *tex ) { unsigned int id; unsigned short sz; int pos, rlen; /* remember where we started */ set_flen( 0 ); pos = (int)_pico_memstream_tell( fp ); /* ordinal string */ tex->ord = getS0( fp ); /* first subchunk header */ id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) return 0; /* process subchunks as they're encountered */ while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_CHAN: tex->chan = getU4( fp ); break; case ID_OPAC: tex->opac_type = getU2( fp ); tex->opacity.val = getF4( fp ); tex->opacity.eindex = getVX( fp ); break; case ID_ENAB: tex->enabled = getU2( fp ); break; case ID_NEGA: tex->negative = getU2( fp ); break; case ID_AXIS: tex->axis = getU2( fp ); break; default: break; } /* error while reading current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) return 0; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the texture header subchunk? */ if ( hsz <= _pico_memstream_tell( fp ) - pos ) break; /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) return 0; } set_flen( (int)_pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetTMap() Read a texture map from a SURF.BLOK in an LWO2 file. The TMAP defines the mapping from texture to world or object coordinates. ====================================================================== */ int lwGetTMap( picoMemStream_t *fp, int tmapsz, lwTMap *tmap ) { unsigned int id; unsigned short sz; int rlen, pos, i; pos = (int)_pico_memstream_tell( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) return 0; while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_SIZE: for ( i = 0; i < 3; i++ ) tmap->size.val[ i ] = getF4( fp ); tmap->size.eindex = getVX( fp ); break; case ID_CNTR: for ( i = 0; i < 3; i++ ) tmap->center.val[ i ] = getF4( fp ); tmap->center.eindex = getVX( fp ); break; case ID_ROTA: for ( i = 0; i < 3; i++ ) tmap->rotate.val[ i ] = getF4( fp ); tmap->rotate.eindex = getVX( fp ); break; case ID_FALL: tmap->fall_type = getU2( fp ); for ( i = 0; i < 3; i++ ) tmap->falloff.val[ i ] = getF4( fp ); tmap->falloff.eindex = getVX( fp ); break; case ID_OREF: tmap->ref_object = getS0( fp ); break; case ID_CSYS: tmap->coord_sys = getU2( fp ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) return 0; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the TMAP subchunk? */ if ( tmapsz <= _pico_memstream_tell( fp ) - pos ) break; /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) return 0; } set_flen( (int)_pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetImageMap() Read an lwImageMap from a SURF.BLOK in an LWO2 file. ====================================================================== */ int lwGetImageMap( picoMemStream_t *fp, int rsz, lwTexture *tex ) { unsigned int id; unsigned short sz; int rlen, pos; pos = (int)_pico_memstream_tell( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) return 0; while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_TMAP: if ( !lwGetTMap( fp, sz, &tex->tmap )) return 0; break; case ID_PROJ: tex->param.imap.projection = getU2( fp ); break; case ID_VMAP: tex->param.imap.vmap_name = getS0( fp ); break; case ID_AXIS: tex->param.imap.axis = getU2( fp ); break; case ID_IMAG: tex->param.imap.cindex = getVX( fp ); break; case ID_WRAP: tex->param.imap.wrapw_type = getU2( fp ); tex->param.imap.wraph_type = getU2( fp ); break; case ID_WRPW: tex->param.imap.wrapw.val = getF4( fp ); tex->param.imap.wrapw.eindex = getVX( fp ); break; case ID_WRPH: tex->param.imap.wraph.val = getF4( fp ); tex->param.imap.wraph.eindex = getVX( fp ); break; case ID_AAST: tex->param.imap.aas_flags = getU2( fp ); tex->param.imap.aa_strength = getF4( fp ); break; case ID_PIXB: tex->param.imap.pblend = getU2( fp ); break; case ID_STCK: tex->param.imap.stck.val = getF4( fp ); tex->param.imap.stck.eindex = getVX( fp ); break; case ID_TAMP: tex->param.imap.amplitude.val = getF4( fp ); tex->param.imap.amplitude.eindex = getVX( fp ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) return 0; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the image map? */ if ( rsz <= _pico_memstream_tell( fp ) - pos ) break; /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) return 0; } set_flen( (int)_pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetProcedural() Read an lwProcedural from a SURF.BLOK in an LWO2 file. ====================================================================== */ int lwGetProcedural( picoMemStream_t *fp, int rsz, lwTexture *tex ) { unsigned int id; unsigned short sz; int rlen, pos; pos = (int)_pico_memstream_tell( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) return 0; while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_TMAP: if ( !lwGetTMap( fp, sz, &tex->tmap )) return 0; break; case ID_AXIS: tex->param.proc.axis = getU2( fp ); break; case ID_VALU: tex->param.proc.value[ 0 ] = getF4( fp ); if ( sz >= 8 ) tex->param.proc.value[ 1 ] = getF4( fp ); if ( sz >= 12 ) tex->param.proc.value[ 2 ] = getF4( fp ); break; case ID_FUNC: tex->param.proc.name = getS0( fp ); rlen = get_flen(); tex->param.proc.data = getbytes( fp, sz - rlen ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) return 0; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the procedural block? */ if ( rsz <= _pico_memstream_tell( fp ) - pos ) break; /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) return 0; } set_flen( (int)_pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetGradient() Read an lwGradient from a SURF.BLOK in an LWO2 file. ====================================================================== */ int lwGetGradient( picoMemStream_t *fp, int rsz, lwTexture *tex ) { unsigned int id; unsigned short sz; int rlen, pos, i, j, nkeys; pos = (int)_pico_memstream_tell( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) return 0; while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_TMAP: if ( !lwGetTMap( fp, sz, &tex->tmap )) return 0; break; case ID_PNAM: tex->param.grad.paramname = getS0( fp ); break; case ID_INAM: tex->param.grad.itemname = getS0( fp ); break; case ID_GRST: tex->param.grad.start = getF4( fp ); break; case ID_GREN: tex->param.grad.end = getF4( fp ); break; case ID_GRPT: tex->param.grad.repeat = getU2( fp ); break; case ID_FKEY: nkeys = sz / sizeof( lwGradKey ); tex->param.grad.key = _pico_calloc( nkeys, sizeof( lwGradKey )); if ( !tex->param.grad.key ) return 0; for ( i = 0; i < nkeys; i++ ) { tex->param.grad.key[ i ].value = getF4( fp ); for ( j = 0; j < 4; j++ ) tex->param.grad.key[ i ].rgba[ j ] = getF4( fp ); } break; case ID_IKEY: nkeys = sz / 2; tex->param.grad.ikey = _pico_calloc( nkeys, sizeof( short )); if ( !tex->param.grad.ikey ) return 0; for ( i = 0; i < nkeys; i++ ) tex->param.grad.ikey[ i ] = getU2( fp ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) return 0; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the gradient? */ if ( rsz <= _pico_memstream_tell( fp ) - pos ) break; /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) return 0; } set_flen( (int)_pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetTexture() Read an lwTexture from a SURF.BLOK in an LWO2 file. ====================================================================== */ lwTexture *lwGetTexture( picoMemStream_t *fp, int bloksz, unsigned int type ) { lwTexture *tex; unsigned short sz; int ok; tex = _pico_calloc( 1, sizeof( lwTexture )); if ( !tex ) return NULL; tex->type = type; tex->tmap.size.val[ 0 ] = tex->tmap.size.val[ 1 ] = tex->tmap.size.val[ 2 ] = 1.0f; tex->opacity.val = 1.0f; tex->enabled = 1; sz = getU2( fp ); if ( !lwGetTHeader( fp, sz, tex )) { _pico_free( tex ); return NULL; } sz = bloksz - sz - 6; switch ( type ) { case ID_IMAP: ok = lwGetImageMap( fp, sz, tex ); break; case ID_PROC: ok = lwGetProcedural( fp, sz, tex ); break; case ID_GRAD: ok = lwGetGradient( fp, sz, tex ); break; default: ok = !_pico_memstream_seek( fp, sz, PICO_SEEK_CUR ); } if ( !ok ) { lwFreeTexture( tex ); return NULL; } set_flen( bloksz ); return tex; } /* ====================================================================== lwGetShader() Read a shader record from a SURF.BLOK in an LWO2 file. ====================================================================== */ lwPlugin *lwGetShader( picoMemStream_t *fp, int bloksz ) { lwPlugin *shdr; unsigned int id; unsigned short sz; int hsz, rlen, pos; shdr = _pico_calloc( 1, sizeof( lwPlugin )); if ( !shdr ) return NULL; pos = (int)_pico_memstream_tell( fp ); set_flen( 0 ); hsz = getU2( fp ); shdr->ord = getS0( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) goto Fail; while ( hsz > 0 ) { sz += sz & 1; hsz -= sz; if ( id == ID_ENAB ) { shdr->flags = getU2( fp ); break; } else { _pico_memstream_seek( fp, sz, PICO_SEEK_CUR ); id = getU4( fp ); sz = getU2( fp ); } } id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) goto Fail; while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_FUNC: shdr->name = getS0( fp ); rlen = get_flen(); shdr->data = getbytes( fp, sz - rlen ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) goto Fail; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the shader block? */ if ( bloksz <= _pico_memstream_tell( fp ) - pos ) break; /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) goto Fail; } set_flen( (int)_pico_memstream_tell( fp ) - pos ); return shdr; Fail: lwFreePlugin( shdr ); return NULL; } /* ====================================================================== compare_textures() compare_shaders() Callbacks for the lwListInsert() function, which is called to add textures to surface channels and shaders to surfaces. ====================================================================== */ static int compare_textures( lwTexture *a, lwTexture *b ) { if (a->ord != NULL && b->ord != NULL) { return strcmp( a->ord, b->ord ); } else if (a->ord != NULL) { return 1; } else if (b->ord != NULL) { return -1; } else { // Both strings are NULL return 0; } } static int compare_shaders( lwPlugin *a, lwPlugin *b ) { if (a->ord != NULL && b->ord != NULL) { return strcmp( a->ord, b->ord ); } else if (a->ord != NULL) { return 1; } else if (b->ord != NULL) { return -1; } else { // Both strings are NULL return 0; } } /* ====================================================================== add_texture() Finds the surface channel (lwTParam or lwCParam) to which a texture is applied, then calls lwListInsert(). ====================================================================== */ static int add_texture( lwSurface *surf, lwTexture *tex ) { lwTexture **list; switch ( tex->chan ) { case ID_COLR: list = &surf->color.tex; break; case ID_LUMI: list = &surf->luminosity.tex; break; case ID_DIFF: list = &surf->diffuse.tex; break; case ID_SPEC: list = &surf->specularity.tex; break; case ID_GLOS: list = &surf->glossiness.tex; break; case ID_REFL: list = &surf->reflection.val.tex; break; case ID_TRAN: list = &surf->transparency.val.tex; break; case ID_RIND: list = &surf->eta.tex; break; case ID_TRNL: list = &surf->translucency.tex; break; case ID_BUMP: list = &surf->bump.tex; break; default: return 0; } lwListInsert( (void **) list, tex, ( void *) compare_textures ); return 1; } /* ====================================================================== lwDefaultSurface() Allocate and initialize a surface. ====================================================================== */ lwSurface *lwDefaultSurface( void ) { lwSurface *surf; surf = _pico_calloc( 1, sizeof( lwSurface )); if ( !surf ) return NULL; surf->color.rgb[ 0 ] = 0.78431f; surf->color.rgb[ 1 ] = 0.78431f; surf->color.rgb[ 2 ] = 0.78431f; surf->diffuse.val = 1.0f; surf->glossiness.val = 0.4f; surf->bump.val = 1.0f; surf->eta.val = 1.0f; surf->sideflags = 1; return surf; } /* ====================================================================== lwGetSurface() Read an lwSurface from an LWO2 file. ====================================================================== */ lwSurface *lwGetSurface( picoMemStream_t *fp, int cksize ) { lwSurface *surf; lwTexture *tex; lwPlugin *shdr; unsigned int id, type; unsigned short sz; int pos, rlen; /* allocate the Surface structure */ surf = _pico_calloc( 1, sizeof( lwSurface )); if ( !surf ) goto Fail; /* non-zero defaults */ surf->color.rgb[ 0 ] = 0.78431f; surf->color.rgb[ 1 ] = 0.78431f; surf->color.rgb[ 2 ] = 0.78431f; surf->diffuse.val = 1.0f; surf->glossiness.val = 0.4f; surf->bump.val = 1.0f; surf->eta.val = 1.0f; surf->sideflags = 1; /* remember where we started */ set_flen( 0 ); pos = (int)_pico_memstream_tell( fp ); /* names */ surf->name = getS0( fp ); surf->srcname = getS0( fp ); /* first subchunk header */ id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) goto Fail; /* process subchunks as they're encountered */ while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_COLR: surf->color.rgb[ 0 ] = getF4( fp ); surf->color.rgb[ 1 ] = getF4( fp ); surf->color.rgb[ 2 ] = getF4( fp ); surf->color.eindex = getVX( fp ); break; case ID_LUMI: surf->luminosity.val = getF4( fp ); surf->luminosity.eindex = getVX( fp ); break; case ID_DIFF: surf->diffuse.val = getF4( fp ); surf->diffuse.eindex = getVX( fp ); break; case ID_SPEC: surf->specularity.val = getF4( fp ); surf->specularity.eindex = getVX( fp ); break; case ID_GLOS: surf->glossiness.val = getF4( fp ); surf->glossiness.eindex = getVX( fp ); break; case ID_REFL: surf->reflection.val.val = getF4( fp ); surf->reflection.val.eindex = getVX( fp ); break; case ID_RFOP: surf->reflection.options = getU2( fp ); break; case ID_RIMG: surf->reflection.cindex = getVX( fp ); break; case ID_RSAN: surf->reflection.seam_angle = getF4( fp ); break; case ID_TRAN: surf->transparency.val.val = getF4( fp ); surf->transparency.val.eindex = getVX( fp ); break; case ID_TROP: surf->transparency.options = getU2( fp ); break; case ID_TIMG: surf->transparency.cindex = getVX( fp ); break; case ID_RIND: surf->eta.val = getF4( fp ); surf->eta.eindex = getVX( fp ); break; case ID_TRNL: surf->translucency.val = getF4( fp ); surf->translucency.eindex = getVX( fp ); break; case ID_BUMP: surf->bump.val = getF4( fp ); surf->bump.eindex = getVX( fp ); break; case ID_SMAN: surf->smooth = getF4( fp ); break; case ID_SIDE: surf->sideflags = getU2( fp ); break; case ID_CLRH: surf->color_hilite.val = getF4( fp ); surf->color_hilite.eindex = getVX( fp ); break; case ID_CLRF: surf->color_filter.val = getF4( fp ); surf->color_filter.eindex = getVX( fp ); break; case ID_ADTR: surf->add_trans.val = getF4( fp ); surf->add_trans.eindex = getVX( fp ); break; case ID_SHRP: surf->dif_sharp.val = getF4( fp ); surf->dif_sharp.eindex = getVX( fp ); break; case ID_GVAL: surf->glow.val = getF4( fp ); surf->glow.eindex = getVX( fp ); break; case ID_LINE: surf->line.enabled = 1; if ( sz >= 2 ) surf->line.flags = getU2( fp ); if ( sz >= 6 ) surf->line.size.val = getF4( fp ); if ( sz >= 8 ) surf->line.size.eindex = getVX( fp ); break; case ID_ALPH: surf->alpha_mode = getU2( fp ); surf->alpha = getF4( fp ); break; case ID_AVAL: surf->alpha = getF4( fp ); break; case ID_BLOK: type = getU4( fp ); switch ( type ) { case ID_IMAP: case ID_PROC: case ID_GRAD: tex = lwGetTexture( fp, sz - 4, type ); if ( !tex ) goto Fail; if ( !add_texture( surf, tex )) lwFreeTexture( tex ); set_flen( 4 + get_flen() ); break; case ID_SHDR: shdr = lwGetShader( fp, sz - 4 ); if ( !shdr ) goto Fail; lwListInsert( (void **) &surf->shader, shdr, (void *) compare_shaders ); ++surf->nshaders; set_flen( 4 + get_flen() ); break; } break; default: break; } /* error while reading current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) goto Fail; /* skip unread parts of the current subchunk */ if ( rlen < sz ) _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); /* end of the SURF chunk? */ if ( cksize <= _pico_memstream_tell( fp ) - pos ) break; /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) goto Fail; } return surf; Fail: if ( surf ) lwFreeSurface( surf ); return NULL; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/vecmath.c������������������������������������������������������0000664�0000000�0000000�00000001272�13217505464�0021213�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== vecmath.c Basic vector and matrix functions. Ernie Wright 17 Sep 00 ====================================================================== */ #include float dot( float a[], float b[] ) { return a[ 0 ] * b[ 0 ] + a[ 1 ] * b[ 1 ] + a[ 2 ] * b[ 2 ]; } void cross( float a[], float b[], float c[] ) { c[ 0 ] = a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ]; c[ 1 ] = a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ]; c[ 2 ] = a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ]; } void normalize( float v[] ) { float r; r = ( float ) sqrt( dot( v, v )); if ( r > 0 ) { v[ 0 ] /= r; v[ 1 ] /= r; v[ 2 ] /= r; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/lwo/vmap.c���������������������������������������������������������0000664�0000000�0000000�00000013464�13217505464�0020535�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ====================================================================== vmap.c Vertex map functions for an LWO2 reader. Ernie Wright 17 Sep 00 ====================================================================== */ #include "../picointernal.h" #include "lwo2.h" /* ====================================================================== lwFreeVMap() Free memory used by an lwVMap. ====================================================================== */ void lwFreeVMap( lwVMap *vmap ) { if ( vmap ) { if ( vmap->name ) _pico_free( vmap->name ); if ( vmap->vindex ) _pico_free( vmap->vindex ); if ( vmap->pindex ) _pico_free( vmap->pindex ); if ( vmap->val ) { if ( vmap->val[ 0 ] ) _pico_free( vmap->val[ 0 ] ); _pico_free( vmap->val ); } _pico_free( vmap ); } } /* ====================================================================== lwGetVMap() Read an lwVMap from a VMAP or VMAD chunk in an LWO2. ====================================================================== */ lwVMap *lwGetVMap( picoMemStream_t *fp, int cksize, int ptoffset, int poloffset, int perpoly ) { unsigned char *buf, *bp; lwVMap *vmap; float *f; int i, j, npts, rlen; /* read the whole chunk */ set_flen( 0 ); buf = getbytes( fp, cksize ); if ( !buf ) return NULL; vmap = _pico_calloc( 1, sizeof( lwVMap )); if ( !vmap ) { _pico_free( buf ); return NULL; } /* initialize the vmap */ vmap->perpoly = perpoly; bp = buf; set_flen( 0 ); vmap->type = sgetU4( &bp ); vmap->dim = sgetU2( &bp ); vmap->name = sgetS0( &bp ); rlen = get_flen(); /* count the vmap records */ npts = 0; while ( bp < buf + cksize ) { i = sgetVX( &bp ); if ( perpoly ) i = sgetVX( &bp ); bp += vmap->dim * sizeof( float ); ++npts; } /* allocate the vmap */ vmap->nverts = npts; vmap->vindex = _pico_calloc( npts, sizeof( int )); if ( !vmap->vindex ) goto Fail; if ( perpoly ) { vmap->pindex = _pico_calloc( npts, sizeof( int )); if ( !vmap->pindex ) goto Fail; } if ( vmap->dim > 0 ) { vmap->val = _pico_calloc( npts, sizeof( float * )); if ( !vmap->val ) goto Fail; f = _pico_alloc( npts * vmap->dim * sizeof( float )); if ( !f ) goto Fail; for ( i = 0; i < npts; i++ ) vmap->val[ i ] = f + i * vmap->dim; } /* fill in the vmap values */ bp = buf + rlen; for ( i = 0; i < npts; i++ ) { vmap->vindex[ i ] = sgetVX( &bp ); if ( perpoly ) vmap->pindex[ i ] = sgetVX( &bp ); for ( j = 0; j < vmap->dim; j++ ) vmap->val[ i ][ j ] = sgetF4( &bp ); } _pico_free( buf ); return vmap; Fail: if ( buf ) _pico_free( buf ); lwFreeVMap( vmap ); return NULL; } /* ====================================================================== lwGetPointVMaps() Fill in the lwVMapPt structure for each point. ====================================================================== */ int lwGetPointVMaps( lwPointList *point, lwVMap *vmap ) { lwVMap *vm; int i, j, n; /* count the number of vmap values for each point */ vm = vmap; while ( vm ) { if ( !vm->perpoly ) for ( i = 0; i < vm->nverts; i++ ) ++point->pt[ vm->vindex[ i ]].nvmaps; vm = vm->next; } /* allocate vmap references for each mapped point */ for ( i = 0; i < point->count; i++ ) { if ( point->pt[ i ].nvmaps ) { point->pt[ i ].vm = _pico_calloc( point->pt[ i ].nvmaps, sizeof( lwVMapPt )); if ( !point->pt[ i ].vm ) return 0; point->pt[ i ].nvmaps = 0; } } /* fill in vmap references for each mapped point */ vm = vmap; while ( vm ) { if ( !vm->perpoly ) { for ( i = 0; i < vm->nverts; i++ ) { j = vm->vindex[ i ]; n = point->pt[ j ].nvmaps; point->pt[ j ].vm[ n ].vmap = vm; point->pt[ j ].vm[ n ].index = i; ++point->pt[ j ].nvmaps; } } vm = vm->next; } return 1; } /* ====================================================================== lwGetPolyVMaps() Fill in the lwVMapPt structure for each polygon vertex. ====================================================================== */ int lwGetPolyVMaps( lwPolygonList *polygon, lwVMap *vmap ) { lwVMap *vm; lwPolVert *pv; int i, j; /* count the number of vmap values for each polygon vertex */ vm = vmap; while ( vm ) { if ( vm->perpoly ) { for ( i = 0; i < vm->nverts; i++ ) { for ( j = 0; j < polygon->pol[ vm->pindex[ i ]].nverts; j++ ) { pv = &polygon->pol[ vm->pindex[ i ]].v[ j ]; if ( vm->vindex[ i ] == pv->index ) { ++pv->nvmaps; break; } } } } vm = vm->next; } /* allocate vmap references for each mapped vertex */ for ( i = 0; i < polygon->count; i++ ) { for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) { pv = &polygon->pol[ i ].v[ j ]; if ( pv->nvmaps ) { pv->vm = _pico_calloc( pv->nvmaps, sizeof( lwVMapPt )); if ( !pv->vm ) return 0; pv->nvmaps = 0; } } } /* fill in vmap references for each mapped point */ vm = vmap; while ( vm ) { if ( vm->perpoly ) { for ( i = 0; i < vm->nverts; i++ ) { for ( j = 0; j < polygon->pol[ vm->pindex[ i ]].nverts; j++ ) { pv = &polygon->pol[ vm->pindex[ i ]].v[ j ]; if ( vm->vindex[ i ] == pv->index ) { pv->vm[ pv->nvmaps ].vmap = vm; pv->vm[ pv->nvmaps ].index = i; ++pv->nvmaps; break; } } } } vm = vm->next; } return 1; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/picointernal.c�����������������������������������������������������0000664�0000000�0000000�00000066077�13217505464�0021470�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define PICOINTERNAL_C /* todo: * - fix p->curLine for parser routines. increased twice */ /* dependencies */ #include #include #include #include "picointernal.h" /* function pointers */ void *(*_pico_ptr_malloc )( size_t ) = malloc; void (*_pico_ptr_free )( void* ) = free; void (*_pico_ptr_load_file )( char*, unsigned char**, int* ) = NULL; void (*_pico_ptr_free_file )( void* ) = NULL; void (*_pico_ptr_print )( int, const char* ) = NULL; typedef union { float f; char c[4]; } floatSwapUnion; /* _pico_alloc: * kludged memory allocation wrapper */ void *_pico_alloc( size_t size ) { void *ptr; /* some sanity checks */ if( size == 0 ) return NULL; if (_pico_ptr_malloc == NULL) return NULL; /* allocate memory */ ptr = _pico_ptr_malloc(size); if (ptr == NULL) return NULL; /* zero out allocated memory */ memset(ptr,0,size); /* return pointer to allocated memory */ return ptr; } /* _pico_calloc: * _pico_calloc wrapper */ void *_pico_calloc( size_t num, size_t size ) { void *ptr; /* some sanity checks */ if( num == 0 || size == 0 ) return NULL; if (_pico_ptr_malloc == NULL) return NULL; /* allocate memory */ ptr = _pico_ptr_malloc(num*size); if (ptr == NULL) return NULL; /* zero out allocated memory */ memset(ptr,0,num*size); /* return pointer to allocated memory */ return ptr; } /* _pico_realloc: * memory reallocation wrapper (note: only grows, * but never shrinks or frees) */ void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize ) { void *ptr2; /* sanity checks */ if( ptr == NULL ) return NULL; if( newSize < oldSize ) return *ptr; if (_pico_ptr_malloc == NULL) return NULL; /* allocate new pointer */ ptr2 = _pico_alloc( newSize ); if( ptr2 == NULL ) return NULL; /* copy */ if( *ptr != NULL ) { memcpy( ptr2, *ptr, oldSize ); _pico_free( *ptr ); } /* fix up and return */ *ptr = ptr2; return *ptr; } /* _pico_clone_alloc: * handy function for quick string allocation/copy. it clones * the given string and returns a pointer to the new allocated * clone (which must be freed by caller of course) or returns * NULL on memory alloc or param errors. if 'size' is -1 the * length of the input string is used, otherwise 'size' is used * as custom clone size (the string is cropped to fit into mem * if needed). -sea */ char *_pico_clone_alloc( const char *str ) { char* cloned; /* sanity check */ if (str == NULL) return NULL; /* allocate memory */ cloned = _pico_alloc( strlen(str) + 1 ); if (cloned == NULL) return NULL; /* copy input string to cloned string */ strcpy( cloned, str ); /* return ptr to cloned string */ return cloned; } /* _pico_free: * wrapper around the free function pointer */ void _pico_free( void *ptr ) { /* sanity checks */ if( ptr == NULL ) return; if (_pico_ptr_free == NULL) return; /* free the allocated memory */ _pico_ptr_free( ptr ); } /* _pico_load_file: * wrapper around the loadfile function pointer */ void _pico_load_file( char *name, unsigned char **buffer, int *bufSize ) { /* sanity checks */ if( name == NULL ) { *bufSize = -1; return; } if (_pico_ptr_load_file == NULL) { *bufSize = -1; return; } /* do the actual call to read in the file; */ /* BUFFER IS ALLOCATED BY THE EXTERNAL LOADFILE FUNC */ _pico_ptr_load_file( name,buffer,bufSize ); } /* _pico_free_file: * wrapper around the file free function pointer */ void _pico_free_file( void *buffer ) { /* sanity checks */ if( buffer == NULL ) return; /* use default free */ if( _pico_ptr_free_file == NULL ) { free( buffer ); return; } /* free the allocated file */ _pico_ptr_free_file( buffer ); } /* _pico_printf: * wrapper around the print function pointer -sea */ void _pico_printf( int level, const char *format, ...) { char str[4096]; va_list argptr; /* sanity checks */ if( format == NULL ) return; if (_pico_ptr_print == NULL) return; /* format string */ va_start( argptr,format ); vsprintf( str,format,argptr ); va_end( argptr ); /* remove linefeeds */ if (str[ strlen(str)-1 ] == '\n') str[ strlen(str)-1 ] = '\0'; /* do the actual call */ _pico_ptr_print( level,str ); } /* _pico_first_token: * trims everything after the first whitespace-delimited token */ void _pico_first_token( char *str ) { if( !str || !*str ) return; while( *str && !isspace( *str ) ) str++; *str = '\0'; } /* _pico_strltrim: * left trims the given string -sea */ char *_pico_strltrim( char *str ) { char *str1 = str, *str2 = str; while (isspace(*str2)) str2++; if( str2 != str ) while( *str2 != '\0' ) /* fix: ydnar */ *str1++ = *str2++; return str; } /* _pico_strrtrim: * right trims the given string -sea */ char *_pico_strrtrim( char *str ) { if (str && *str) { char *str1 = str; int allspace = 1; while (*str1) { if (allspace && !isspace(*str1)) allspace = 0; str1++; } if (allspace) *str = '\0'; else { str1--; while ((isspace(*str1)) && (str1 >= str)) *str1-- = '\0'; } } return str; } /* _pico_strlwr: * pico internal string-to-lower routine. */ char *_pico_strlwr( char *str ) { char *cp; for (cp=str; *cp; ++cp) { if ('A' <= *cp && *cp <= 'Z') { *cp += ('a' - 'A'); } } return str; } /* _pico_strchcount: * counts how often the given char appears in str. -sea */ int _pico_strchcount( char *str, int ch ) { int count = 0; while (*str++) if (*str == ch) count++; return count; } void _pico_zero_bounds( picoVec3_t mins, picoVec3_t maxs ) { int i; for (i=0; i<3; i++) { mins[i] = +999999; maxs[i] = -999999; } } void _pico_expand_bounds( picoVec3_t p, picoVec3_t mins, picoVec3_t maxs ) { int i; for (i=0; i<3; i++) { double value = p[i]; if (value < mins[i]) mins[i] = value; if (value > maxs[i]) maxs[i] = value; } } void _pico_zero_vec( picoVec3_t vec ) { vec[ 0 ] = vec[ 1 ] = vec[ 2 ] = 0; } void _pico_zero_vec2( picoVec2_t vec ) { vec[ 0 ] = vec[ 1 ] = 0; } void _pico_zero_vec4( picoVec4_t vec ) { vec[ 0 ] = vec[ 1 ] = vec[ 2 ] = vec[ 3 ] = 0; } void _pico_set_vec( picoVec3_t v, double a, double b, double c ) { v[ 0 ] = a; v[ 1 ] = b; v[ 2 ] = c; } void _pico_set_vec4( picoVec4_t v, double a, double b, double c, double d ) { v[ 0 ] = a; v[ 1 ] = b; v[ 2 ] = c; v[ 3 ] = d; } void _pico_copy_vec( picoVec3_t src, picoVec3_t dest ) { dest[ 0 ] = src[ 0 ]; dest[ 1 ] = src[ 1 ]; dest[ 2 ] = src[ 2 ]; } void _pico_copy_vec2( picoVec2_t src, picoVec2_t dest ) { dest[ 0 ] = src[ 0 ]; dest[ 1 ] = src[ 1 ]; } void _pico_copy_vec4( picoVec4_t src, picoVec4_t dest ) { dest[ 0 ] = src[ 0 ]; dest[ 1 ] = src[ 1 ]; dest[ 2 ] = src[ 2 ]; dest[ 3 ] = src[ 3 ]; } /* ydnar */ picoVec_t _pico_normalize_vec( picoVec3_t vec ) { double len, ilen; len = sqrt( vec[ 0 ] * vec[ 0 ] + vec[ 1 ] * vec[ 1 ] + vec[ 2 ] * vec[ 2 ] ); if( len == 0.0 ) return 0.0; ilen = 1.0 / len; vec[ 0 ] *= (picoVec_t) ilen; vec[ 1 ] *= (picoVec_t) ilen; vec[ 2 ] *= (picoVec_t) ilen; return (picoVec_t) len; } void _pico_add_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ) { dest[ 0 ] = a[ 0 ] + b[ 0 ]; dest[ 1 ] = a[ 1 ] + b[ 1 ]; dest[ 2 ] = a[ 2 ] + b[ 2 ]; } void _pico_subtract_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ) { dest[ 0 ] = a[ 0 ] - b[ 0 ]; dest[ 1 ] = a[ 1 ] - b[ 1 ]; dest[ 2 ] = a[ 2 ] - b[ 2 ]; } void _pico_scale_vec( picoVec3_t v, float scale, picoVec3_t dest ) { dest[ 0 ] = v[ 0 ] * scale; dest[ 1 ] = v[ 1 ] * scale; dest[ 2 ] = v[ 2 ] * scale; } void _pico_scale_vec4( picoVec4_t v, float scale, picoVec4_t dest ) { dest[ 0 ] = v[ 0 ] * scale; dest[ 1 ] = v[ 1 ] * scale; dest[ 2 ] = v[ 2 ] * scale; dest[ 3 ] = v[ 3 ] * scale; } picoVec_t _pico_dot_vec( picoVec3_t a, picoVec3_t b ) { return a[ 0 ] * b[ 0 ] + a[ 1 ] * b[ 1 ] + a[ 2 ] * b[ 2 ]; } void _pico_cross_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ) { dest[ 0 ] = a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ]; dest[ 1 ] = a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ]; dest[ 2 ] = a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ]; } picoVec_t _pico_calc_plane( picoVec4_t plane, picoVec3_t a, picoVec3_t b, picoVec3_t c ) { picoVec3_t ba, ca; _pico_subtract_vec( b, a, ba ); _pico_subtract_vec( c, a, ca ); _pico_cross_vec( ca, ba, plane ); plane[ 3 ] = _pico_dot_vec( a, plane ); return _pico_normalize_vec( plane ); } /* separate from _pico_set_vec4 */ void _pico_set_color( picoColor_t c, int r, int g, int b, int a ) { c[ 0 ] = r; c[ 1 ] = g; c[ 2 ] = b; c[ 3 ] = a; } void _pico_copy_color( picoColor_t src, picoColor_t dest ) { dest[ 0 ] = src[ 0 ]; dest[ 1 ] = src[ 1 ]; dest[ 2 ] = src[ 2 ]; dest[ 3 ] = src[ 3 ]; } #ifdef __BIG_ENDIAN__ int _pico_big_long ( int src ) { return src; } short _pico_big_short( short src ) { return src; } float _pico_big_float( float src ) { return src; } int _pico_little_long( int src ) { return ((src & 0xFF000000) >> 24) | ((src & 0x00FF0000) >> 8) | ((src & 0x0000FF00) << 8) | ((src & 0x000000FF) << 24); } short _pico_little_short( short src ) { return ((src & 0xFF00) >> 8) | ((src & 0x00FF) << 8); } float _pico_little_float( float src ) { floatSwapUnion in,out; in.f = src; out.c[ 0 ] = in.c[ 3 ]; out.c[ 1 ] = in.c[ 2 ]; out.c[ 2 ] = in.c[ 1 ]; out.c[ 3 ] = in.c[ 0 ]; return out.f; } #else /*__BIG_ENDIAN__*/ int _pico_little_long ( int src ) { return src; } short _pico_little_short( short src ) { return src; } float _pico_little_float( float src ) { return src; } int _pico_big_long( int src ) { return ((src & 0xFF000000) >> 24) | ((src & 0x00FF0000) >> 8) | ((src & 0x0000FF00) << 8) | ((src & 0x000000FF) << 24); } short _pico_big_short( short src ) { return ((src & 0xFF00) >> 8) | ((src & 0x00FF) << 8); } float _pico_big_float( float src ) { floatSwapUnion in,out; in.f = src; out.c[ 0 ] = in.c[ 3 ]; out.c[ 1 ] = in.c[ 2 ]; out.c[ 2 ] = in.c[ 1 ]; out.c[ 3 ] = in.c[ 0 ]; return out.f; } #endif /*__BIG_ENDIAN__*/ /* _pico_stristr: * case-insensitive strstr. -sea */ char *_pico_stristr( char *str, const char *substr ) { const size_t sublen = strlen(substr); while (*str) { if (!_pico_strnicmp(str,substr,sublen)) break; str++; } if (!(*str)) str = NULL; return str; } /* _pico_unixify() changes dos \ style path separators to / */ void _pico_unixify( char *path ) { if( path == NULL ) return; while( *path ) { if( *path == '\\' ) *path = '/'; path++; } } /* _pico_nofname: * removes file name portion from given file path and converts * the directory separators to un*x style. returns 1 on success * or 0 when 'destSize' was exceeded. -sea */ int _pico_nofname( const char *path, char *dest, int destSize ) { int left = destSize; char *temp = dest; while ((*dest = *path) != '\0') { if (*dest == '/' || *dest == '\\') { temp = (dest + 1); *dest = '/'; } dest++; path++; if (--left < 1) { *temp = '\0'; return 0; } } *temp = '\0'; return 1; } /* _pico_nopath: * returns ptr to filename portion in given path or an empty * string otherwise. given 'path' is not altered. -sea */ const char *_pico_nopath( const char *path ) { const char *src; src = path + (strlen(path) - 1); if (path == NULL) return ""; if (!strchr(path,'/') && !strchr(path,'\\')) return (path); while ((src--) != path) { if (*src == '/' || *src == '\\') return (++src); } return ""; } /* _pico_setfext: * sets/changes the file extension for the given filename * or filepath's filename portion. the given 'path' *is* * altered. leave 'ext' empty to remove extension. -sea */ char *_pico_setfext( char *path, const char *ext ) { char *src; int remfext = 0; src = path + (strlen(path) - 1); if (ext == NULL) ext = ""; if (strlen(ext ) < 1) remfext = 1; if (strlen(path) < 1) return path; while ((src--) != path) { if (*src == '/' || *src == '\\') return path; if (*src == '.') { if (remfext) { *src = '\0'; return path; } *(++src) = '\0'; break; } } strcat(path,ext); return path; } /* _pico_getline: * extracts one line from the given buffer and stores it in dest. * returns -1 on error or the length of the line on success. i've * removed string trimming here. this can be done manually by the * calling func. */ int _pico_getline( char *buf, int bufsize, char *dest, int destsize ) { int pos; /* check output */ if (dest == NULL || destsize < 1) return -1; memset( dest,0,destsize ); /* check input */ if (buf == NULL || bufsize < 1) return -1; /* get next line */ for (pos=0; poscursor == NULL) return; /* skin white spaces */ while( 1 ) { /* sanity checks */ if (p->cursor < p->buffer || p->cursor >= p->max) { return; } /* break for chars other than white spaces */ if (*p->cursor > 0x20) break; if (*p->cursor == 0x00) return; /* a bit of linefeed handling */ if (*p->cursor == '\n') { *hasLFs = 1; p->curLine++; } /* go to next character */ p->cursor++; } } /* _pico_new_parser: * allocates a new ascii parser object. */ picoParser_t *_pico_new_parser( picoByte_t *buffer, int bufSize ) { picoParser_t *p; /* sanity check */ if( buffer == NULL || bufSize <= 0 ) return NULL; /* allocate reader */ p = _pico_alloc( sizeof(picoParser_t) ); if (p == NULL) return NULL; memset( p,0,sizeof(picoParser_t) ); /* allocate token space */ p->tokenSize = 0; p->tokenMax = 1024; p->token = _pico_alloc( p->tokenMax ); if( p->token == NULL ) { _pico_free( p ); return NULL; } /* setup */ p->buffer = (char*)buffer; p->cursor = (char*)buffer; p->bufSize = bufSize; p->max = p->buffer + bufSize; p->curLine = 1; /* sea: new */ /* return ptr to parser */ return p; } /* _pico_free_parser: * frees an existing pico parser object. */ void _pico_free_parser( picoParser_t *p ) { /* sanity check */ if (p == NULL) return; /* free the parser */ if (p->token != NULL) { _pico_free( p->token ); } _pico_free( p ); } /* _pico_parse_ex: * reads the next token from given pico parser object. if param * 'allowLFs' is 1 it will read beyond linefeeds and return 0 when * the EOF is reached. if 'allowLFs' is 0 it will return 0 when * the EOL is reached. if 'handleQuoted' is 1 the parser function * will handle "quoted" strings and return the data between the * quotes as token. returns 0 on end/error or 1 on success. -sea */ int _pico_parse_ex( picoParser_t *p, int allowLFs, int handleQuoted ) { int hasLFs = 0; char *old; /* sanity checks */ if( p == NULL || p->buffer == NULL || p->cursor < p->buffer || p->cursor >= p->max ) { return 0; } /* clear parser token */ p->tokenSize = 0; p->token[ 0 ] = '\0'; old = p->cursor; /* skip whitespaces */ while( p->cursor < p->max && *p->cursor <= 32 ) { if (*p->cursor == '\n') { p->curLine++; hasLFs++; } p->cursor++; } /* return if we're not allowed to go beyond lfs */ if ((hasLFs > 0) && !allowLFs) { p->cursor = old; return 0; } /* get next quoted string */ if (*p->cursor == '\"' && handleQuoted) { p->cursor++; while (p->cursor < p->max && *p->cursor) { if (*p->cursor == '\\') { if (*(p->cursor+1) == '"') { p->cursor++; } p->token[ p->tokenSize++ ] = *p->cursor++; continue; } else if (*p->cursor == '\"') { p->cursor++; break; } else if (*p->cursor == '\n') { p->curLine++; } p->token[ p->tokenSize++ ] = *p->cursor++; } /* terminate token */ p->token[ p->tokenSize ] = '\0'; return 1; } /* otherwise get next word */ while( p->cursor < p->max && *p->cursor > 32 ) { if (*p->cursor == '\n') { p->curLine++; } p->token[ p->tokenSize++ ] = *p->cursor++; } /* terminate token */ p->token[ p->tokenSize ] = '\0'; return 1; } /* _pico_parse_first: * reads the first token from the next line and returns * a pointer to it. returns NULL on EOL or EOF. -sea */ char *_pico_parse_first( picoParser_t *p ) { /* sanity check */ if (p == NULL) return NULL; /* try to read next token (with lfs & quots) */ if (!_pico_parse_ex( p,1,1 )) return NULL; /* return ptr to the token string */ return p->token; } /* _pico_parse: * reads the next token from the parser and returns a pointer * to it. quoted strings are handled as usual. returns NULL * on EOL or EOF. -sea */ char *_pico_parse( picoParser_t *p, int allowLFs ) { /* sanity check */ if (p == NULL) return NULL; /* try to read next token (with quots) */ if (!_pico_parse_ex( p,allowLFs,1 )) return NULL; /* return ptr to the token string */ return p->token; } /* _pico_parse_skip_rest: * skips the rest of the current line in parser. */ void _pico_parse_skip_rest( picoParser_t *p ) { while( _pico_parse_ex( p,0,0 ) ) ; } /* _pico_parse_skip_braced: * parses/skips over a braced section. returns 1 on success * or 0 on error (when there was no closing bracket and the * end of buffer was reached or when the opening bracket was * missing). */ int _pico_parse_skip_braced( picoParser_t *p ) { int firstToken = 1; int level; /* sanity check */ if (p == NULL) return 0; /* set the initial level for parsing */ level = 0; /* skip braced section */ while( 1 ) { /* read next token (lfs allowed) */ if (!_pico_parse_ex( p,1,1 )) { /* end of parser buffer reached */ return 0; } /* first token must be an opening bracket */ if (firstToken && p->token[0] != '{') { /* opening bracket missing */ return 0; } /* we only check this once */ firstToken = 0; /* update level */ if (p->token[1] == '\0') { if (p->token[0] == '{') level++; if (p->token[0] == '}') level--; } /* break if we're back at our starting level */ if (level == 0) break; } /* successfully skipped braced section */ return 1; } int _pico_parse_check( picoParser_t *p, int allowLFs, char *str ) { if (!_pico_parse_ex( p,allowLFs,1 )) return 0; if (!strcmp(p->token,str)) return 1; return 0; } int _pico_parse_checki( picoParser_t *p, int allowLFs, char *str ) { if (!_pico_parse_ex( p,allowLFs,1 )) return 0; if (!_pico_stricmp(p->token,str)) return 1; return 0; } int _pico_parse_int( picoParser_t *p, int *out ) { char *token; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* get token and turn it into an integer */ *out = 0; token = _pico_parse( p,0 ); if (token == NULL) return 0; *out = atoi( token ); /* success */ return 1; } int _pico_parse_int_def( picoParser_t *p, int *out, int def ) { char *token; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* get token and turn it into an integer */ *out = def; token = _pico_parse( p,0 ); if (token == NULL) return 0; *out = atoi( token ); /* success */ return 1; } int _pico_parse_float( picoParser_t *p, float *out ) { char *token; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* get token and turn it into a float */ *out = 0.0f; token = _pico_parse( p,0 ); if (token == NULL) return 0; *out = (float) atof( token ); /* success */ return 1; } int _pico_parse_float_def( picoParser_t *p, float *out, float def ) { char *token; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* get token and turn it into a float */ *out = def; token = _pico_parse( p,0 ); if (token == NULL) return 0; *out = (float) atof( token ); /* success */ return 1; } int _pico_parse_double( picoParser_t *p, double *out ) { char *token; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* get token and turn it into a double */ *out = 0; token = _pico_parse( p,0 ); if (token == NULL) return 0; *out = (double) strtod( token, NULL ); /* success */ return 1; } int _pico_parse_double_def( picoParser_t *p, double *out, double def ) { char *token; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* get token and turn it into a double */ *out = def; token = _pico_parse( p,0 ); if (token == NULL) return 0; *out = (double) strtod( token, NULL ); /* success */ return 1; } int _pico_parse_vec( picoParser_t *p, picoVec3_t out ) { char *token; int i; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* zero out outination vector */ _pico_zero_vec( out ); /* parse three vector components */ for (i=0; i<3; i++) { token = _pico_parse( p,0 ); if (token == NULL) { _pico_zero_vec( out ); return 0; } out[ i ] = (float) atof( token ); } /* success */ return 1; } int _pico_parse_vec_def( picoParser_t *p, picoVec3_t out, picoVec3_t def ) { char *token; int i; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* assign default vector value */ _pico_copy_vec( def,out ); /* parse three vector components */ for (i=0; i<3; i++) { token = _pico_parse( p,0 ); if (token == NULL) { _pico_copy_vec( def,out ); return 0; } out[ i ] = (float) atof( token ); } /* success */ return 1; } int _pico_parse_vec2( picoParser_t *p, picoVec2_t out ) { char *token; int i; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* zero out outination vector */ _pico_zero_vec2( out ); /* parse two vector components */ for (i=0; i<2; i++) { token = _pico_parse( p,0 ); if (token == NULL) { _pico_zero_vec2( out ); return 0; } out[ i ] = (float) atof( token ); } /* success */ return 1; } int _pico_parse_vec2_def( picoParser_t *p, picoVec2_t out, picoVec2_t def ) { char *token; int i; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* assign default vector value */ _pico_copy_vec2( def,out ); /* parse two vector components */ for (i=0; i<2; i++) { token = _pico_parse( p,0 ); if (token == NULL) { _pico_copy_vec2( def,out ); return 0; } out[ i ] = (float) atof( token ); } /* success */ return 1; } int _pico_parse_vec4( picoParser_t *p, picoVec4_t out ) { char *token; int i; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* zero out outination vector */ _pico_zero_vec4( out ); /* parse four vector components */ for (i=0; i<4; i++) { token = _pico_parse( p,0 ); if (token == NULL) { _pico_zero_vec4( out ); return 0; } out[ i ] = (float) atof( token ); } /* success */ return 1; } int _pico_parse_vec4_def( picoParser_t *p, picoVec4_t out, picoVec4_t def ) { char *token; int i; /* sanity checks */ if (p == NULL || out == NULL) return 0; /* assign default vector value */ _pico_copy_vec4( def,out ); /* parse four vector components */ for (i=0; i<4; i++) { token = _pico_parse( p,0 ); if (token == NULL) { _pico_copy_vec4( def,out ); return 0; } out[ i ] = (float) atof( token ); } /* success */ return 1; } /* _pico_new_memstream: * allocates a new memorystream object. */ picoMemStream_t *_pico_new_memstream( picoByte_t *buffer, int bufSize ) { picoMemStream_t *s; /* sanity check */ if( buffer == NULL || bufSize <= 0 ) return NULL; /* allocate stream */ s = _pico_alloc( sizeof(picoMemStream_t) ); if (s == NULL) return NULL; memset( s,0,sizeof(picoMemStream_t) ); /* setup */ s->buffer = buffer; s->curPos = buffer; s->bufSize = bufSize; s->flag = 0; /* return ptr to stream */ return s; } /* _pico_free_memstream: * frees an existing pico memorystream object. */ void _pico_free_memstream( picoMemStream_t *s ) { /* sanity check */ if (s == NULL) return; /* free the stream */ _pico_free( s ); } /* _pico_memstream_read: * reads data from a pico memorystream into a buffer. */ int _pico_memstream_read( picoMemStream_t *s, void *buffer, size_t len ) { int ret = 1; /* sanity checks */ if (s == NULL || buffer == NULL) return 0; if (s->curPos + len > s->buffer + s->bufSize) { s->flag |= PICO_IOEOF; len = s->buffer + s->bufSize - s->curPos; ret = 0; } /* read the data */ memcpy( buffer, s->curPos, len ); s->curPos += len; return ret; } /* _pico_memstream_read: * reads a character from a pico memorystream */ int _pico_memstream_getc( picoMemStream_t *s ) { int c = 0; /* sanity check */ if (s == NULL) return -1; /* read the character */ if (_pico_memstream_read( s, &c, 1) == 0) return -1; return c; } /* _pico_memstream_seek: * sets the current read position to a different location */ int _pico_memstream_seek( picoMemStream_t *s, long offset, int origin ) { intptr_t overflow; /* sanity check */ if (s == NULL) return -1; if (origin == PICO_SEEK_SET) { s->curPos = s->buffer + offset; overflow = s->curPos - ( s->buffer + s->bufSize ); if (overflow > 0) { s->curPos = s->buffer + s->bufSize; return (int)(offset - overflow); } return 0; } else if (origin == PICO_SEEK_CUR) { s->curPos += offset; overflow = s->curPos - ( s->buffer + s->bufSize ); if (overflow > 0) { s->curPos = s->buffer + s->bufSize; return (int)(offset - overflow); } return 0; } else if (origin == PICO_SEEK_END) { s->curPos = ( s->buffer + s->bufSize ) - offset; overflow = s->buffer - s->curPos; if (overflow > 0) { s->curPos = s->buffer; return (int)(offset - overflow); } return 0; } return -1; } /* _pico_memstream_tell: * returns the current read position in the pico memorystream */ long _pico_memstream_tell( picoMemStream_t *s ) { /* sanity check */ if (s == NULL) return -1; return (long)(s->curPos - s->buffer); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/picointernal.h�����������������������������������������������������0000664�0000000�0000000�00000016403�13217505464�0021461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #ifndef PICOINTERNAL_H #define PICOINTERNAL_H #ifdef __cplusplus extern "C" { #endif /* dependencies */ #include #include #include #include #include #include #include "picomodel.h" /* os dependent replacements */ #if WIN32 || _WIN32 #define _pico_stricmp stricmp #define _pico_strnicmp strnicmp #else #define _pico_stricmp strcasecmp #define _pico_strnicmp strncasecmp #endif /* constants */ #define PICO_PI 3.14159265358979323846 #define PICO_SEEK_SET 0 #define PICO_SEEK_CUR 1 #define PICO_SEEK_END 2 #define PICO_IOEOF 1 #define PICO_IOERR 2 /* types */ typedef struct picoParser_s { char *buffer; int bufSize; char *token; int tokenSize; int tokenMax; char *cursor; char *max; int curLine; } picoParser_t; typedef struct picoMemStream_s { picoByte_t *buffer; int bufSize; picoByte_t *curPos; int flag; } picoMemStream_t; /* variables */ extern const picoModule_t *picoModules[]; extern void *(*_pico_ptr_malloc)( size_t ); extern void (*_pico_ptr_free)( void* ); extern void (*_pico_ptr_load_file)( char*, unsigned char**, int* ); extern void (*_pico_ptr_free_file)( void* ); extern void (*_pico_ptr_print)( int, const char* ); /* prototypes */ /* memory */ void *_pico_alloc( size_t size ); void *_pico_calloc( size_t num, size_t size ); void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize ); char *_pico_clone_alloc( const char *str ); void _pico_free( void *ptr ); /* files */ void _pico_load_file( char *name, unsigned char **buffer, int *bufSize ); void _pico_free_file( void *buffer ); /* strings */ void _pico_first_token( char *str ); char *_pico_strltrim( char *str ); char *_pico_strrtrim( char *str ); int _pico_strchcount( char *str, int ch ); void _pico_printf( int level, const char *format, ... ); char *_pico_stristr( char *str, const char *substr ); void _pico_unixify( char *path ); int _pico_nofname( const char *path, char *dest, int destSize ); const char *_pico_nopath( const char *path ); char *_pico_setfext( char *path, const char *ext ); int _pico_getline( char *buf, int bufsize, char *dest, int destsize ); char *_pico_strlwr( char *str ); /* vectors */ void _pico_zero_bounds( picoVec3_t mins, picoVec3_t maxs ); void _pico_expand_bounds( picoVec3_t p, picoVec3_t mins, picoVec3_t maxs ); void _pico_zero_vec( picoVec3_t vec ); void _pico_zero_vec2( picoVec2_t vec ); void _pico_zero_vec4( picoVec4_t vec ); void _pico_set_vec( picoVec3_t v, double a, double b, double c ); void _pico_set_vec4( picoVec4_t v, double a, double b, double c, double d ); void _pico_set_color( picoColor_t c, int r, int g, int b, int a ); void _pico_copy_color( picoColor_t src, picoColor_t dest ); void _pico_copy_vec( picoVec3_t src, picoVec3_t dest ); void _pico_copy_vec2( picoVec2_t src, picoVec2_t dest ); picoVec_t _pico_normalize_vec( picoVec3_t vec ); void _pico_add_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ); void _pico_subtract_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ); picoVec_t _pico_dot_vec( picoVec3_t a, picoVec3_t b ); void _pico_cross_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ); picoVec_t _pico_calc_plane( picoVec4_t plane, picoVec3_t a, picoVec3_t b, picoVec3_t c ); void _pico_scale_vec( picoVec3_t v, float scale, picoVec3_t dest ); void _pico_scale_vec4( picoVec4_t v, float scale, picoVec4_t dest ); /* endian */ int _pico_big_long( int src ); short _pico_big_short( short src ); float _pico_big_float( float src ); int _pico_little_long( int src ); short _pico_little_short( short src ); float _pico_little_float( float src ); /* pico ascii parser */ picoParser_t *_pico_new_parser( picoByte_t *buffer, int bufSize ); void _pico_free_parser( picoParser_t *p ); int _pico_parse_ex( picoParser_t *p, int allowLFs, int handleQuoted ); char *_pico_parse_first( picoParser_t *p ); char *_pico_parse( picoParser_t *p, int allowLFs ); void _pico_parse_skip_rest( picoParser_t *p ); int _pico_parse_skip_braced( picoParser_t *p ); int _pico_parse_check( picoParser_t *p, int allowLFs, char *str ); int _pico_parse_checki( picoParser_t *p, int allowLFs, char *str ); int _pico_parse_int( picoParser_t *p, int *out ); int _pico_parse_int_def( picoParser_t *p, int *out, int def ); int _pico_parse_float( picoParser_t *p, float *out ); int _pico_parse_float_def( picoParser_t *p, float *out, float def ); int _pico_parse_double( picoParser_t *p, double *out ); int _pico_parse_double_def( picoParser_t *p, double *out, double def ); int _pico_parse_vec( picoParser_t *p, picoVec3_t out); int _pico_parse_vec_def( picoParser_t *p, picoVec3_t out, picoVec3_t def); int _pico_parse_vec2( picoParser_t *p, picoVec2_t out ); int _pico_parse_vec2_def( picoParser_t *p, picoVec2_t out, picoVec2_t def ); int _pico_parse_vec4( picoParser_t *p, picoVec4_t out); int _pico_parse_vec4_def( picoParser_t *p, picoVec4_t out, picoVec4_t def); /* pico memory stream */ picoMemStream_t *_pico_new_memstream( picoByte_t *buffer, int bufSize ); void _pico_free_memstream( picoMemStream_t *s ); int _pico_memstream_read( picoMemStream_t *s, void *buffer, size_t len ); int _pico_memstream_getc( picoMemStream_t *s ); int _pico_memstream_seek( picoMemStream_t *s, long offset, int origin ); long _pico_memstream_tell( picoMemStream_t *s ); #define _pico_memstream_eof( _pico_memstream ) ((_pico_memstream)->flag & PICO_IOEOF) #define _pico_memstream_error( _pico_memstream ) ((_pico_memstream)->flag & PICO_IOERR) /* end marker */ #ifdef __cplusplus } #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/picomodel.c��������������������������������������������������������0000664�0000000�0000000�00000152462�13217505464�0020746�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define PICOMODEL_C /* dependencies */ #include "picointernal.h" /* PicoInit() initializes the picomodel library */ int PicoInit( void ) { /* successfully initialized -sea */ return 1; } /* PicoShutdown() shuts the pico model library down */ void PicoShutdown( void ) { /* do something interesting here in the future */ return; } /* PicoError() returns last picomodel error code (see PME_* defines) */ int PicoError( void ) { /* todo: do something here */ return 0; } /* PicoSetMallocFunc() sets the ptr to the malloc function */ void PicoSetMallocFunc( void *(*func)( size_t ) ) { if( func != NULL ) _pico_ptr_malloc = func; } /* PicoSetFreeFunc() sets the ptr to the free function */ void PicoSetFreeFunc( void (*func)( void* ) ) { if( func != NULL ) _pico_ptr_free = func; } /* PicoSetLoadFileFunc() sets the ptr to the file load function */ void PicoSetLoadFileFunc( void (*func)( char*, unsigned char**, int* ) ) { if( func != NULL ) _pico_ptr_load_file = func; } /* PicoSetFreeFileFunc() sets the ptr to the free function */ void PicoSetFreeFileFunc( void (*func)( void* ) ) { if( func != NULL ) _pico_ptr_free_file = func; } /* PicoSetPrintFunc() sets the ptr to the print function */ void PicoSetPrintFunc( void (*func)( int, const char* ) ) { if( func != NULL ) _pico_ptr_print = func; } picoModel_t *PicoModuleLoadModel( const picoModule_t* pm, char* fileName, picoByte_t* buffer, int bufSize, int frameNum ) { char *modelFileName, *remapFileName; /* see whether this module can load the model file or not */ if( pm->canload( fileName, buffer, bufSize ) == PICO_PMV_OK ) { /* use loader provided by module to read the model data */ picoModel_t* model = pm->load( fileName, frameNum, buffer, bufSize ); if( model == NULL ) { _pico_free_file( buffer ); return NULL; } /* assign pointer to file format module */ model->module = pm; /* get model file name */ modelFileName = PicoGetModelFileName( model ); /* apply model remappings from .remap */ if( strlen( modelFileName ) ) { /* alloc copy of model file name */ remapFileName = _pico_alloc( strlen( modelFileName ) + 20 ); if( remapFileName != NULL ) { /* copy model file name and change extension */ strcpy( remapFileName, modelFileName ); _pico_setfext( remapFileName, "remap" ); /* try to remap model; we don't handle the result */ PicoRemapModel( model, remapFileName ); /* free the remap file name string */ _pico_free( remapFileName ); } } return model; } return NULL; } /* PicoLoadModel() the meat and potatoes function */ picoModel_t *PicoLoadModel( char *fileName, int frameNum ) { const picoModule_t **modules, *pm; picoModel_t *model; picoByte_t *buffer; int bufSize; /* init */ model = NULL; /* make sure we've got a file name */ if( fileName == NULL ) { _pico_printf( PICO_ERROR, "PicoLoadModel: No filename given (fileName == NULL)" ); return NULL; } /* load file data (buffer is allocated by host app) */ _pico_load_file( fileName, &buffer, &bufSize ); if( bufSize < 0 ) { _pico_printf( PICO_ERROR, "PicoLoadModel: Failed loading model %s", fileName ); return NULL; } /* get ptr to list of supported modules */ modules = PicoModuleList( NULL ); /* run it through the various loader functions and try */ /* to find a loader that fits the given file data */ for( ; *modules != NULL; modules++ ) { /* get module */ pm = *modules; /* sanity check */ if( pm == NULL) break; /* module must be able to load */ if( pm->canload == NULL || pm->load == NULL ) continue; model = PicoModuleLoadModel(pm, fileName, buffer, bufSize, frameNum); if(model != NULL) { /* model was loaded, so break out of loop */ break; } } /* free memory used by file buffer */ if( buffer) _pico_free_file( buffer ); /* return */ return model; } picoModel_t *PicoModuleLoadModelStream( const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum ) { picoModel_t *model; picoByte_t *buffer; int bufSize; /* init */ model = NULL; if( inputStream == NULL ) { _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStream == NULL)" ); return NULL; } if( inputStreamRead == NULL ) { _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStreamRead == NULL)" ); return NULL; } buffer = _pico_alloc(streamLength + 1); bufSize = (int)inputStreamRead(inputStream, buffer, streamLength); buffer[bufSize] = '\0'; { // dummy filename char fileName[128]; fileName[0] = '.'; strncpy(fileName + 1, module->defaultExts[0], 126); fileName[127] = '\0'; model = PicoModuleLoadModel(module, fileName, buffer, bufSize, frameNum); } if(model != 0) { _pico_free(buffer); } /* return */ return model; } /* ---------------------------------------------------------------------------- models ---------------------------------------------------------------------------- */ /* PicoNewModel() creates a new pico model */ picoModel_t *PicoNewModel( void ) { picoModel_t *model; /* allocate */ model = _pico_alloc( sizeof(picoModel_t) ); if( model == NULL ) return NULL; /* clear */ memset( model,0,sizeof(picoModel_t) ); /* model set up */ _pico_zero_bounds( model->mins,model->maxs ); /* set initial frame count to 1 -sea */ model->numFrames = 1; /* return ptr to new model */ return model; } /* PicoFreeModel() frees a model and all associated data */ void PicoFreeModel( picoModel_t *model ) { int i; /* sanity check */ if( model == NULL ) return; /* free bits */ if( model->name ) _pico_free( model->name ); if( model->fileName ) _pico_free( model->fileName ); /* free shaders */ for( i = 0; i < model->numShaders; i++ ) PicoFreeShader( model->shader[ i ] ); free( model->shader ); /* free surfaces */ for( i = 0; i < model->numSurfaces; i++ ) PicoFreeSurface( model->surface[ i ] ); free( model->surface ); /* free the model */ _pico_free( model ); } /* PicoAdjustModel() adjusts a models's memory allocations to handle the requested sizes. will always grow, never shrink */ int PicoAdjustModel( picoModel_t *model, int numShaders, int numSurfaces ) { /* dummy check */ if( model == NULL ) return 0; /* bare minimums */ /* sea: null surface/shader fix (1s=>0s) */ if( numShaders < 0 ) numShaders = 0; if( numSurfaces < 0 ) numSurfaces = 0; /* additional shaders? */ while( numShaders > model->maxShaders ) { model->maxShaders += PICO_GROW_SHADERS; if( !_pico_realloc( (void *) &model->shader, model->numShaders * sizeof( *model->shader ), model->maxShaders * sizeof( *model->shader ) ) ) return 0; } /* set shader count to higher */ if( numShaders > model->numShaders ) model->numShaders = numShaders; /* additional surfaces? */ while( numSurfaces > model->maxSurfaces ) { model->maxSurfaces += PICO_GROW_SURFACES; if( !_pico_realloc( (void *) &model->surface, model->numSurfaces * sizeof( *model->surface ), model->maxSurfaces * sizeof( *model->surface ) ) ) return 0; } /* set shader count to higher */ if( numSurfaces > model->numSurfaces ) model->numSurfaces = numSurfaces; /* return ok */ return 1; } /* ---------------------------------------------------------------------------- shaders ---------------------------------------------------------------------------- */ /* PicoNewShader() creates a new pico shader and returns its index. -sea */ picoShader_t *PicoNewShader( picoModel_t *model ) { picoShader_t *shader; /* allocate and clear */ shader = _pico_alloc( sizeof(picoShader_t) ); if( shader == NULL ) return NULL; memset( shader, 0, sizeof(picoShader_t) ); /* attach it to the model */ if( model != NULL ) { /* adjust model */ if( !PicoAdjustModel( model, model->numShaders + 1, 0 ) ) { _pico_free( shader ); return NULL; } /* attach */ model->shader[ model->numShaders - 1 ] = shader; shader->model = model; } /* setup default shader colors */ _pico_set_color( shader->ambientColor,0,0,0,0 ); _pico_set_color( shader->diffuseColor,255,255,255,1 ); _pico_set_color( shader->specularColor,0,0,0,0 ); /* no need to do this, but i do it anyway */ shader->transparency = 0; shader->shininess = 0; /* return the newly created shader */ return shader; } /* PicoFreeShader() frees a shader and all associated data -sea */ void PicoFreeShader( picoShader_t *shader ) { /* dummy check */ if( shader == NULL ) return; /* free bits */ if( shader->name ) _pico_free( shader->name ); if( shader->mapName ) _pico_free( shader->mapName ); /* free the shader */ _pico_free( shader ); } /* PicoFindShader() finds a named shader in a model */ picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive ) { int i; /* sanity checks */ if( model == NULL || name == NULL ) /* sea: null name fix */ return NULL; /* walk list */ for( i = 0; i < model->numShaders; i++ ) { /* skip null shaders or shaders with null names */ if( model->shader[ i ] == NULL || model->shader[ i ]->name == NULL ) continue; /* compare the shader name with name we're looking for */ if( caseSensitive ) { if( !strcmp( name, model->shader[ i ]->name ) ) return model->shader[ i ]; } else if( !_pico_stricmp( name, model->shader[ i ]->name ) ) return model->shader[ i ]; } /* named shader not found */ return NULL; } /* ---------------------------------------------------------------------------- surfaces ---------------------------------------------------------------------------- */ /* PicoNewSurface() creates a new pico surface */ picoSurface_t *PicoNewSurface( picoModel_t *model ) { picoSurface_t *surface; char surfaceName[64]; /* allocate and clear */ surface = _pico_alloc( sizeof( *surface ) ); if( surface == NULL ) return NULL; memset( surface, 0, sizeof( *surface ) ); /* attach it to the model */ if( model != NULL ) { /* adjust model */ if( !PicoAdjustModel( model, 0, model->numSurfaces + 1 ) ) { _pico_free( surface ); return NULL; } /* attach */ model->surface[ model->numSurfaces - 1 ] = surface; surface->model = model; /* set default name */ sprintf( surfaceName, "Unnamed_%d", model->numSurfaces ); PicoSetSurfaceName( surface, surfaceName ); } /* return */ return surface; } /* PicoFreeSurface() frees a surface and all associated data */ void PicoFreeSurface( picoSurface_t *surface ) { int i; /* dummy check */ if( surface == NULL ) return; /* free bits */ _pico_free( surface->xyz ); _pico_free( surface->normal ); _pico_free( surface->smoothingGroup ); _pico_free( surface->index ); _pico_free( surface->faceNormal ); if( surface->name ) _pico_free( surface->name ); /* free arrays */ for( i = 0; i < surface->numSTArrays; i++ ) _pico_free( surface->st[ i ] ); free( surface->st ); for( i = 0; i < surface->numColorArrays; i++ ) _pico_free( surface->color[ i ] ); free( surface->color ); /* free the surface */ _pico_free( surface ); } /* PicoAdjustSurface() adjusts a surface's memory allocations to handle the requested sizes. will always grow, never shrink */ int PicoAdjustSurface( picoSurface_t *surface, int numVertexes, int numSTArrays, int numColorArrays, int numIndexes, int numFaceNormals ) { int i; /* dummy check */ if( surface == NULL ) return 0; /* bare minimums */ if( numVertexes < 1 ) numVertexes = 1; if( numSTArrays < 1 ) numSTArrays = 1; if( numColorArrays < 1 ) numColorArrays = 1; if( numIndexes < 1 ) numIndexes = 1; /* additional vertexes? */ while( numVertexes > surface->maxVertexes ) /* fix */ { surface->maxVertexes += PICO_GROW_VERTEXES; if( !_pico_realloc( (void *) &surface->xyz, surface->numVertexes * sizeof( *surface->xyz ), surface->maxVertexes * sizeof( *surface->xyz ) ) ) return 0; if( !_pico_realloc( (void *) &surface->normal, surface->numVertexes * sizeof( *surface->normal ), surface->maxVertexes * sizeof( *surface->normal ) ) ) return 0; if( !_pico_realloc( (void *) &surface->smoothingGroup, surface->numVertexes * sizeof( *surface->smoothingGroup ), surface->maxVertexes * sizeof( *surface->smoothingGroup ) ) ) return 0; for( i = 0; i < surface->numSTArrays; i++ ) if( !_pico_realloc( (void*) &surface->st[ i ], surface->numVertexes * sizeof( *surface->st[ i ] ), surface->maxVertexes * sizeof( *surface->st[ i ] ) ) ) return 0; for( i = 0; i < surface->numColorArrays; i++ ) if( !_pico_realloc( (void*) &surface->color[ i ], surface->numVertexes * sizeof( *surface->color[ i ] ), surface->maxVertexes * sizeof( *surface->color[ i ] ) ) ) return 0; } /* set vertex count to higher */ if( numVertexes > surface->numVertexes ) surface->numVertexes = numVertexes; /* additional st arrays? */ while( numSTArrays > surface->maxSTArrays ) /* fix */ { surface->maxSTArrays += PICO_GROW_ARRAYS; if( !_pico_realloc( (void*) &surface->st, surface->numSTArrays * sizeof( *surface->st ), surface->maxSTArrays * sizeof( *surface->st ) ) ) return 0; while( surface->numSTArrays < numSTArrays ) { surface->st[ surface->numSTArrays ] = _pico_alloc( surface->maxVertexes * sizeof( *surface->st[ 0 ] ) ); memset( surface->st[ surface->numSTArrays ], 0, surface->maxVertexes * sizeof( *surface->st[ 0 ] ) ); surface->numSTArrays++; } } /* additional color arrays? */ while( numColorArrays > surface->maxColorArrays ) /* fix */ { surface->maxColorArrays += PICO_GROW_ARRAYS; if( !_pico_realloc( (void*) &surface->color, surface->numColorArrays * sizeof( *surface->color ), surface->maxColorArrays * sizeof( *surface->color ) ) ) return 0; while( surface->numColorArrays < numColorArrays ) { surface->color[ surface->numColorArrays ] = _pico_alloc( surface->maxVertexes * sizeof( *surface->color[ 0 ] ) ); memset( surface->color[ surface->numColorArrays ], 0, surface->maxVertexes * sizeof( *surface->color[ 0 ] ) ); surface->numColorArrays++; } } /* additional indexes? */ while( numIndexes > surface->maxIndexes ) /* fix */ { surface->maxIndexes += PICO_GROW_INDEXES; if( !_pico_realloc( (void*) &surface->index, surface->numIndexes * sizeof( *surface->index ), surface->maxIndexes * sizeof( *surface->index ) ) ) return 0; } /* set index count to higher */ if( numIndexes > surface->numIndexes ) surface->numIndexes = numIndexes; /* additional face normals? */ while( numFaceNormals > surface->maxFaceNormals ) /* fix */ { surface->maxFaceNormals += PICO_GROW_FACES; if( !_pico_realloc( (void *) &surface->faceNormal, surface->numFaceNormals * sizeof( *surface->faceNormal ), surface->maxFaceNormals * sizeof( *surface->faceNormal ) ) ) return 0; } /* set face normal count to higher */ if( numFaceNormals > surface->numFaceNormals ) surface->numFaceNormals = numFaceNormals; /* return ok */ return 1; } /* PicoFindSurface: * Finds first matching named surface in a model. */ picoSurface_t *PicoFindSurface( picoModel_t *model, char *name, int caseSensitive ) { int i; /* sanity check */ if( model == NULL || name == NULL ) return NULL; /* walk list */ for( i = 0; i < model->numSurfaces; i++ ) { /* skip null surfaces or surfaces with null names */ if( model->surface[ i ] == NULL || model->surface[ i ]->name == NULL ) continue; /* compare the surface name with name we're looking for */ if (caseSensitive) { if( !strcmp(name,model->surface[ i ]->name) ) return model->surface[ i ]; } else { if( !_pico_stricmp(name,model->surface[ i ]->name) ) return model->surface[ i ]; } } /* named surface not found */ return NULL; } /*---------------------------------------------------------------------------- PicoSet*() Setter Functions ----------------------------------------------------------------------------*/ void PicoSetModelName( picoModel_t *model, char *name ) { if( model == NULL || name == NULL ) return; if( model->name != NULL ) _pico_free( model->name ); model->name = _pico_clone_alloc( name ); } void PicoSetModelFileName( picoModel_t *model, char *fileName ) { if( model == NULL || fileName == NULL ) return; if( model->fileName != NULL ) _pico_free( model->fileName ); model->fileName = _pico_clone_alloc( fileName ); } void PicoSetModelFrameNum( picoModel_t *model, int frameNum ) { if( model == NULL ) return; model->frameNum = frameNum; } void PicoSetModelNumFrames( picoModel_t *model, int numFrames ) { if( model == NULL ) return; model->numFrames = numFrames; } void PicoSetModelData( picoModel_t *model, void *data ) { if( model == NULL ) return; model->data = data; } void PicoSetShaderName( picoShader_t *shader, char *name ) { if( shader == NULL || name == NULL ) return; if( shader->name != NULL ) _pico_free( shader->name ); shader->name = _pico_clone_alloc( name ); } void PicoSetShaderMapName( picoShader_t *shader, char *mapName ) { if( shader == NULL || mapName == NULL ) return; if( shader->mapName != NULL ) _pico_free( shader->mapName ); shader->mapName = _pico_clone_alloc( mapName ); } void PicoSetShaderAmbientColor( picoShader_t *shader, picoColor_t color ) { if( shader == NULL || color == NULL ) return; shader->ambientColor[ 0 ] = color[ 0 ]; shader->ambientColor[ 1 ] = color[ 1 ]; shader->ambientColor[ 2 ] = color[ 2 ]; shader->ambientColor[ 3 ] = color[ 3 ]; } void PicoSetShaderDiffuseColor( picoShader_t *shader, picoColor_t color ) { if( shader == NULL || color == NULL ) return; shader->diffuseColor[ 0 ] = color[ 0 ]; shader->diffuseColor[ 1 ] = color[ 1 ]; shader->diffuseColor[ 2 ] = color[ 2 ]; shader->diffuseColor[ 3 ] = color[ 3 ]; } void PicoSetShaderSpecularColor( picoShader_t *shader, picoColor_t color ) { if( shader == NULL || color == NULL ) return; shader->specularColor[ 0 ] = color[ 0 ]; shader->specularColor[ 1 ] = color[ 1 ]; shader->specularColor[ 2 ] = color[ 2 ]; shader->specularColor[ 3 ] = color[ 3 ]; } void PicoSetShaderTransparency( picoShader_t *shader, float value ) { if( shader == NULL ) return; shader->transparency = value; /* cap to 0..1 range */ if (shader->transparency < 0.0) shader->transparency = 0.0; if (shader->transparency > 1.0) shader->transparency = 1.0; } void PicoSetShaderShininess( picoShader_t *shader, float value ) { if( shader == NULL ) return; shader->shininess = value; /* cap to 0..127 range */ if (shader->shininess < 0.0) shader->shininess = 0.0; if (shader->shininess > 127.0) shader->shininess = 127.0; } void PicoSetSurfaceData( picoSurface_t *surface, void *data ) { if( surface == NULL ) return; surface->data = data; } void PicoSetSurfaceType( picoSurface_t *surface, picoSurfaceType_t type ) { if( surface == NULL ) return; surface->type = type; } void PicoSetSurfaceName( picoSurface_t *surface, char *name ) { if( surface == NULL || name == NULL ) return; if( surface->name != NULL ) _pico_free( surface->name ); surface->name = _pico_clone_alloc( name ); } void PicoSetSurfaceShader( picoSurface_t *surface, picoShader_t *shader ) { if( surface == NULL ) return; surface->shader = shader; } void PicoSetSurfaceXYZ( picoSurface_t *surface, int num, picoVec3_t xyz ) { if( surface == NULL || num < 0 || xyz == NULL ) return; if( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) ) return; _pico_copy_vec( xyz, surface->xyz[ num ] ); if( surface->model != NULL ) _pico_expand_bounds( xyz, surface->model->mins, surface->model->maxs ); } void PicoSetSurfaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ) { if( surface == NULL || num < 0 || normal == NULL ) return; if( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) ) return; _pico_copy_vec( normal, surface->normal[ num ] ); } void PicoSetSurfaceST( picoSurface_t *surface, int array, int num, picoVec2_t st ) { if( surface == NULL || num < 0 || st == NULL ) return; if( !PicoAdjustSurface( surface, num + 1, array + 1, 0, 0, 0 ) ) return; surface->st[ array ][ num ][ 0 ] = st[ 0 ]; surface->st[ array ][ num ][ 1 ] = st[ 1 ]; } void PicoSetSurfaceColor( picoSurface_t *surface, int array, int num, picoColor_t color ) { if( surface == NULL || num < 0 || color == NULL ) return; if( !PicoAdjustSurface( surface, num + 1, 0, array + 1, 0, 0 ) ) return; surface->color[ array ][ num ][ 0 ] = color[ 0 ]; surface->color[ array ][ num ][ 1 ] = color[ 1 ]; surface->color[ array ][ num ][ 2 ] = color[ 2 ]; surface->color[ array ][ num ][ 3 ] = color[ 3 ]; } void PicoSetSurfaceIndex( picoSurface_t *surface, int num, picoIndex_t index ) { if( surface == NULL || num < 0 ) return; if( !PicoAdjustSurface( surface, 0, 0, 0, num + 1, 0 ) ) return; surface->index[ num ] = index; } void PicoSetSurfaceIndexes( picoSurface_t *surface, int num, picoIndex_t *index, int count ) { if( num < 0 || index == NULL || count < 1 ) return; if( !PicoAdjustSurface( surface, 0, 0, 0, num + count, 0 ) ) return; memcpy( &surface->index[ num ], index, count * sizeof( surface->index[ num ] ) ); } void PicoSetFaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ) { if( surface == NULL || num < 0 || normal == NULL ) return; if( !PicoAdjustSurface( surface, 0, 0, 0, 0, num + 1 ) ) return; _pico_copy_vec( normal, surface->faceNormal[ num ] ); } void PicoSetSurfaceSmoothingGroup( picoSurface_t *surface, int num, picoIndex_t smoothingGroup ) { if( num < 0 ) return; if( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) ) return; surface->smoothingGroup[ num ] = smoothingGroup; } void PicoSetSurfaceSpecial( picoSurface_t *surface, int num, int special ) { if( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) return; surface->special[ num ] = special; } /*---------------------------------------------------------------------------- PicoGet*() Getter Functions ----------------------------------------------------------------------------*/ char *PicoGetModelName( picoModel_t *model ) { if( model == NULL ) return NULL; if( model->name == NULL) return (char*) ""; return model->name; } char *PicoGetModelFileName( picoModel_t *model ) { if( model == NULL ) return NULL; if( model->fileName == NULL) return (char*) ""; return model->fileName; } int PicoGetModelFrameNum( picoModel_t *model ) { if( model == NULL ) return 0; return model->frameNum; } int PicoGetModelNumFrames( picoModel_t *model ) { if( model == NULL ) return 0; return model->numFrames; } void *PicoGetModelData( picoModel_t *model ) { if( model == NULL ) return NULL; return model->data; } int PicoGetModelNumShaders( picoModel_t *model ) { if( model == NULL ) return 0; return model->numShaders; } picoShader_t *PicoGetModelShader( picoModel_t *model, int num ) { /* a few sanity checks */ if( model == NULL ) return NULL; if( model->shader == NULL) return NULL; if( num < 0 || num >= model->numShaders ) return NULL; /* return the shader */ return model->shader[ num ]; } int PicoGetModelNumSurfaces( picoModel_t *model ) { if( model == NULL ) return 0; return model->numSurfaces; } picoSurface_t *PicoGetModelSurface( picoModel_t *model, int num ) { /* a few sanity checks */ if( model == NULL ) return NULL; if( model->surface == NULL) return NULL; if( num < 0 || num >= model->numSurfaces ) return NULL; /* return the surface */ return model->surface[ num ]; } int PicoGetModelTotalVertexes( picoModel_t *model ) { int i, count; if( model == NULL ) return 0; if( model->surface == NULL ) return 0; count = 0; for( i = 0; i < model->numSurfaces; i++ ) count += PicoGetSurfaceNumVertexes( model->surface[ i ] ); return count; } int PicoGetModelTotalIndexes( picoModel_t *model ) { int i, count; if( model == NULL ) return 0; if( model->surface == NULL ) return 0; count = 0; for( i = 0; i < model->numSurfaces; i++ ) count += PicoGetSurfaceNumIndexes( model->surface[ i ] ); return count; } char *PicoGetShaderName( picoShader_t *shader ) { if( shader == NULL ) return NULL; if( shader->name == NULL) return (char*) ""; return shader->name; } char *PicoGetShaderMapName( picoShader_t *shader ) { if( shader == NULL ) return NULL; if( shader->mapName == NULL) return (char*) ""; return shader->mapName; } picoByte_t *PicoGetShaderAmbientColor( picoShader_t *shader ) { if( shader == NULL ) return NULL; return shader->ambientColor; } picoByte_t *PicoGetShaderDiffuseColor( picoShader_t *shader ) { if( shader == NULL ) return NULL; return shader->diffuseColor; } picoByte_t *PicoGetShaderSpecularColor( picoShader_t *shader ) { if( shader == NULL ) return NULL; return shader->specularColor; } float PicoGetShaderTransparency( picoShader_t *shader ) { if( shader == NULL ) return 0.0f; return shader->transparency; } float PicoGetShaderShininess( picoShader_t *shader ) { if( shader == NULL ) return 0.0f; return shader->shininess; } void *PicoGetSurfaceData( picoSurface_t *surface ) { if( surface == NULL ) return NULL; return surface->data; } picoSurfaceType_t PicoGetSurfaceType( picoSurface_t *surface ) { if( surface == NULL ) return PICO_BAD; return surface->type; } char *PicoGetSurfaceName( picoSurface_t *surface ) { if( surface == NULL ) return NULL; if( surface->name == NULL ) return (char*) ""; return surface->name; } picoShader_t *PicoGetSurfaceShader( picoSurface_t *surface ) { if( surface == NULL ) return NULL; return surface->shader; } int PicoGetSurfaceNumVertexes( picoSurface_t *surface ) { if( surface == NULL ) return 0; return surface->numVertexes; } picoVec_t *PicoGetSurfaceXYZ( picoSurface_t *surface, int num ) { if( surface == NULL || num < 0 || num > surface->numVertexes ) return NULL; return surface->xyz[ num ]; } picoVec_t *PicoGetSurfaceNormal( picoSurface_t *surface, int num ) { if( surface == NULL || num < 0 || num > surface->numVertexes ) return NULL; return surface->normal[ num ]; } picoVec_t *PicoGetSurfaceST( picoSurface_t *surface, int array, int num ) { if( surface == NULL || array < 0 || array > surface->numSTArrays || num < 0 || num > surface->numVertexes ) return NULL; return surface->st[ array ][ num ]; } picoByte_t *PicoGetSurfaceColor( picoSurface_t *surface, int array, int num ) { if( surface == NULL || array < 0 || array > surface->numColorArrays || num < 0 || num > surface->numVertexes ) return NULL; return surface->color[ array ][ num ]; } int PicoGetSurfaceNumIndexes( picoSurface_t *surface ) { if( surface == NULL ) return 0; return surface->numIndexes; } picoIndex_t PicoGetSurfaceIndex( picoSurface_t *surface, int num ) { if( surface == NULL || num < 0 || num > surface->numIndexes ) return 0; return surface->index[ num ]; } picoIndex_t *PicoGetSurfaceIndexes( picoSurface_t *surface, int num ) { if( surface == NULL || num < 0 || num > surface->numIndexes ) return NULL; return &surface->index[ num ]; } picoVec_t *PicoGetFaceNormal( picoSurface_t *surface, int num ) { if( surface == NULL || num < 0 || num > surface->numFaceNormals ) return NULL; return surface->faceNormal[ num ]; } picoIndex_t PicoGetSurfaceSmoothingGroup( picoSurface_t *surface, int num ) { if( surface == NULL || num < 0 || num > surface->numVertexes ) return -1; return surface->smoothingGroup[ num ]; } int PicoGetSurfaceSpecial( picoSurface_t *surface, int num ) { if( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) return 0; return surface->special[ num ]; } /* ---------------------------------------------------------------------------- hashtable related functions ---------------------------------------------------------------------------- */ /* hashtable code for faster vertex lookups */ //#define HASHTABLE_SIZE 32768 // 2048 /* power of 2, use & */ #define HASHTABLE_SIZE 7919 // 32749 // 2039 /* prime, use % */ int PicoGetHashTableSize( void ) { return HASHTABLE_SIZE; } #define HASH_USE_EPSILON #ifdef HASH_USE_EPSILON #define HASH_XYZ_EPSILON 0.01f #define HASH_XYZ_EPSILONSPACE_MULTIPLIER 1.f / HASH_XYZ_EPSILON #define HASH_ST_EPSILON 0.0001f #define HASH_NORMAL_EPSILON 0.02f #endif // djb2 Hash function adapted from http://www.cse.yorku.ca/~oz/hash.html unsigned int calculateHash(unsigned char* str, size_t len) { unsigned int hash = 5381; int c; size_t i; for (i = 0; i < len; ++i) { c = str[i]; hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } return hash; } unsigned int PicoVertexCoordGenerateHash( picoVec3_t xyz ) { unsigned int hash = 0; // Allocate bytes for the 3 doubles unsigned char buffer[sizeof(picoVec3_t)]; #ifndef HASH_USE_EPSILON hash += ~(*((unsigned int*) &xyz[ 0 ]) << 15); hash ^= (*((unsigned int*) &xyz[ 0 ]) >> 10); hash += (*((unsigned int*) &xyz[ 1 ]) << 3); hash ^= (*((unsigned int*) &xyz[ 1 ]) >> 6); hash += ~(*((unsigned int*) &xyz[ 2 ]) << 11); hash ^= (*((unsigned int*) &xyz[ 2 ]) >> 16); #else picoVec3_t xyz_epsilonspace; _pico_scale_vec( xyz, HASH_XYZ_EPSILONSPACE_MULTIPLIER, xyz_epsilonspace ); xyz_epsilonspace[ 0 ] = (double)floor(xyz_epsilonspace[ 0 ]); xyz_epsilonspace[ 1 ] = (double)floor(xyz_epsilonspace[ 1 ]); xyz_epsilonspace[ 2 ] = (double)floor(xyz_epsilonspace[ 2 ]); /* greebo: Copy the memory occupied by the floor'ed doubles to the unsigned char array and hash them */ memcpy(buffer, &xyz_epsilonspace[0], sizeof(picoVec3_t)); hash = calculateHash(buffer, sizeof(picoVec3_t)); /* greebo: This was the previous, compiler-warning-producing code hash += ~(*((unsigned int*) &xyz_epsilonspace[ 0 ]) << 15); hash ^= (*((unsigned int*) &xyz_epsilonspace[ 0 ]) >> 10); hash += (*((unsigned int*) &xyz_epsilonspace[ 1 ]) << 3); hash ^= (*((unsigned int*) &xyz_epsilonspace[ 1 ]) >> 6); hash += ~(*((unsigned int*) &xyz_epsilonspace[ 2 ]) << 11); hash ^= (*((unsigned int*) &xyz_epsilonspace[ 2 ]) >> 16); */ #endif //hash = hash & (HASHTABLE_SIZE-1); hash = hash % (HASHTABLE_SIZE); return hash; } picoVertexCombinationHash_t **PicoNewVertexCombinationHashTable( void ) { picoVertexCombinationHash_t **hashTable = _pico_alloc( HASHTABLE_SIZE * sizeof(picoVertexCombinationHash_t*) ); memset( hashTable, 0, HASHTABLE_SIZE * sizeof(picoVertexCombinationHash_t*) ); return hashTable; } void PicoFreeVertexCombinationHashTable( picoVertexCombinationHash_t **hashTable ) { int i; picoVertexCombinationHash_t *vertexCombinationHash; picoVertexCombinationHash_t *nextVertexCombinationHash; /* dummy check */ if (hashTable == NULL) return; for( i = 0; i < HASHTABLE_SIZE; i++ ) { if (hashTable[ i ]) { nextVertexCombinationHash = NULL; for( vertexCombinationHash = hashTable[ i ]; vertexCombinationHash; vertexCombinationHash = nextVertexCombinationHash ) { nextVertexCombinationHash = vertexCombinationHash->next; if (vertexCombinationHash->data != NULL) { _pico_free( vertexCombinationHash->data ); } _pico_free( vertexCombinationHash ); } } } _pico_free( hashTable ); } picoVertexCombinationHash_t *PicoFindVertexCombinationInHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color ) { unsigned int hash; picoVertexCombinationHash_t *vertexCombinationHash; /* dumy check */ if (hashTable == NULL || xyz == NULL || normal == NULL || st == NULL || color == NULL ) return NULL; hash = PicoVertexCoordGenerateHash( xyz ); for( vertexCombinationHash = hashTable[ hash ]; vertexCombinationHash; vertexCombinationHash = vertexCombinationHash->next ) { #ifndef HASH_USE_EPSILON /* check xyz */ if( (vertexCombinationHash->vcd.xyz[ 0 ] != xyz[ 0 ] || vertexCombinationHash->vcd.xyz[ 1 ] != xyz[ 1 ] || vertexCombinationHash->vcd.xyz[ 2 ] != xyz[ 2 ]) ) continue; /* check normal */ if( (vertexCombinationHash->vcd.normal[ 0 ] != normal[ 0 ] || vertexCombinationHash->vcd.normal[ 1 ] != normal[ 1 ] || vertexCombinationHash->vcd.normal[ 2 ] != normal[ 2 ]) ) continue; /* check st */ if( vertexCombinationHash->vcd.st[ 0 ] != st[ 0 ] || vertexCombinationHash->vcd.st[ 1 ] != st[ 1 ] ) continue; #else /* check xyz */ if( ( fabs(xyz[ 0 ] - vertexCombinationHash->vcd.xyz[ 0 ]) ) > HASH_XYZ_EPSILON || ( fabs(xyz[ 1 ] - vertexCombinationHash->vcd.xyz[ 1 ]) ) > HASH_XYZ_EPSILON || ( fabs(xyz[ 2 ] - vertexCombinationHash->vcd.xyz[ 2 ]) ) > HASH_XYZ_EPSILON ) continue; /* check normal */ if( ( fabs(normal[ 0 ] - vertexCombinationHash->vcd.normal[ 0 ]) ) > HASH_NORMAL_EPSILON || ( fabs(normal[ 1 ] - vertexCombinationHash->vcd.normal[ 1 ]) ) > HASH_NORMAL_EPSILON || ( fabs(normal[ 2 ] - vertexCombinationHash->vcd.normal[ 2 ]) ) > HASH_NORMAL_EPSILON ) continue; /* check st */ if( ( fabs(st[ 0 ] - vertexCombinationHash->vcd.st[ 0 ]) ) > HASH_ST_EPSILON || ( fabs(st[ 1 ] - vertexCombinationHash->vcd.st[ 1 ]) ) > HASH_ST_EPSILON ) continue; #endif /* check color */ if (vertexCombinationHash->vcd.color[0] != color[0] || vertexCombinationHash->vcd.color[1] != color[1] || vertexCombinationHash->vcd.color[2] != color[2] || vertexCombinationHash->vcd.color[3] != color[3]) /* greebo: Old code: if( *((int*) vertexCombinationHash->vcd.color) != *((int*) color) )*/ { continue; } /* gotcha */ return vertexCombinationHash; } return NULL; } picoVertexCombinationHash_t *PicoAddVertexCombinationToHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color, picoIndex_t index ) { unsigned int hash; picoVertexCombinationHash_t *vertexCombinationHash; /* dumy check */ if (hashTable == NULL || xyz == NULL || normal == NULL || st == NULL || color == NULL ) return NULL; vertexCombinationHash = _pico_alloc( sizeof(picoVertexCombinationHash_t) ); if (!vertexCombinationHash) return NULL; hash = PicoVertexCoordGenerateHash( xyz ); _pico_copy_vec( xyz, vertexCombinationHash->vcd.xyz ); _pico_copy_vec( normal, vertexCombinationHash->vcd.normal ); _pico_copy_vec2( st, vertexCombinationHash->vcd.st ); _pico_copy_color( color, vertexCombinationHash->vcd.color ); vertexCombinationHash->index = index; vertexCombinationHash->data = NULL; vertexCombinationHash->next = hashTable[ hash ]; hashTable[ hash ] = vertexCombinationHash; return vertexCombinationHash; } /* ---------------------------------------------------------------------------- specialized routines ---------------------------------------------------------------------------- */ /* PicoFindSurfaceVertex() finds a vertex matching the set parameters fixme: needs non-naive algorithm */ int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color, picoIndex_t smoothingGroup) { int i, j; /* dummy check */ if( surface == NULL || surface->numVertexes <= 0 ) return -1; /* walk vertex list */ for( i = 0; i < surface->numVertexes; i++ ) { /* check xyz */ if( xyz != NULL && (surface->xyz[ i ][ 0 ] != xyz[ 0 ] || surface->xyz[ i ][ 1 ] != xyz[ 1 ] || surface->xyz[ i ][ 2 ] != xyz[ 2 ]) ) continue; /* check normal */ if( normal != NULL && (surface->normal[ i ][ 0 ] != normal[ 0 ] || surface->normal[ i ][ 1 ] != normal[ 1 ] || surface->normal[ i ][ 2 ] != normal[ 2 ]) ) continue; /* check normal */ if( surface->smoothingGroup[ i ] != smoothingGroup ) continue; /* check st */ if( numSTs > 0 && st != NULL ) { for( j = 0; j < numSTs; j++ ) { if( surface->st[ j ][ i ][ 0 ] != st[ j ][ 0 ] || surface->st[ j ][ i ][ 1 ] != st[ j ][ 1 ] ) break; } if( j != numSTs ) continue; } /* check color */ if( numColors > 0 && color != NULL ) { for( j = 0; j < numSTs; j++ ) { if( *((int*) surface->color[ j ]) != *((int*) color[ j ]) ) break; } if( j != numColors ) continue; } /* vertex matches */ return i; } /* nada */ return -1; } typedef struct _IndexArray IndexArray; struct _IndexArray { picoIndex_t* data; picoIndex_t* last; }; void indexarray_push_back(IndexArray* self, picoIndex_t value) { *self->last++ = value; } size_t indexarray_size(IndexArray* self) { return self->last - self->data; } void indexarray_reserve(IndexArray* self, size_t size) { self->data = self->last = _pico_calloc(size, sizeof(picoIndex_t)); } void indexarray_clear(IndexArray* self) { _pico_free(self->data); } typedef struct _BinaryTreeNode BinaryTreeNode; struct _BinaryTreeNode { picoIndex_t left; picoIndex_t right; }; typedef struct _BinaryTree BinaryTree; struct _BinaryTree { BinaryTreeNode* data; BinaryTreeNode* last; }; void binarytree_extend(BinaryTree* self) { self->last->left = 0; self->last->right = 0; ++self->last; } size_t binarytree_size(BinaryTree* self) { return self->last - self->data; } void binarytree_reserve(BinaryTree* self, size_t size) { self->data = self->last = _pico_calloc(size, sizeof(BinaryTreeNode)); } void binarytree_clear(BinaryTree* self) { _pico_free(self->data); } typedef int (*LessFunc)(void*, picoIndex_t, picoIndex_t); typedef struct _UniqueIndices UniqueIndices; struct _UniqueIndices { BinaryTree tree; IndexArray indices; LessFunc lessFunc; void* lessData; }; size_t UniqueIndices_size(UniqueIndices* self) { return binarytree_size(&self->tree); } void UniqueIndices_reserve(UniqueIndices* self, size_t size) { binarytree_reserve(&self->tree, size); indexarray_reserve(&self->indices, size); } void UniqueIndices_init(UniqueIndices* self, LessFunc lessFunc, void* lessData) { self->lessFunc = lessFunc; self->lessData = lessData; } void UniqueIndices_destroy(UniqueIndices* self) { binarytree_clear(&self->tree); indexarray_clear(&self->indices); } picoIndex_t UniqueIndices_find_or_insert(UniqueIndices* self, picoIndex_t value) { picoIndex_t index = 0; for(;;) { if(self->lessFunc(self->lessData, value, self->indices.data[index])) { BinaryTreeNode* node = self->tree.data + index; if(node->left != 0) { index = node->left; continue; } else { node->left = (picoIndex_t)binarytree_size(&self->tree); binarytree_extend(&self->tree); indexarray_push_back(&self->indices, value); return node->left; } } if(self->lessFunc(self->lessData, self->indices.data[index], value)) { BinaryTreeNode* node = self->tree.data + index; if(node->right != 0) { index = node->right; continue; } else { node->right = (picoIndex_t)binarytree_size(&self->tree); binarytree_extend(&self->tree); indexarray_push_back(&self->indices, value); return node->right; } } return index; } } picoIndex_t UniqueIndices_insert(UniqueIndices* self, picoIndex_t value) { if(self->tree.data == self->tree.last) { binarytree_extend(&self->tree); indexarray_push_back(&self->indices, value); return 0; } else { return UniqueIndices_find_or_insert(self, value); } } typedef struct picoSmoothVertices_s picoSmoothVertices_t; struct picoSmoothVertices_s { picoVec3_t* xyz; picoIndex_t* smoothingGroups; }; int lessSmoothVertex(void* data, picoIndex_t first, picoIndex_t second) { picoSmoothVertices_t* smoothVertices = data; if(smoothVertices->xyz[first][0] != smoothVertices->xyz[second][0]) { return smoothVertices->xyz[first][0] < smoothVertices->xyz[second][0]; } if(smoothVertices->xyz[first][1] != smoothVertices->xyz[second][1]) { return smoothVertices->xyz[first][1] < smoothVertices->xyz[second][1]; } if(smoothVertices->xyz[first][2] != smoothVertices->xyz[second][2]) { return smoothVertices->xyz[first][2] < smoothVertices->xyz[second][2]; } if(smoothVertices->smoothingGroups[first] != smoothVertices->smoothingGroups[second]) { return smoothVertices->smoothingGroups[first] < smoothVertices->smoothingGroups[second]; } return 0; } void _pico_vertices_combine_shared_normals(picoVec3_t* xyz, picoIndex_t* smoothingGroups, picoVec3_t* normals, picoIndex_t numVertices) { UniqueIndices vertices; IndexArray indices; picoSmoothVertices_t smoothVertices = { xyz, smoothingGroups }; UniqueIndices_init(&vertices, lessSmoothVertex, &smoothVertices); UniqueIndices_reserve(&vertices, numVertices); indexarray_reserve(&indices, numVertices); { picoIndex_t i = 0; for(; i < numVertices; ++i) { size_t size = UniqueIndices_size(&vertices); picoIndex_t index = UniqueIndices_insert(&vertices, i); if((size_t)index != size) { double* normal = normals[vertices.indices.data[index]]; _pico_add_vec(normal, normals[i], normal); } indexarray_push_back(&indices, index); } } { picoIndex_t maxIndex = 0; picoIndex_t* i = indices.data; for(; i != indices.last; ++i) { if(*i <= maxIndex) { _pico_copy_vec(normals[vertices.indices.data[*i]], normals[i - indices.data]); } else { maxIndex = *i; } } } UniqueIndices_destroy(&vertices); indexarray_clear(&indices); } typedef picoVec3_t* picoNormalIter_t; typedef picoIndex_t* picoIndexIter_t; #define THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL 1 void _pico_triangles_generate_weighted_normals(picoIndexIter_t first, picoIndexIter_t end, picoVec3_t* xyz, picoVec3_t* normals) { for(; first != end; first += 3) { #if (THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL) picoVec3_t weightedNormal; { double* a = xyz[*(first + 0)]; double* b = xyz[*(first + 1)]; double* c = xyz[*(first + 2)]; picoVec3_t ba, ca; _pico_subtract_vec( b, a, ba ); _pico_subtract_vec( c, a, ca ); _pico_cross_vec( ca, ba, weightedNormal ); } #endif { int j = 0; for(; j < 3; ++j) { double* normal = normals[*(first + j)]; #if (!THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL) picoVec3_t weightedNormal; { double* a = xyz[*(first + ((j + 0) % 3))]; double* b = xyz[*(first + ((j + 1) % 3))]; double* c = xyz[*(first + ((j + 2) % 3))]; picoVec3_t ba, ca; _pico_subtract_vec( b, a, ba ); _pico_subtract_vec( c, a, ca ); _pico_cross_vec( ca, ba, weightedNormal ); } #endif _pico_add_vec(weightedNormal, normal, normal); } } } } void _pico_normals_zero(picoNormalIter_t first, picoNormalIter_t last) { for(; first != last; ++first) { _pico_zero_vec(*first); } } void _pico_normals_normalize(picoNormalIter_t first, picoNormalIter_t last) { for(; first != last; ++first) { _pico_normalize_vec(*first); } } double _pico_length_vec( picoVec3_t vec ) { return sqrt( vec[ 0 ] * vec[ 0 ] + vec[ 1 ] * vec[ 1 ] + vec[ 2 ] * vec[ 2 ] ); } #define NORMAL_UNIT_LENGTH_EPSILON 0.01 #define FLOAT_EQUAL_EPSILON(f, other, epsilon) (fabs(f - other) < epsilon) int _pico_normal_is_unit_length(picoVec3_t normal) { return FLOAT_EQUAL_EPSILON(_pico_length_vec(normal), 1.0, NORMAL_UNIT_LENGTH_EPSILON); } int _pico_normal_within_tolerance(picoVec3_t normal, picoVec3_t other) { return _pico_dot_vec(normal, other) > 0.0f; } void _pico_normals_assign_generated_normals(picoNormalIter_t first, picoNormalIter_t last, picoNormalIter_t generated) { for(; first != last; ++first, ++generated) { if(!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated)) { _pico_copy_vec(*generated, *first); } } } void PicoFixSurfaceNormals(picoSurface_t* surface) { picoVec3_t* normals = (picoVec3_t*)_pico_calloc(surface->numVertexes, sizeof(picoVec3_t)); _pico_normals_zero(normals, normals + surface->numVertexes); _pico_triangles_generate_weighted_normals(surface->index, surface->index + surface->numIndexes, surface->xyz, normals); _pico_vertices_combine_shared_normals(surface->xyz, surface->smoothingGroup, normals, surface->numVertexes); _pico_normals_normalize(normals, normals + surface->numVertexes); _pico_normals_assign_generated_normals(surface->normal, surface->normal + surface->numVertexes, normals); _pico_free(normals); } /* PicoRemapModel() - sea remaps model material/etc. information using the remappings contained in the given 'remapFile' (full path to the ascii file to open) returns 1 on success or 0 on error */ #define _prm_error_return \ { \ _pico_free_parser( p ); \ _pico_free_file( remapBuffer ); \ return 0; \ } int PicoRemapModel( picoModel_t *model, char *remapFile ) { picoParser_t *p; picoByte_t *remapBuffer; int remapBufSize; /* sanity checks */ if( model == NULL || remapFile == NULL ) return 0; /* load remap file contents */ _pico_load_file( remapFile,&remapBuffer,&remapBufSize ); /* check result */ if( remapBufSize == 0 ) return 1; /* file is empty: no error */ if( remapBufSize < 0 ) return 0; /* load failed: error */ /* create a new pico parser */ p = _pico_new_parser( remapBuffer, remapBufSize ); if (p == NULL) { /* ram is really cheap nowadays... */ _prm_error_return; } /* doo teh parse */ while( 1 ) { /* get next token in remap file */ if (!_pico_parse( p,1 )) break; /* skip over c++ style comment lines */ if (!_pico_stricmp(p->token,"//")) { _pico_parse_skip_rest( p ); continue; } /* block for quick material shader name remapping */ /* materials { "m" (=>|->|=) "s" } */ if( !_pico_stricmp(p->token, "materials" ) ) { int level = 1; /* check bracket */ if (!_pico_parse_check( p,1,"{" )) _prm_error_return; /* process assignments */ while( 1 ) { picoShader_t *shader; char *materialName; /* get material name */ if (_pico_parse( p,1 ) == NULL) break; if (!strlen(p->token)) continue; materialName = _pico_clone_alloc( p->token ); if (materialName == NULL) _prm_error_return; /* handle levels */ if (p->token[0] == '{') level++; if (p->token[0] == '}') level--; if (!level) break; /* get next token (assignment token or shader name) */ if (!_pico_parse( p,0 )) { _pico_free( materialName ); _prm_error_return; } /* skip assignment token (if present) */ if (!strcmp(p->token,"=>") || !strcmp(p->token,"->") || !strcmp(p->token,"=")) { /* simply grab the next token */ if (!_pico_parse( p,0 )) { _pico_free( materialName ); _prm_error_return; } } /* try to find material by name */ shader = PicoFindShader( model,materialName,0 ); /* we've found a material matching the name */ if (shader != NULL) { PicoSetShaderName( shader,p->token ); } /* free memory used by material name */ _pico_free( materialName ); /* skip rest */ _pico_parse_skip_rest( p ); } } /* block for detailed single material remappings */ /* materials[ "m" ] { key data... } */ else if (!_pico_stricmp(p->token,"materials[")) { picoShader_t *shader; char *tempMaterialName; int level = 1; /* get material name */ if (!_pico_parse( p,0 )) _prm_error_return; /* temporary copy of material name */ tempMaterialName = _pico_clone_alloc( p->token ); if (tempMaterialName == NULL) _prm_error_return; /* check square closing bracket */ if (!_pico_parse_check( p,0,"]" )) _prm_error_return; /* try to find material by name */ shader = PicoFindShader( model,tempMaterialName,0 ); /* free memory used by temporary material name */ _pico_free( tempMaterialName ); /* we haven't found a material matching the name */ /* so we simply skip the braced section now and */ /* continue parsing with the next main token */ if (shader == NULL) { _pico_parse_skip_braced( p ); continue; } /* check opening bracket */ if (!_pico_parse_check( p,1,"{" )) _prm_error_return; /* process material info keys */ while( 1 ) { /* get key name */ if (_pico_parse( p,1 ) == NULL) break; if (!strlen(p->token)) continue; /* handle levels */ if (p->token[0] == '{') level++; if (p->token[0] == '}') level--; if (!level) break; /* remap shader name */ if (!_pico_stricmp(p->token,"shader")) { if (!_pico_parse( p,0 )) _prm_error_return; PicoSetShaderName( shader,p->token ); } /* remap shader map name */ else if (!_pico_stricmp(p->token,"mapname")) { if (!_pico_parse( p,0 )) _prm_error_return; PicoSetShaderMapName( shader,p->token ); } /* remap shader's ambient color */ else if (!_pico_stricmp(p->token,"ambient")) { picoColor_t color; picoVec3_t v; /* get vector from parser */ if (!_pico_parse_vec( p,v )) _prm_error_return; /* store as color */ color[ 0 ] = (picoByte_t)v[ 0 ]; color[ 1 ] = (picoByte_t)v[ 1 ]; color[ 2 ] = (picoByte_t)v[ 2 ]; /* set new ambient color */ PicoSetShaderAmbientColor( shader,color ); } /* remap shader's diffuse color */ else if (!_pico_stricmp(p->token,"diffuse")) { picoColor_t color; picoVec3_t v; /* get vector from parser */ if (!_pico_parse_vec( p,v )) _prm_error_return; /* store as color */ color[ 0 ] = (picoByte_t)v[ 0 ]; color[ 1 ] = (picoByte_t)v[ 1 ]; color[ 2 ] = (picoByte_t)v[ 2 ]; /* set new ambient color */ PicoSetShaderDiffuseColor( shader,color ); } /* remap shader's specular color */ else if (!_pico_stricmp(p->token,"specular")) { picoColor_t color; picoVec3_t v; /* get vector from parser */ if (!_pico_parse_vec( p,v )) _prm_error_return; /* store as color */ color[ 0 ] = (picoByte_t)v[ 0 ]; color[ 1 ] = (picoByte_t)v[ 1 ]; color[ 2 ] = (picoByte_t)v[ 2 ]; /* set new ambient color */ PicoSetShaderSpecularColor( shader,color ); } /* skip rest */ _pico_parse_skip_rest( p ); } } /* end 'materials[' */ } /* free both parser and file buffer */ _pico_free_parser( p ); _pico_free_file( remapBuffer ); /* return with success */ return 1; } /* PicoAddTriangleToModel() - jhefty A nice way to add individual triangles to the model. Chooses an appropriate surface based on the shader, or adds a new surface if necessary */ void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors, picoShader_t* shader, picoIndex_t* smoothingGroup ) { int i,j; int vertDataIndex; picoSurface_t* workSurface = NULL; /* see if a surface already has the shader */ for ( i = 0 ; i < model->numSurfaces ; i++ ) { workSurface = model->surface[i]; if ( workSurface->shader == shader ) { break; } } /* no surface uses this shader yet, so create a new surface */ if ( !workSurface || i >=model->numSurfaces ) { /* create a new surface in the model for the unique shader */ workSurface = PicoNewSurface(model); if ( !workSurface ) { _pico_printf ( PICO_ERROR , "Could not allocate a new surface!\n" ); return; } /* do surface setup */ PicoSetSurfaceType( workSurface, PICO_TRIANGLES ); PicoSetSurfaceName( workSurface, shader->name ); PicoSetSurfaceShader( workSurface, shader ); } /* add the triangle data to the surface */ for ( i = 0 ; i < 3 ; i++ ) { /* get the next free spot in the index array */ int newVertIndex = PicoGetSurfaceNumIndexes ( workSurface ); /* get the index of the vertex that we're going to store at newVertIndex */ vertDataIndex = PicoFindSurfaceVertexNum ( workSurface , *xyz[i] , *normals[i] , numSTs , st[i] , numColors , colors[i], smoothingGroup[i]); /* the vertex wasn't found, so create a new vertex in the pool from the data we have */ if ( vertDataIndex == -1 ) { /* find the next spot for a new vertex */ vertDataIndex = PicoGetSurfaceNumVertexes ( workSurface ); /* assign the data to it */ PicoSetSurfaceXYZ ( workSurface ,vertDataIndex , *xyz[i] ); PicoSetSurfaceNormal ( workSurface , vertDataIndex , *normals[i] ); /* make sure to copy over all available ST's and colors for the vertex */ for ( j = 0 ; j < numColors ; j++ ) { PicoSetSurfaceColor( workSurface , j , vertDataIndex , colors[i][j] ); } for ( j = 0 ; j < numSTs ; j++ ) { PicoSetSurfaceST ( workSurface , j , vertDataIndex , st[i][j] ); } PicoSetSurfaceSmoothingGroup ( workSurface , vertDataIndex , smoothingGroup[i] ); } /* add this vertex to the triangle */ PicoSetSurfaceIndex ( workSurface , newVertIndex , vertDataIndex ); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/picomodules.c������������������������������������������������������0000664�0000000�0000000�00000006151�13217505464�0021307�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define PICOMODULES_C /* dependencies */ #include "picointernal.h" /* external modules */ extern const picoModule_t picoModuleMD3; extern const picoModule_t picoModule3DS; extern const picoModule_t picoModuleASE; extern const picoModule_t picoModuleOBJ; extern const picoModule_t picoModuleMS3D; extern const picoModule_t picoModuleMDC; extern const picoModule_t picoModuleMD2; extern const picoModule_t picoModuleFM; extern const picoModule_t picoModuleLWO; extern const picoModule_t picoModuleTerrain; /* list of all supported file format modules */ const picoModule_t *picoModules[] = { &picoModuleMD3, /* quake3 arena md3 */ &picoModule3DS, /* autodesk 3ds */ &picoModuleASE, /* autodesk ase */ &picoModuleMS3D, /* milkshape3d */ &picoModuleMDC, /* return to castle wolfenstein mdc */ &picoModuleMD2, /* quake2 md2 */ &picoModuleFM, /* heretic2 fm */ &picoModuleLWO, /* lightwave object */ &picoModuleTerrain, /* picoterrain object */ &picoModuleOBJ, /* wavefront object */ NULL /* arnold */ }; /* PicoModuleList() returns a pointer to the module list and optionally stores the number of supported modules in 'numModules'. Note that this param can be NULL when the count is not needed. */ const picoModule_t **PicoModuleList( int *numModules ) { /* get module count */ if( numModules != NULL ) for( (*numModules) = 0; picoModules[ *numModules ] != NULL; (*numModules)++ ); /* return list of modules */ return (const picoModule_t**) picoModules; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/pm_3ds.c�����������������������������������������������������������0000664�0000000�0000000�00000047556�13217505464�0020167�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define PM_3DS_C /* dependencies */ #include "picointernal.h" /* ydnar */ static picoColor_t white = { 255,255,255,255 }; /* remarks: * - 3ds file version is stored in pico special field 0 on load (ydnar: removed) * todo: * - sometimes there is one unnamed surface 0 having 0 verts as * well as 0 faces. this error occurs since pm 0.6 (ydnar?) */ /* uncomment when debugging this module */ /* #define DEBUG_PM_3DS #define DEBUG_PM_3DS_EX */ /* structure holding persistent 3ds loader specific data used */ /* to store formerly static vars to keep the module reentrant */ /* safe. put everything that needs to be static in here. */ typedef struct S3dsLoaderPers { picoModel_t *model; /* ptr to output model */ picoSurface_t *surface; /* ptr to current surface */ picoShader_t *shader; /* ptr to current shader */ picoByte_t *bufptr; /* ptr to raw data */ char *basename; /* ptr to model base name (eg. jeep) */ int cofs; int maxofs; } T3dsLoaderPers; /* 3ds chunk types that we use */ enum { /* primary chunk */ CHUNK_MAIN = 0x4D4D, /* main chunks */ CHUNK_VERSION = 0x0002, CHUNK_EDITOR_CONFIG = 0x3D3E, CHUNK_EDITOR_DATA = 0x3D3D, CHUNK_KEYFRAME_DATA = 0xB000, /* editor data sub chunks */ CHUNK_MATERIAL = 0xAFFF, CHUNK_OBJECT = 0x4000, /* material sub chunks */ CHUNK_MATNAME = 0xA000, CHUNK_MATDIFFUSE = 0xA020, CHUNK_MATMAP = 0xA200, CHUNK_MATMAPFILE = 0xA300, /* lets us know we're reading a new object */ CHUNK_OBJECT_MESH = 0x4100, /* object mesh sub chunks */ CHUNK_OBJECT_VERTICES = 0x4110, CHUNK_OBJECT_FACES = 0x4120, CHUNK_OBJECT_MATERIAL = 0x4130, CHUNK_OBJECT_UV = 0x4140, }; #ifdef DEBUG_PM_3DS static struct { int id; char *name; } debugChunkNames[] = { { CHUNK_MAIN , "CHUNK_MAIN" }, { CHUNK_VERSION , "CHUNK_VERSION" }, { CHUNK_EDITOR_CONFIG , "CHUNK_EDITOR_CONFIG" }, { CHUNK_EDITOR_DATA , "CHUNK_EDITOR_DATA" }, { CHUNK_KEYFRAME_DATA , "CHUNK_KEYFRAME_DATA" }, { CHUNK_MATERIAL , "CHUNK_MATERIAL" }, { CHUNK_OBJECT , "CHUNK_OBJECT" }, { CHUNK_MATNAME , "CHUNK_MATNAME" }, { CHUNK_MATDIFFUSE , "CHUNK_MATDIFFUSE" }, { CHUNK_MATMAP , "CHUNK_MATMAP" }, { CHUNK_MATMAPFILE , "CHUNK_MATMAPFILE" }, { CHUNK_OBJECT_MESH , "CHUNK_OBJECT_MESH" }, { CHUNK_OBJECT_VERTICES , "CHUNK_OBJECT_VERTICES" }, { CHUNK_OBJECT_FACES , "CHUNK_OBJECT_FACES" }, { CHUNK_OBJECT_MATERIAL , "CHUNK_OBJECT_MATERIAL" }, { CHUNK_OBJECT_UV , "CHUNK_OBJECT_UV" }, { 0 , NULL } }; static char *DebugGetChunkName (int id) { int i,max; /* imax? ;) */ max = sizeof(debugChunkNames) / sizeof(debugChunkNames[0]); for (i=0; ilen)) return PICO_PMV_ERROR_SIZE; /* check 3ds magic */ if (_pico_little_short(chunk->id) != CHUNK_MAIN) return PICO_PMV_ERROR_IDENT; /* file seems to be a valid 3ds */ return PICO_PMV_OK; } static T3dsChunk *GetChunk (T3dsLoaderPers *pers) { T3dsChunk *chunk; /* sanity check */ if (pers->cofs > pers->maxofs) return 0; #ifdef DEBUG_PM_3DS /* printf("GetChunk: pers->cofs %x\n",pers->cofs); */ #endif /* fill in pointer to chunk */ chunk = (T3dsChunk *)&pers->bufptr[ pers->cofs ]; if (!chunk) return NULL; chunk->id = _pico_little_short(chunk->id ); chunk->len = _pico_little_long (chunk->len); /* advance in buffer */ pers->cofs += sizeof(T3dsChunk); /* this means yay */ return chunk; } static int GetASCIIZ (T3dsLoaderPers *pers, char *dest, int max) { int pos = 0; int ch; for (;;) { ch = pers->bufptr[ pers->cofs++ ]; if (ch == '\0') break; if (pers->cofs >= pers->maxofs) { dest[ pos ] = '\0'; return 0; } dest[ pos++ ] = ch; if (pos >= max) break; } dest[ pos ] = '\0'; return 1; } static picoByte_t GetByte (T3dsLoaderPers *pers) { picoByte_t *value; /* sanity check */ if (pers->cofs > pers->maxofs) return 0; /* get and return value */ value = (picoByte_t *)(pers->bufptr + pers->cofs); pers->cofs += 1; return *value; } static int GetWord (T3dsLoaderPers *pers) { unsigned short *value; /* sanity check */ if (pers->cofs > pers->maxofs) return 0; /* get and return value */ value = (unsigned short *)(pers->bufptr + pers->cofs); pers->cofs += 2; return _pico_little_short(*value); } static float GetFloat (T3dsLoaderPers *pers) { float *value; /* sanity check */ if (pers->cofs > pers->maxofs) return 0; /* get and return value */ value = (float *)(pers->bufptr + pers->cofs); pers->cofs += 4; return _pico_little_float(*value); } static int GetMeshVertices (T3dsLoaderPers *pers) { int numVerts; int i; /* get number of verts for this surface */ numVerts = GetWord(pers); #ifdef DEBUG_PM_3DS printf("GetMeshVertices: numverts %d\n",numVerts); #endif /* read in vertices for current surface */ for (i=0; isurface,i,v ); PicoSetSurfaceColor( pers->surface,0,i,white ); /* ydnar */ #ifdef DEBUG_PM_3DS_EX printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]); #endif } /* success (no errors occured) */ return 1; } static int GetMeshFaces (T3dsLoaderPers *pers) { int numFaces; int i; /* get number of faces for this surface */ numFaces = GetWord(pers); #ifdef DEBUG_PM_3DS printf("GetMeshFaces: numfaces %d\n",numFaces); #endif /* read in vertex indices for current surface */ for (i=0; isurface, (i * 3 + 0), (picoIndex_t)face.a ); PicoSetSurfaceIndex( pers->surface, (i * 3 + 1), (picoIndex_t)face.b ); PicoSetSurfaceIndex( pers->surface, (i * 3 + 2), (picoIndex_t)face.c ); #ifdef DEBUG_PM_3DS_EX printf("Face: a: %d b: %d c: %d (%d)\n",face.a,face.b,face.c,face.visible); #endif } /* success (no errors occured) */ return 1; } static int GetMeshTexCoords (T3dsLoaderPers *pers) { int numTexCoords; int i; /* get number of uv coords for this surface */ numTexCoords = GetWord(pers); #ifdef DEBUG_PM_3DS printf("GetMeshTexCoords: numcoords %d\n",numTexCoords); #endif /* read in uv coords for current surface */ for (i=0; isurface == NULL) continue; /* add current uv */ PicoSetSurfaceST( pers->surface,0,i,uv ); #ifdef DEBUG_PM_3DS_EX printf("u: %f v: %f\n",uv[0],uv[1]); #endif } /* success (no errors occured) */ return 1; } static int GetMeshShader (T3dsLoaderPers *pers) { char shaderName[255] = { 0 }; picoShader_t *shader; int numSharedVerts; int setShaderName = 0; int i; /* the shader is either the color or the texture map of the */ /* object. it can also hold other information like the brightness, */ /* shine, etc. stuff we don't really care about. we just want the */ /* color, or the texture map file name really */ /* get in the shader name */ if (!GetASCIIZ(pers,shaderName,sizeof(shaderName))) return 0; /* ydnar: trim to first whitespace */ _pico_first_token( shaderName ); /* now that we have the shader name we need to go through all of */ /* the shaders and check the name against each shader. when we */ /* find a shader in our shader list that matches this name we */ /* just read in, then we assign the shader's id of the object to */ /* that shader */ /* get shader id for shader name */ shader = PicoFindShader( pers->model, shaderName, 1 ); /* we've found a matching shader */ if ((shader != NULL) && pers->surface) { char mapName[1024+1]; char *mapNamePtr; memset( mapName,0,sizeof(mapName) ); /* get ptr to shader's map name */ mapNamePtr = PicoGetShaderMapName( shader ); /* we have a valid map name ptr */ if (mapNamePtr != NULL) { char temp[128]; const char *name; /* copy map name to local buffer */ strcpy( mapName,mapNamePtr ); /* extract file name */ name = _pico_nopath( mapName ); strncpy( temp, name, sizeof(temp) ); /* remove file extension */ /* name = _pico_setfext( name,"" ); */ /* assign default name if no name available */ if (strlen(temp) < 1) strcpy(temp,pers->basename); /* build shader name */ _pico_strlwr( temp ); /* gaynux update -sea */ sprintf( mapName,"models/mapobjects/%s/%s",pers->basename,temp ); /* set shader name */ /* PicoSetShaderName( shader,mapName ); */ /* ydnar: this will screw up the named shader */ /* set surface's shader index */ PicoSetSurfaceShader( pers->surface, shader ); setShaderName = 1; } } /* we didn't set a shader name; throw out warning */ if (!setShaderName) { _pico_printf( PICO_WARNING,"3DS mesh is missing shader name"); } /* we don't process the list of shared vertices here; there is a */ /* short int that gives the number of faces of the mesh concerned */ /* by this shader, then there is the list itself of these faces. */ /* 0000 means the first face of the (4120) face list */ /* get number of shared verts */ numSharedVerts = GetWord(pers); #ifdef DEBUG_PM_3DS printf("GetMeshShader: uses shader '%s' (nsv %d)\n",shaderName,numSharedVerts); #endif /* skip list of shared verts */ for (i=0; ishader ) { PicoSetShaderDiffuseColor( pers->shader,color ); } #ifdef DEBUG_PM_3DS printf("GetDiffuseColor: %d %d %d\n",color[0],color[1],color[2]); #endif /* success (no errors occured) */ return 1; } static int DoNextEditorDataChunk (T3dsLoaderPers *pers, long endofs) { T3dsChunk *chunk; #ifdef DEBUG_PM_3DS_EX printf("DoNextEditorDataChunk: endofs %d\n",endofs); #endif while (pers->cofs < endofs) { long nextofs = pers->cofs; if ((chunk = GetChunk(pers)) == NULL) return 0; if (!chunk->len) return 0; nextofs += chunk->len; #ifdef DEBUG_PM_3DS_EX printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs); #endif /*** meshes ***/ if (chunk->id == CHUNK_OBJECT) { picoSurface_t *surface; char surfaceName[ 0xff ] = { 0 }; /* read in surface name */ if( !GetASCIIZ(pers,surfaceName,sizeof(surfaceName)) ) return 0; /* this is bad */ //PicoGetSurfaceName /* ignore NULL name surfaces */ // if( surfaceName /* allocate a pico surface */ surface = PicoNewSurface( pers->model ); if( surface == NULL ) { pers->surface = NULL; return 0; /* this is bad too */ } /* assign ptr to current surface */ pers->surface = surface; /* 3ds models surfaces are all triangle meshes */ PicoSetSurfaceType( pers->surface,PICO_TRIANGLES ); /* set surface name */ PicoSetSurfaceName( pers->surface,surfaceName ); /* continue mess with object's sub chunks */ DoNextEditorDataChunk(pers,nextofs); continue; } if (chunk->id == CHUNK_OBJECT_MESH) { /* continue mess with mesh's sub chunks */ if (!DoNextEditorDataChunk(pers,nextofs)) return 0; continue; } if (chunk->id == CHUNK_OBJECT_VERTICES) { if (!GetMeshVertices(pers)) return 0; continue; } if (chunk->id == CHUNK_OBJECT_FACES) { if (!GetMeshFaces(pers)) return 0; continue; } if (chunk->id == CHUNK_OBJECT_UV) { if (!GetMeshTexCoords(pers)) return 0; continue; } if (chunk->id == CHUNK_OBJECT_MATERIAL) { if (!GetMeshShader(pers)) return 0; continue; } /*** materials ***/ if (chunk->id == CHUNK_MATERIAL) { /* new shader specific things should be */ /* initialized right here */ picoShader_t *shader; /* allocate a pico shader */ shader = PicoNewShader( pers->model ); /* ydnar */ if( shader == NULL ) { pers->shader = NULL; return 0; /* this is bad too */ } /* assign ptr to current shader */ pers->shader = shader; /* continue and process the material's sub chunks */ DoNextEditorDataChunk(pers,nextofs); continue; } if (chunk->id == CHUNK_MATNAME) { /* new material's names should be stored here. note that */ /* GetMeshMaterial returns the name of the material that */ /* is used by the mesh. new material names are set HERE. */ /* but for now we skip the new material's name ... */ if (pers->shader) { char *name = (char*) (pers->bufptr + pers->cofs); char *cleanedName = _pico_clone_alloc( name ); _pico_first_token( cleanedName ); PicoSetShaderName( pers->shader, cleanedName ); #ifdef DEBUG_PM_3DS printf( "NewShader: '%s'\n", cleanedName ); #endif _pico_free( cleanedName ); } } if (chunk->id == CHUNK_MATDIFFUSE) { /* todo: color for last inserted new material should be */ /* stored somewhere by GetDiffuseColor */ if (!GetDiffuseColor(pers)) return 0; /* rest of chunk is skipped here */ } if (chunk->id == CHUNK_MATMAP) { /* continue and process the material map sub chunks */ DoNextEditorDataChunk(pers,nextofs); continue; } if (chunk->id == CHUNK_MATMAPFILE) { /* map file name for last inserted new material should */ /* be stored here. but for now we skip this too ... */ if( pers->shader ) { char *name = (char *)(pers->bufptr + pers->cofs); PicoSetShaderMapName( pers->shader,name ); #ifdef DEBUG_PM_3DS printf("NewShaderMapfile: '%s'\n",name); #endif } } /*** keyframes ***/ if (chunk->id == CHUNK_KEYFRAME_DATA) { /* well umm, this is a bit too much since we don't really */ /* need model animation sequences right now. we skip this */ #ifdef DEBUG_PM_3DS printf("KeyframeData: len %d\n",chunk->len); #endif } /* skip unknown chunk */ pers->cofs = (int)nextofs; if (pers->cofs >= pers->maxofs) break; } return 1; } static int DoNextChunk (T3dsLoaderPers *pers, int endofs) { T3dsChunk *chunk; #ifdef DEBUG_PM_3DS printf("DoNextChunk: endofs %d\n",endofs); #endif while (pers->cofs < endofs) { long nextofs = pers->cofs; if ((chunk = GetChunk(pers)) == NULL) return 0; if (!chunk->len) return 0; nextofs += chunk->len; #ifdef DEBUG_PM_3DS_EX printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs); #endif /*** version ***/ if (chunk->id == CHUNK_VERSION) { /* at this point i get the 3ds file version. since there */ /* might be new additions to the 3ds file format in 4.0 */ /* it might be a good idea to store the version somewhere */ /* for later handling or message displaying */ /* get the version */ int version; version = GetWord(pers); GetWord(pers); #ifdef DEBUG_PM_3DS printf("FileVersion: %d\n",version); #endif /* throw out a warning for version 4 models */ if (version == 4) { _pico_printf( PICO_WARNING, "3DS version is 4. Model might load incorrectly."); } /* store the 3ds file version in pico special field 0 */ /* PicoSetSurfaceSpecial(pers->surface,0,version); */ /* ydnar: this was causing a crash accessing uninitialized surface */ /* rest of chunk is skipped here */ } /*** editor data ***/ if (chunk->id == CHUNK_EDITOR_DATA) { if (!DoNextEditorDataChunk(pers,nextofs)) return 0; continue; } /* skip unknown chunk */ pers->cofs = (int)nextofs; if (pers->cofs >= pers->maxofs) break; } return 1; } /* _3ds_load: * loads an autodesk 3ds model file. */ static picoModel_t *_3ds_load( PM_PARAMS_LOAD ) { T3dsLoaderPers pers; picoModel_t *model; char basename[128]; /* create a new pico model */ model = PicoNewModel(); if (model == NULL) { /* user must have some serious ram problems ;) */ return NULL; } /* get model's base name (eg. jeep from c:\models\jeep.3ds) */ memset( basename,0,sizeof(basename) ); strncpy( basename,_pico_nopath(fileName),sizeof(basename) ); _pico_setfext( basename,"" ); /* initialize persistant vars (formerly static) */ pers.model = model; pers.bufptr = (picoByte_t *)buffer; pers.basename = (char *)basename; pers.maxofs = bufSize; pers.cofs = 0L; /* do model setup */ PicoSetModelFrameNum( model,frameNum ); PicoSetModelName( model,fileName ); PicoSetModelFileName( model,fileName ); /* skip first chunk in file (magic) */ GetChunk(&pers); /* process chunks */ if (!DoNextChunk(&pers,pers.maxofs)) { /* well, bleh i guess */ PicoFreeModel(model); return NULL; } /* return allocated pico model */ return model; } /* pico file format module definition */ const picoModule_t picoModule3DS = { "0.86-b", /* module version string */ "Autodesk 3Dstudio", /* module display name */ "seaw0lf", /* author's name */ "2002 seaw0lf", /* module copyright */ { "3ds",NULL,NULL,NULL /* default extensions to use */ }, _3ds_canload, /* validation routine */ _3ds_load, /* load routine */ NULL, /* save validation routine */ NULL /* save routine */ }; ��������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/pm_ase.c�����������������������������������������������������������0000664�0000000�0000000�00000107260�13217505464�0020233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other aseMaterialList provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define PM_ASE_C /* uncomment when debugging this module */ //#define DEBUG_PM_ASE //#define DEBUG_PM_ASE_EX /* dependencies */ #include "picointernal.h" #ifdef DEBUG_PM_ASE #include "time.h" #endif /* plain white */ static picoColor_t white = { 255, 255, 255, 255 }; /* jhefty - multi-subobject material support */ /* Material/SubMaterial management */ /* A material should have 1..n submaterials assigned to it */ typedef struct aseSubMaterial_s { struct aseSubMaterial_s* next; int subMtlId; picoShader_t* shader; float uOffset; /* UVW_U_OFFSET */ float vOffset; /* UVW_V_OFFSET */ float uScale; /* UVW_U_TILING */ float vScale; /* UVW_V_TILING */ float uvAngle; /* UVW_ANGLE */ } aseSubMaterial_t; typedef struct aseMaterial_s { struct aseMaterial_s* next; struct aseSubMaterial_s* subMtls; int mtlId; } aseMaterial_t; /* Material/SubMaterial management functions */ static aseMaterial_t* _ase_get_material ( aseMaterial_t* list , int mtlIdParent ) { aseMaterial_t* mtl = list; while ( mtl ) { if ( mtlIdParent == mtl->mtlId ) { break; } mtl = mtl->next; } return mtl; } static aseSubMaterial_t* _ase_get_submaterial ( aseMaterial_t* list, int mtlIdParent , int subMtlId ) { aseMaterial_t* parent = _ase_get_material ( list , mtlIdParent ); aseSubMaterial_t* subMtl = NULL; if ( !parent ) { _pico_printf ( PICO_ERROR , "No ASE material exists with id %i\n" , mtlIdParent ); return NULL; } subMtl = parent->subMtls; while ( subMtl ) { if ( subMtlId == subMtl->subMtlId ) { break; } subMtl = subMtl->next; } return subMtl; } aseSubMaterial_t* _ase_get_submaterial_or_default ( aseMaterial_t* materials, int mtlIdParent , int subMtlId ) { aseSubMaterial_t* subMtl = _ase_get_submaterial( materials, mtlIdParent, subMtlId ); if(subMtl != NULL) { return subMtl; } /* ydnar: trying default submaterial */ subMtl = _ase_get_submaterial( materials, mtlIdParent, 0 ); if( subMtl != NULL ) { return subMtl; } _pico_printf( PICO_ERROR, "Could not find material/submaterial for id %d/%d\n", mtlIdParent, subMtlId ); return NULL; } static aseMaterial_t* _ase_add_material( aseMaterial_t **list, int mtlIdParent ) { aseMaterial_t *mtl = _pico_calloc( 1, sizeof( aseMaterial_t ) ); mtl->mtlId = mtlIdParent; mtl->subMtls = NULL; mtl->next = *list; *list = mtl; return mtl; } static aseSubMaterial_t* _ase_add_submaterial( aseMaterial_t **list, int mtlIdParent, int subMtlId, picoShader_t* shader ) { aseMaterial_t *parent = _ase_get_material( *list, mtlIdParent ); aseSubMaterial_t *subMtl = _pico_calloc( 1, sizeof ( aseSubMaterial_t ) ); /* Initialise some values */ subMtl->uOffset = 0.0f; subMtl->vOffset = 0.0f; subMtl->uScale = 1.0f; subMtl->vScale = 1.0f; subMtl->uvAngle = 0.0f; if ( !parent ) { parent = _ase_add_material ( list , mtlIdParent ); } subMtl->shader = shader; subMtl->subMtlId = subMtlId; subMtl->next = parent->subMtls; parent->subMtls = subMtl; return subMtl; } static void _ase_free_materials( aseMaterial_t **list ) { aseMaterial_t* mtl = *list; aseSubMaterial_t* subMtl = NULL; aseMaterial_t* mtlTemp = NULL; aseSubMaterial_t* subMtlTemp = NULL; while ( mtl ) { subMtl = mtl->subMtls; while ( subMtl ) { subMtlTemp = subMtl->next; _pico_free ( subMtl ); subMtl = subMtlTemp; } mtlTemp = mtl->next; _pico_free ( mtl ); mtl = mtlTemp; } (*list) = NULL; } #ifdef DEBUG_PM_ASE static void _ase_print_materials( aseMaterial_t *list ) { aseMaterial_t* mtl = list; aseSubMaterial_t* subMtl = NULL; while ( mtl ) { _pico_printf ( PICO_NORMAL , "ASE Material %i" , mtl->mtlId ); subMtl = mtl->subMtls; while ( subMtl ) { _pico_printf ( PICO_NORMAL , " -- ASE SubMaterial %i - %s\n" , subMtl->subMtlId , subMtl->shader->name ); subMtl = subMtl->next; } mtl = mtl->next; } } #endif //DEBUG_PM_ASE /* todo: * - apply material specific uv offsets to uv coordinates */ /* _ase_canload: * validates a 3dsmax ase model file. */ static int _ase_canload( PM_PARAMS_CANLOAD ) { picoParser_t *p; /* quick data length validation */ if( bufSize < 80 ) return PICO_PMV_ERROR_SIZE; /* keep the friggin compiler happy */ *fileName = *fileName; /* create pico parser */ p = _pico_new_parser( (picoByte_t*) buffer, bufSize ); if( p == NULL ) return PICO_PMV_ERROR_MEMORY; /* get first token */ if( _pico_parse_first( p ) == NULL) { return PICO_PMV_ERROR_IDENT; } /* check first token */ if( _pico_stricmp( p->token, "*3dsmax_asciiexport" ) ) { _pico_free_parser( p ); return PICO_PMV_ERROR_IDENT; } /* free the pico parser object */ _pico_free_parser( p ); /* file seems to be a valid ase file */ return PICO_PMV_OK; } typedef struct aseVertex_s aseVertex_t; struct aseVertex_s { picoVec3_t xyz; picoVec3_t normal; picoIndex_t id; }; typedef struct aseTexCoord_s aseTexCoord_t; struct aseTexCoord_s { picoVec2_t texcoord; }; typedef struct aseColor_s aseColor_t; struct aseColor_s { picoColor_t color; }; typedef struct aseFace_s aseFace_t; struct aseFace_s { picoIndex_t indices[9]; picoIndex_t smoothingGroup; picoIndex_t materialId; picoIndex_t subMaterialId; }; typedef aseFace_t* aseFacesIter_t; picoSurface_t* PicoModelFindOrAddSurface( picoModel_t *model, picoShader_t* shader ) { /* see if a surface already has the shader */ int i = 0; for ( ; i < model->numSurfaces ; i++ ) { picoSurface_t* workSurface = model->surface[i]; if ( workSurface->shader == shader ) { return workSurface; } } /* no surface uses this shader yet, so create a new surface */ { /* create a new surface in the model for the unique shader */ picoSurface_t* workSurface = PicoNewSurface(model); if ( !workSurface ) { _pico_printf ( PICO_ERROR , "Could not allocate a new surface!\n" ); return 0; } /* do surface setup */ PicoSetSurfaceType( workSurface, PICO_TRIANGLES ); PicoSetSurfaceName( workSurface, shader->name ); PicoSetSurfaceShader( workSurface, shader ); return workSurface; } } /* _ase_submit_triangles - jhefty use the surface and the current face list to look up material/submaterial IDs and submit them to the model for proper processing The following still holds from ydnar's _ase_make_surface: indexes 0 1 2 = vert indexes indexes 3 4 5 = st indexes indexes 6 7 8 = color indexes (new) */ #if 0 typedef picoIndex_t* picoIndexIter_t; typedef struct aseUniqueIndices_s aseUniqueIndices_t; struct aseUniqueIndices_s { picoIndex_t* data; picoIndex_t* last; aseFace_t* faces; }; size_t aseUniqueIndices_size(aseUniqueIndices_t* self) { return self->last - self->data; } void aseUniqueIndices_reserve(aseUniqueIndices_t* self, picoIndex_t size) { self->data = self->last = (picoIndex_t*)_pico_calloc(size, sizeof(picoIndex_t)); } void aseUniqueIndices_clear(aseUniqueIndices_t* self) { _pico_free(self->data); } void aseUniqueIndices_pushBack(aseUniqueIndices_t* self, picoIndex_t index) { *self->last++ = index; } picoIndex_t aseFaces_getVertexIndex(aseFace_t* faces, picoIndex_t index) { return faces[index / 3].indices[index % 3]; } picoIndex_t aseFaces_getTexCoordIndex(aseFace_t* faces, picoIndex_t index) { return faces[index / 3].indices[(index % 3) + 3]; } picoIndex_t aseFaces_getColorIndex(aseFace_t* faces, picoIndex_t index) { return faces[index / 3].indices[(index % 3) + 6]; } int aseUniqueIndex_equal(aseFace_t* faces, picoIndex_t index, picoIndex_t other) { return aseFaces_getVertexIndex(faces, index) == aseFaces_getVertexIndex(faces, other) && aseFaces_getTexCoordIndex(faces, index) == aseFaces_getTexCoordIndex(faces, other) && aseFaces_getColorIndex(faces, index) == aseFaces_getColorIndex(faces, other); } picoIndex_t aseUniqueIndices_insertUniqueVertex(aseUniqueIndices_t* self, picoIndex_t index) { picoIndexIter_t i = self->data; for(; i != self->last; ++i) { picoIndex_t other = (picoIndex_t)(i - self->data); if(aseUniqueIndex_equal(self->faces, index, other)) { return other; } } aseUniqueIndices_pushBack(self, index); return (picoIndex_t)(aseUniqueIndices_size(self) - 1); } static void _ase_submit_triangles_unshared ( picoModel_t* model , aseMaterial_t* materials , aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces, int meshHasNormals ) { aseFacesIter_t i = faces, end = faces + numFaces; aseUniqueIndices_t indices; aseUniqueIndices_t remap; aseUniqueIndices_reserve(&indices, numFaces * 3); aseUniqueIndices_reserve(&remap, numFaces * 3); indices.faces = faces; for(; i != end; ++i) { /* look up the shader for the material/submaterial pair */ aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, (*i).materialId, (*i).subMaterialId ); if( subMtl == NULL ) { return; } { picoSurface_t* surface = PicoModelFindOrAddSurface(model, subMtl->shader); int j; /* we pull the data from the vertex, color and texcoord arrays using the face index data */ for ( j = 0 ; j < 3 ; j ++ ) { picoIndex_t index = (picoIndex_t)(((i - faces) * 3) + j); picoIndex_t size = (picoIndex_t)aseUniqueIndices_size(&indices); picoIndex_t unique = aseUniqueIndices_insertUniqueVertex(&indices, index); picoIndex_t numVertexes = PicoGetSurfaceNumVertexes(surface); picoIndex_t numIndexes = PicoGetSurfaceNumIndexes(surface); aseUniqueIndices_pushBack(&remap, numIndexes); PicoSetSurfaceIndex(surface, numIndexes, remap.data[unique]); if(unique == size) { PicoSetSurfaceXYZ(surface, numVertexes, vertices[(*i).indices[j]].xyz); PicoSetSurfaceNormal(surface, numVertexes, vertices[(*i).indices[j]].normal); PicoSetSurfaceST(surface, 0, numVertexes, texcoords[(*i).indices[j + 3]].texcoord); if ( (*i).indices[j + 6] >= 0 ) { PicoSetSurfaceColor(surface, 0, numVertexes, colors[(*i).indices[j + 6]].color); } else { PicoSetSurfaceColor(surface, 0, numVertexes, white); } PicoSetSurfaceSmoothingGroup(surface, numVertexes, (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup); } } } } aseUniqueIndices_clear(&indices); aseUniqueIndices_clear(&remap); } #endif static void _ase_submit_triangles( picoModel_t* model , aseMaterial_t* materials , aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces ) { aseFacesIter_t i = faces, end = faces + numFaces; for(; i != end; ++i) { /* look up the shader for the material/submaterial pair */ aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, (*i).materialId, (*i).subMaterialId ); if( subMtl == NULL ) { return; } { picoVec3_t* xyz[3]; picoVec3_t* normal[3]; picoVec2_t* stRef[3]; picoVec2_t st[3]; picoColor_t* color[3]; picoIndex_t smooth[3]; double u,v; double materialSin = sin(subMtl->uvAngle); double materialCos = cos(subMtl->uvAngle); int j; /* we pull the data from the vertex, color and texcoord arrays using the face index data */ for ( j = 0 ; j < 3 ; j ++ ) { xyz[j] = &vertices[(*i).indices[j]].xyz; normal[j] = &vertices[(*i).indices[j]].normal; /* Old code st[j] = &texcoords[(*i).indices[j + 3]].texcoord; */ /* greebo: Apply shift, scale and rotation */ /* Also check for NULL texcoords pointer, some models surfaces don't have any tverts */ u = texcoords != NULL ? texcoords[(*i).indices[j + 3]].texcoord[0] * subMtl->uScale + subMtl->uOffset : 0.0; v = texcoords != NULL ? texcoords[(*i).indices[j + 3]].texcoord[1] * subMtl->vScale + subMtl->vOffset : 0.0; st[j][0] = u * materialCos + v * materialSin; st[j][1] = u * -materialSin + v * materialCos; stRef[j] = &st[j]; if( colors != NULL && (*i).indices[j + 6] >= 0 ) { color[j] = &colors[(*i).indices[j + 6]].color; } else { color[j] = &white; } smooth[j] = (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup; /* don't merge vertices */ } /* submit the triangle to the model */ PicoAddTriangleToModel ( model , xyz , normal , 1 , stRef, 1 , color , subMtl->shader, smooth ); } } } static void shadername_convert(char* shaderName) { /* unix-style path separators */ char* s = shaderName; for(; *s != '\0'; ++s) { if(*s == '\\') { *s = '/'; } } } /* _ase_load: * loads a 3dsmax ase model file. */ static picoModel_t *_ase_load( PM_PARAMS_LOAD ) { picoModel_t *model; picoParser_t *p; char lastNodeName[ 1024 ]; aseVertex_t* vertices = NULL; aseTexCoord_t* texcoords = NULL; aseColor_t* colors = NULL; aseFace_t* faces = NULL; int numVertices = 0; int numFaces = 0; int numTextureVertices = 0; int numTextureVertexFaces = 0; int numColorVertices = 0; int numColorVertexFaces = 0; int vertexId = 0; aseMaterial_t* materials = NULL; #ifdef DEBUG_PM_ASE clock_t start, finish; double elapsed; start = clock(); #endif /* helper */ #define _ase_error_return(m) \ { \ _pico_printf( PICO_ERROR,"%s in ASE, line %d.",m,p->curLine); \ _pico_free_parser( p ); \ PicoFreeModel( model ); \ return NULL; \ } /* create a new pico parser */ p = _pico_new_parser( (picoByte_t *)buffer,bufSize ); if (p == NULL) return NULL; /* create a new pico model */ model = PicoNewModel(); if (model == NULL) { _pico_free_parser( p ); return NULL; } /* do model setup */ PicoSetModelFrameNum( model, frameNum ); PicoSetModelName( model, fileName ); PicoSetModelFileName( model, fileName ); /* initialize some stuff */ memset( lastNodeName,0,sizeof(lastNodeName) ); /* parse ase model file */ while( 1 ) { /* get first token on line */ if (_pico_parse_first( p ) == NULL) break; /* we just skip empty lines */ if (p->token == NULL || !strlen( p->token )) continue; /* we skip invalid ase statements */ if (p->token[0] != '*' && p->token[0] != '{' && p->token[0] != '}') { _pico_parse_skip_rest( p ); continue; } /* remember node name */ if (!_pico_stricmp(p->token,"*node_name")) { /* read node name */ char *ptr = _pico_parse( p,0 ); if (ptr == NULL) _ase_error_return("Node name parse error"); /* remember node name */ strncpy( lastNodeName,ptr,sizeof(lastNodeName) ); } /* model mesh (originally contained within geomobject) */ else if (!_pico_stricmp(p->token,"*mesh")) { /* finish existing surface */ _ase_submit_triangles(model, materials, vertices, texcoords, colors, faces, numFaces); _pico_free(faces); _pico_free(vertices); _pico_free(texcoords); _pico_free(colors); colors = NULL; /* OrbWeaver: reset all pointers to avoid double-free */ faces = NULL; vertices = NULL; texcoords = NULL; } else if (!_pico_stricmp(p->token,"*mesh_numvertex")) { if (!_pico_parse_int( p, &numVertices) ) _ase_error_return("Missing MESH_NUMVERTEX value"); vertices = _pico_calloc(numVertices, sizeof(aseVertex_t)); } else if (!_pico_stricmp(p->token,"*mesh_numfaces")) { if (!_pico_parse_int( p, &numFaces) ) _ase_error_return("Missing MESH_NUMFACES value"); faces = _pico_calloc(numFaces, sizeof(aseFace_t)); } else if (!_pico_stricmp(p->token,"*mesh_numtvertex")) { if (!_pico_parse_int( p, &numTextureVertices) ) _ase_error_return("Missing MESH_NUMTVERTEX value"); texcoords = _pico_calloc(numTextureVertices, sizeof(aseTexCoord_t)); } else if (!_pico_stricmp(p->token,"*mesh_numtvfaces")) { if (!_pico_parse_int( p, &numTextureVertexFaces) ) _ase_error_return("Missing MESH_NUMTVFACES value"); } else if (!_pico_stricmp(p->token,"*mesh_numcvertex")) { if (!_pico_parse_int( p, &numColorVertices) ) _ase_error_return("Missing MESH_NUMCVERTEX value"); colors = _pico_calloc(numColorVertices, sizeof(aseColor_t)); memset( colors, 255, numColorVertices * sizeof( aseColor_t ) ); /* ydnar: force colors to white initially */ } else if (!_pico_stricmp(p->token,"*mesh_numcvfaces")) { if (!_pico_parse_int( p, &numColorVertexFaces) ) _ase_error_return("Missing MESH_NUMCVFACES value"); } /* mesh material reference. this usually comes at the end of */ /* geomobjects after the mesh blocks. we must assume that the */ /* new mesh was already created so all we can do here is assign */ /* the material reference id (shader index) now. */ else if (!_pico_stricmp(p->token,"*material_ref")) { int mtlId; /* get the material ref (0..n) */ if (!_pico_parse_int( p,&mtlId) ) _ase_error_return("Missing material reference ID"); { int i = 0; /* fix up all of the aseFaceList in the surface to point to the parent material */ /* we've already saved off their subMtl */ for(; i < numFaces; ++i) { faces[i].materialId = mtlId; } } } /* model mesh vertex */ else if (!_pico_stricmp(p->token,"*mesh_vertex")) { int index; if( numVertices == 0 ) _ase_error_return("Vertex parse error"); /* get vertex data (orig: index +y -x +z) */ if (!_pico_parse_int( p,&index )) _ase_error_return("Vertex parse error"); if (!_pico_parse_vec( p,vertices[index].xyz )) _ase_error_return("Vertex parse error"); if (index >= numVertices) { _ase_error_return("Error: MESH_VERTEX index out of bounds (is >= MESH_NUMVERTEX)"); } vertices[index].id = vertexId++; } /* model mesh vertex normal */ else if (!_pico_stricmp(p->token,"*mesh_vertexnormal")) { int index; if( numVertices == 0 ) _ase_error_return("Vertex parse error"); /* get vertex data (orig: index +y -x +z) */ if (!_pico_parse_int( p,&index )) _ase_error_return("Vertex parse error"); if (index >= numVertices) { _ase_error_return("Error: MESH_VERTEXNORMAL index out of bounds (is >= MESH_NUMVERTEX)"); } if (!_pico_parse_vec( p,vertices[index].normal )) _ase_error_return("Vertex parse error"); } /* model mesh face */ else if (!_pico_stricmp(p->token,"*mesh_face")) { picoIndex_t indexes[3]; int index; if( numFaces == 0 ) _ase_error_return("Face parse error"); /* get face index */ if (!_pico_parse_int( p,&index )) _ase_error_return("Face parse error"); if (index >= numFaces) { _ase_error_return("Error: MESH_FACE index out of bounds (is >= MESH_NUMFACES)"); } /* get 1st vertex index */ _pico_parse( p,0 ); if (!_pico_parse_int( p,&indexes[0] )) _ase_error_return("Face parse error"); if (indexes[0] >= numVertices) { _ase_error_return("Error: MESH_FACE: vertex index 0 out of bounds (is >= MESH_NUMVERTEX)"); } /* get 2nd vertex index */ _pico_parse( p,0 ); if (!_pico_parse_int( p,&indexes[1] )) _ase_error_return("Face parse error"); if (indexes[1] >= numVertices) { _ase_error_return("Error: MESH_FACE: vertex index 1 out of bounds (is >= MESH_NUMVERTEX)"); } /* get 3rd vertex index */ _pico_parse( p,0 ); if (!_pico_parse_int( p,&indexes[2] )) _ase_error_return("Face parse error"); if (indexes[2] >= numVertices) { _ase_error_return("Error: MESH_FACE: vertex index 2 out of bounds (is >= MESH_NUMVERTEX)"); } /* parse to the subMaterial ID */ while ( 1 ) { if (!_pico_parse (p,0)) /* EOL */ { break; } if (!_pico_stricmp (p->token,"*MESH_SMOOTHING" )) { _pico_parse_int ( p , &faces[index].smoothingGroup ); } if (!_pico_stricmp (p->token,"*MESH_MTLID" )) { _pico_parse_int ( p , &faces[index].subMaterialId ); } } faces[index].materialId = 0; faces[index].indices[0] = indexes[2]; faces[index].indices[1] = indexes[1]; faces[index].indices[2] = indexes[0]; } /* model texture vertex */ else if (!_pico_stricmp(p->token,"*mesh_tvert")) { int index; if( numVertices == 0 ) _ase_error_return("Texture Vertex parse error"); /* get uv vertex index */ if (!_pico_parse_int( p,&index ) || index >= numTextureVertices) _ase_error_return("Texture vertex parse error"); /* get uv vertex s */ if (!_pico_parse_double( p,&texcoords[index].texcoord[0] )) _ase_error_return("Texture vertex parse error"); /* get uv vertex t */ if (!_pico_parse_double( p,&texcoords[index].texcoord[1] )) _ase_error_return("Texture vertex parse error"); /* ydnar: invert t */ texcoords[index].texcoord[ 1 ] = 1.0f - texcoords[index].texcoord[ 1 ]; } /* ydnar: model mesh texture face */ else if( !_pico_stricmp( p->token, "*mesh_tface" ) ) { picoIndex_t indexes[3]; int index; if( numFaces == 0 ) _ase_error_return("Texture face parse error"); /* get face index */ if (!_pico_parse_int( p,&index )) _ase_error_return("Texture face parse error"); if (index >= numFaces) { _ase_error_return("Error: MESH_TFACE index out of bounds (is >= MESH_NUMFACES)"); } /* get 1st vertex index */ if (!_pico_parse_int( p,&indexes[0] )) _ase_error_return("Texture face parse error"); /* get 2nd vertex index */ if (!_pico_parse_int( p,&indexes[1] )) _ase_error_return("Texture face parse error"); /* get 3rd vertex index */ if (!_pico_parse_int( p,&indexes[2] )) _ase_error_return("Texture face parse error"); faces[index].indices[3] = indexes[2]; faces[index].indices[4] = indexes[1]; faces[index].indices[5] = indexes[0]; } /* model color vertex */ else if (!_pico_stricmp(p->token,"*mesh_vertcol")) { int index; float colorInput; if( numVertices == 0 ) _ase_error_return("Color Vertex parse error"); /* get color vertex index */ if (!_pico_parse_int( p,&index )) _ase_error_return("Color vertex parse error"); if (index >= numColorVertices) { _ase_error_return("Error: MESH_VERTCOL index out of bounds (is >= MESH_NUMCVERTEX)"); } /* get R component */ if (!_pico_parse_float( p,&colorInput )) _ase_error_return("Color vertex parse error"); colors[index].color[0] = (picoByte_t)(colorInput * 255); /* get G component */ if (!_pico_parse_float( p,&colorInput )) _ase_error_return("Color vertex parse error"); colors[index].color[1] = (picoByte_t)(colorInput * 255); /* get B component */ if (!_pico_parse_float( p,&colorInput )) _ase_error_return("Color vertex parse error"); colors[index].color[2] = (picoByte_t)(colorInput * 255); /* leave alpha alone since we don't get any data from the ASE format */ colors[index].color[3] = 255; } /* model color face */ else if (!_pico_stricmp(p->token,"*mesh_cface")) { picoIndex_t indexes[3]; int index; if( numFaces == 0 ) _ase_error_return("Face parse error"); /* get face index */ if (!_pico_parse_int( p,&index )) _ase_error_return("Face parse error"); if (index >= numFaces) { _ase_error_return("Error: MESH_CFACE index out of bounds (is >= MESH_NUMFACES)"); } /* get 1st cvertex index */ // _pico_parse( p,0 ); if (!_pico_parse_int( p,&indexes[0] )) _ase_error_return("Face parse error"); /* get 2nd cvertex index */ // _pico_parse( p,0 ); if (!_pico_parse_int( p,&indexes[1] )) _ase_error_return("Face parse error"); /* get 3rd cvertex index */ // _pico_parse( p,0 ); if (!_pico_parse_int( p,&indexes[2] )) _ase_error_return("Face parse error"); faces[index].indices[6] = indexes[2]; faces[index].indices[7] = indexes[1]; faces[index].indices[8] = indexes[0]; } /* model material */ else if( !_pico_stricmp( p->token, "*material" ) ) { aseSubMaterial_t* subMaterial = NULL; picoShader_t *shader = NULL; int level = 1, index; char materialName[ 1024 ]; float transValue = 0.0f, shineValue = 1.0f; picoColor_t ambientColor, diffuseColor, specularColor; char *mapname = NULL; int subMtlId = -1, subMaterialLevel = -1; float uOffset = 0.0f; float vOffset = 0.0f; float uScale = 1.0f; float vScale = 1.0f; float uvAngle = 0; /* get material index */ _pico_parse_int( p,&index ); /* check brace */ if (!_pico_parse_check(p,1,"{")) _ase_error_return("Material missing opening brace"); /* parse material block */ while( 1 ) { /* get next token */ if (_pico_parse(p,1) == NULL) break; if (!strlen(p->token)) continue; /* handle levels */ if (p->token[0] == '{') level++; if (p->token[0] == '}') level--; if (!level) break; if( level == subMaterialLevel ) { /* set material name */ _pico_first_token( materialName ); shadername_convert(materialName); PicoSetShaderName( shader, materialName); /* set shader's transparency */ PicoSetShaderTransparency( shader,transValue ); /* set shader's ambient color */ PicoSetShaderAmbientColor( shader,ambientColor ); /* set diffuse alpha to transparency */ diffuseColor[3] = (picoByte_t)( transValue * 255.0 ); /* set shader's diffuse color */ PicoSetShaderDiffuseColor( shader,diffuseColor ); /* set shader's specular color */ PicoSetShaderSpecularColor( shader,specularColor ); /* set shader's shininess */ PicoSetShaderShininess( shader,shineValue ); /* set material map name */ PicoSetShaderMapName( shader, mapname ); subMaterial = _ase_add_submaterial( &materials, index, subMtlId, shader ); // Assign offsets subMaterial->uOffset = uOffset; subMaterial->vOffset = vOffset; subMaterial->uScale = uScale; subMaterial->vScale = vScale; subMaterial->uvAngle = uvAngle; subMaterialLevel = -1; } /* parse submaterial index */ if (!_pico_stricmp(p->token,"*submaterial")) { /* allocate new pico shader */ _pico_parse_int( p , &subMtlId ); shader = PicoNewShader( model ); if (shader == NULL) { PicoFreeModel( model ); return NULL; } subMaterialLevel = level; } /* parse material name */ else if (!_pico_stricmp(p->token,"*material_name")) { char* name = _pico_parse(p,0); if ( name == NULL) _ase_error_return("Missing material name"); strcpy ( materialName , name ); /* skip rest and continue with next token */ _pico_parse_skip_rest( p ); continue; } /* parse material transparency */ else if (!_pico_stricmp(p->token,"*material_transparency")) { /* get transparency value from ase */ if (!_pico_parse_float( p,&transValue )) _ase_error_return("Material transparency parse error"); /* skip rest and continue with next token */ _pico_parse_skip_rest( p ); continue; } /* parse material shininess */ else if (!_pico_stricmp(p->token,"*material_shine")) { /* remark: * - not sure but instead of '*material_shine' i might * need to use '*material_shinestrength' */ /* get shine value from ase */ if (!_pico_parse_float( p,&shineValue )) _ase_error_return("Material shine parse error"); /* scale ase shine range 0..1 to pico range 0..127 */ shineValue *= 128.0; /* skip rest and continue with next token */ _pico_parse_skip_rest( p ); continue; } /* parse ambient material color */ else if (!_pico_stricmp(p->token,"*material_ambient")) { picoVec3_t vec; /* get r,g,b float values from ase */ if (!_pico_parse_vec( p,vec )) _ase_error_return("Material color parse error"); /* setup 0..255 range color values */ ambientColor[ 0 ] = (int)( vec[ 0 ] * 255.0 ); ambientColor[ 1 ] = (int)( vec[ 1 ] * 255.0 ); ambientColor[ 2 ] = (int)( vec[ 2 ] * 255.0 ); ambientColor[ 3 ] = (int)( 255 ); /* skip rest and continue with next token */ _pico_parse_skip_rest( p ); continue; } /* parse diffuse material color */ else if (!_pico_stricmp(p->token,"*material_diffuse")) { picoVec3_t vec; /* get r,g,b float values from ase */ if (!_pico_parse_vec( p,vec )) _ase_error_return("Material color parse error"); /* setup 0..255 range color */ diffuseColor[ 0 ] = (int)( vec[ 0 ] * 255.0 ); diffuseColor[ 1 ] = (int)( vec[ 1 ] * 255.0 ); diffuseColor[ 2 ] = (int)( vec[ 2 ] * 255.0 ); diffuseColor[ 3 ] = (int)( 255 ); /* skip rest and continue with next token */ _pico_parse_skip_rest( p ); continue; } /* parse specular material color */ else if (!_pico_stricmp(p->token,"*material_specular")) { picoVec3_t vec; /* get r,g,b float values from ase */ if (!_pico_parse_vec( p,vec )) _ase_error_return("Material color parse error"); /* setup 0..255 range color */ specularColor[ 0 ] = (int)( vec[ 0 ] * 255 ); specularColor[ 1 ] = (int)( vec[ 1 ] * 255 ); specularColor[ 2 ] = (int)( vec[ 2 ] * 255 ); specularColor[ 3 ] = (int)( 255 ); /* skip rest and continue with next token */ _pico_parse_skip_rest( p ); continue; } /* material diffuse map */ else if (!_pico_stricmp(p->token,"*map_diffuse") ) { int sublevel = 0; /* parse material block */ while( 1 ) { /* get next token */ if (_pico_parse(p,1) == NULL) break; if (!strlen(p->token)) continue; /* handle levels */ if (p->token[0] == '{') sublevel++; if (p->token[0] == '}') sublevel--; if (!sublevel) break; /* parse diffuse map bitmap */ if (!_pico_stricmp(p->token,"*bitmap")) { char* name = _pico_parse(p,0); if (name == NULL) _ase_error_return("Missing material map bitmap name"); mapname = _pico_alloc ( strlen ( name ) + 1 ); strcpy ( mapname, name ); /* skip rest and continue with next token */ _pico_parse_skip_rest( p ); continue; } if (!_pico_stricmp(p->token, "*uvw_u_offset")) { if (!_pico_parse_float(p, &uOffset)) _ase_error_return("Material UVW_U_OFFSET error"); // Negate the u offset value uOffset = -uOffset; /* skip rest and continue with next token */ _pico_parse_skip_rest(p); continue; } if (!_pico_stricmp(p->token, "*uvw_v_offset")) { if (!_pico_parse_float(p, &vOffset)) _ase_error_return("Material UVW_V_OFFSET error"); /* skip rest and continue with next token */ _pico_parse_skip_rest(p); continue; } if (!_pico_stricmp(p->token, "*uvw_u_tiling")) { if (!_pico_parse_float(p, &uScale)) _ase_error_return("Material UVW_U_TILING error"); /* skip rest and continue with next token */ _pico_parse_skip_rest(p); continue; } if (!_pico_stricmp(p->token, "*uvw_v_tiling")) { if (!_pico_parse_float(p, &vScale)) _ase_error_return("Material UVW_V_TILING error"); /* skip rest and continue with next token */ _pico_parse_skip_rest(p); continue; } if (!_pico_stricmp(p->token, "*uvw_angle")) { if (!_pico_parse_float(p, &uvAngle)) _ase_error_return("Material UVW_ANGLE error"); /* skip rest and continue with next token */ _pico_parse_skip_rest(p); continue; } } } /* end map_diffuse block */ } /* end material block */ if( subMaterial == NULL ) { /* allocate new pico shader */ shader = PicoNewShader( model ); if (shader == NULL) { PicoFreeModel( model ); return NULL; } /* set material name */ shadername_convert(materialName); PicoSetShaderName( shader,materialName ); /* set shader's transparency */ PicoSetShaderTransparency( shader,transValue ); /* set shader's ambient color */ PicoSetShaderAmbientColor( shader,ambientColor ); /* set diffuse alpha to transparency */ diffuseColor[3] = (picoByte_t)( transValue * 255.0 ); /* set shader's diffuse color */ PicoSetShaderDiffuseColor( shader,diffuseColor ); /* set shader's specular color */ PicoSetShaderSpecularColor( shader,specularColor ); /* set shader's shininess */ PicoSetShaderShininess( shader,shineValue ); /* set material map name */ PicoSetShaderMapName( shader, mapname ); /* extract shadername from bitmap path */ if(mapname != NULL) { char* p = mapname; /* convert to shader-name format */ shadername_convert(mapname); { /* remove extension */ char* last_period = strrchr(p, '.'); if (last_period != NULL) { *last_period = '\0'; } } /* find game root */ for(; *p != '\0'; ++p) { if (_pico_strnicmp(p, "quake", 5) == 0 || _pico_strnicmp(p, "doom", 4) == 0) { break; } } /* root-relative */ for (; *p != '\0'; ++p) { if (*p == '/') { ++p; break; } } /* game-relative */ for (; *p != '\0'; ++p) { if (*p == '/') { ++p; break; } } if (*p != '\0') { /* set material name */ PicoSetShaderName(shader, p); } } /* this is just a material with 1 submaterial */ subMaterial = _ase_add_submaterial( &materials, index, 0, shader ); // Assign offsets subMaterial->uOffset = uOffset; subMaterial->vOffset = vOffset; subMaterial->uScale = uScale; subMaterial->vScale = vScale; subMaterial->uvAngle = uvAngle; } /* ydnar: free mapname */ if( mapname != NULL ) _pico_free( mapname ); } // !_pico_stricmp ( "*material" ) /* skip unparsed rest of line and continue */ _pico_parse_skip_rest( p ); } /* ydnar: finish existing surface */ _ase_submit_triangles(model, materials, vertices, texcoords, colors, faces, numFaces); _pico_free(faces); _pico_free(vertices); _pico_free(texcoords); _pico_free(colors); #ifdef DEBUG_PM_ASE _ase_print_materials(materials); finish = clock(); elapsed = (double)(finish - start) / CLOCKS_PER_SEC; _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s)\n", elapsed ); #endif //DEBUG_PM_ASE _ase_free_materials(&materials); _pico_free_parser( p ); /* return allocated pico model */ return model; } /* pico file format module definition */ const picoModule_t picoModuleASE = { "1.0", /* module version string */ "Autodesk 3DSMAX ASCII", /* module display name */ "Jared Hefty, seaw0lf", /* author's name */ "2003 Jared Hefty, 2002 seaw0lf", /* module copyright */ { "ase",NULL,NULL,NULL /* default extensions to use */ }, _ase_canload, /* validation routine */ _ase_load, /* load routine */ NULL, /* save validation routine */ NULL /* save routine */ }; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/pm_fm.c������������������������������������������������������������0000664�0000000�0000000�00000047156�13217505464�0020074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* Nurail: Used pm_md3.c (Randy Reddig) as a template. */ /* marker */ #define PM_FM_C /* dependencies */ #include "pm_fm.h" //#define FM_VERBOSE_DBG 0 #undef FM_VERBOSE_DBG #undef FM_DBG typedef struct index_LUT_s { short Vert; short ST; struct index_LUT_s *next; } index_LUT_t; typedef struct index_DUP_LUT_s { short ST; short OldVert; } index_DUP_LUT_t; // _fm_canload() static int _fm_canload( PM_PARAMS_CANLOAD ) { fm_t fm; unsigned char *bb; int fm_file_pos; bb = (unsigned char *) buffer; // Header fm.fm_header_hdr = (fm_chunk_header_t *) bb; fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size; #ifdef FM_VERBOSE_DBG _pico_printf( PICO_VERBOSE, "IDENT: %s\n", (unsigned char *) fm.fm_header_hdr->ident ); #endif if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME)) ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n"); #endif return PICO_PMV_ERROR_IDENT; } // check fm if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM Header Version incorrect\n"); #endif return PICO_PMV_ERROR_VERSION; } // Skin fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size; #ifdef FM_VERBOSE_DBG _pico_printf( PICO_VERBOSE, "SKIN: %s\n", (unsigned char *) fm.fm_skin_hdr->ident ); #endif if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n"); #endif return PICO_PMV_ERROR_IDENT; } // check fm if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n"); #endif return PICO_PMV_ERROR_VERSION; } // st fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size; #ifdef FM_VERBOSE_DBG _pico_printf( PICO_VERBOSE, "ST: %s\n", (unsigned char *) fm.fm_st_hdr->ident ); #endif if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n"); #endif return PICO_PMV_ERROR_IDENT; } // check fm if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM ST Version incorrect\n"); #endif return PICO_PMV_ERROR_VERSION; } // tri fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size; #ifdef FM_VERBOSE_DBG _pico_printf( PICO_VERBOSE, "TRI: %s\n", (unsigned char *) fm.fm_tri_hdr->ident ); #endif if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n"); #endif return PICO_PMV_ERROR_IDENT; } // check fm if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n"); #endif return PICO_PMV_ERROR_VERSION; } // frame fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); fm_file_pos += sizeof(fm_chunk_header_t); #ifdef FM_VERBOSE_DBG _pico_printf( PICO_VERBOSE, "FRAME: %s\n", (unsigned char *) fm.fm_frame_hdr->ident ); #endif if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n"); #endif return PICO_PMV_ERROR_IDENT; } // check fm if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER ) { #ifdef FM_DBG _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n"); #endif return PICO_PMV_ERROR_VERSION; } // file seems to be a valid fm return PICO_PMV_OK; } // _fm_load() loads a Heretic 2 model file. static picoModel_t *_fm_load( PM_PARAMS_LOAD ) { int i, j, dups, dup_index; int fm_file_pos; /*short tot_numVerts; -- silence compiler warning */ index_LUT_t *p_index_LUT, *p_index_LUT2, *p_index_LUT3; index_DUP_LUT_t *p_index_LUT_DUPS; fm_vert_normal_t *vert; char skinname[FM_SKINPATHSIZE]; fm_t fm; fm_header_t *fm_head; fm_st_t *texCoord; fm_xyz_st_t *tri_verts; fm_xyz_st_t *triangle; fm_frame_t *frame; picoByte_t *bb; picoModel_t *picoModel; picoSurface_t *picoSurface; picoShader_t *picoShader; picoVec3_t xyz, normal; picoVec2_t st; picoColor_t color; bb = (picoByte_t*) buffer; // Header Header fm.fm_header_hdr = (fm_chunk_header_t *) bb; fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size; if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME)) ) { _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n"); return NULL; } if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER ) { _pico_printf( PICO_WARNING, "FM Header Version incorrect\n"); return NULL; } // Skin Header fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size; if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) ) { _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n"); return NULL; } if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER ) { _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n"); return NULL; } // ST Header fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size; if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) ) { _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n"); return NULL; } if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER ) { _pico_printf( PICO_WARNING, "FM ST Version incorrect\n"); return NULL; } // Tris Header fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size; if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) ) { _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n"); return NULL; } if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER ) { _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n"); return NULL; } // Frame Header fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); fm_file_pos += sizeof(fm_chunk_header_t); if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) ) { _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n"); return NULL; } if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER ) { _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n"); return NULL; } // Header fm_file_pos = sizeof(fm_chunk_header_t); fm_head = fm.fm_header = (fm_header_t *) (bb + fm_file_pos); fm_file_pos += fm.fm_header_hdr->size; // Skin fm_file_pos += sizeof(fm_chunk_header_t); fm.fm_skin = (fm_skinpath_t *) (bb + fm_file_pos); fm_file_pos += fm.fm_skin_hdr->size; // ST fm_file_pos += sizeof(fm_chunk_header_t); texCoord = fm.fm_st = (fm_st_t *) (bb + fm_file_pos); fm_file_pos += fm.fm_st_hdr->size; // Tri fm_file_pos += sizeof(fm_chunk_header_t); tri_verts = fm.fm_tri = (fm_xyz_st_t *) (bb + fm_file_pos); fm_file_pos += fm.fm_tri_hdr->size; // Frame fm_file_pos += sizeof(fm_chunk_header_t); frame = fm.fm_frame = (fm_frame_t *) (bb + fm_file_pos); // do frame check if( fm_head->numFrames < 1 ) { _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName ); return NULL; } if( frameNum < 0 || frameNum >= fm_head->numFrames ) { _pico_printf( PICO_ERROR, "Invalid or out-of-range FM frame specified" ); return NULL; } // swap fm fm_head->skinWidth = _pico_little_long( fm_head->skinWidth ); fm_head->skinHeight = _pico_little_long( fm_head->skinHeight ); fm_head->frameSize = _pico_little_long( fm_head->frameSize ); fm_head->numSkins = _pico_little_long( fm_head->numSkins ); fm_head->numXYZ = _pico_little_long( fm_head->numXYZ ); fm_head->numST = _pico_little_long( fm_head->numST ); fm_head->numTris = _pico_little_long( fm_head->numTris ); fm_head->numGLCmds = _pico_little_long( fm_head->numGLCmds ); fm_head->numFrames = _pico_little_long( fm_head->numFrames ); // swap frame scale and translation for( i = 0; i < 3; i++ ) { frame->header.scale[ i ] = _pico_little_float( frame->header.scale[ i ] ); frame->header.translate[ i ] = _pico_little_float( frame->header.translate[ i ] ); } // swap triangles triangle = tri_verts; for( i = 0; i < fm_head->numTris; i++, triangle++ ) { for( j = 0; j < 3; j++ ) { triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] ); triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] ); } } // swap st coords for( i = 0; i < fm_head->numST; i++ ) { texCoord->s = _pico_little_short( texCoord[i].s ); texCoord->t = _pico_little_short( texCoord[i].t ); } // set Skin Name strncpy(skinname, fm.fm_skin->path, FM_SKINPATHSIZE ); #ifdef FM_VERBOSE_DBG // Print out md2 values _pico_printf(PICO_VERBOSE,"numSkins->%d numXYZ->%d numST->%d numTris->%d numFrames->%d\nSkin Name \"%s\"\n", fm_head->numSkins, fm_head->numXYZ, fm_head->numST, fm_head->numTris, fm_head->numFrames, &skinname ); #endif // detox Skin name _pico_setfext( skinname, "" ); _pico_unixify( skinname ); /* create new pico model */ picoModel = PicoNewModel(); if( picoModel == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); return NULL; } /* do model setup */ PicoSetModelFrameNum( picoModel, frameNum ); PicoSetModelNumFrames( picoModel, fm_head->numFrames ); /* sea */ PicoSetModelName( picoModel, fileName ); PicoSetModelFileName( picoModel, fileName ); // allocate new pico surface picoSurface = PicoNewSurface( picoModel ); if( picoSurface == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); PicoFreeModel( picoModel ); return NULL; } PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); PicoSetSurfaceName( picoSurface, frame->header.name ); picoShader = PicoNewShader( picoModel ); if( picoShader == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); PicoFreeModel( picoModel ); return NULL; } PicoSetShaderName( picoShader, skinname ); // associate current surface with newly created shader PicoSetSurfaceShader( picoSurface, picoShader ); // Init LUT for Verts p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * fm_head->numXYZ); for(i=0; inumXYZ; i++) { p_index_LUT[i].Vert = -1; p_index_LUT[i].ST = -1; p_index_LUT[i].next = NULL; } // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert. /* tot_numVerts = fm_head->numXYZ; -- silence compiler warning */ dups = 0; triangle = tri_verts; for(i=0; inumTris; i++) { for(j=0; j<3; j++) { if (p_index_LUT[triangle->index_xyz[j]].ST == -1) // No Main Entry p_index_LUT[triangle->index_xyz[j]].ST = triangle->index_st[j]; else if (triangle->index_st[j] == p_index_LUT[triangle->index_xyz[j]].ST ) // Equal to Main Entry { #ifdef FM_VERBOSE_DBG _pico_printf( PICO_NORMAL, "-> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]); #endif continue; } else if ( p_index_LUT[triangle->index_xyz[j]].next == NULL ) // Not equal to Main entry, and no LL entry { // Add first entry of LL from Main p_index_LUT2 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t)); if (p_index_LUT2 == NULL) _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n"); p_index_LUT[triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2; p_index_LUT2->Vert = dups; p_index_LUT2->ST = triangle->index_st[j]; p_index_LUT2->next = NULL; #ifdef FM_VERBOSE_DBG _pico_printf( PICO_NORMAL, " ADDING first LL XYZ:%d DUP:%d ST:%d\n", triangle->index_xyz[j], dups, triangle->index_st[j]); #endif triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk dups++; } else // Try to find in LL from Main Entry { p_index_LUT3 = p_index_LUT2 = p_index_LUT[triangle->index_xyz[j]].next; while ( (p_index_LUT2 != NULL) && (triangle->index_xyz[j] != p_index_LUT2->Vert) ) // Walk down LL { p_index_LUT3 = p_index_LUT2; p_index_LUT2 = p_index_LUT2->next; } p_index_LUT2 = p_index_LUT3; if ( triangle->index_st[j] == p_index_LUT2->ST ) // Found it { triangle->index_xyz[j] = p_index_LUT2->Vert + fm_head->numXYZ; // Make change in Tri hunk #ifdef FM_VERBOSE_DBG _pico_printf( PICO_NORMAL, "--> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]); #endif continue; } if ( p_index_LUT2->next == NULL) // Didn't find it. Add entry to LL. { // Add the Entry p_index_LUT3 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t)); if (p_index_LUT3 == NULL) _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n"); p_index_LUT2->next = (index_LUT_t *)p_index_LUT3; p_index_LUT3->Vert = dups; p_index_LUT3->ST = triangle->index_st[j]; p_index_LUT3->next = NULL; #ifdef FM_VERBOSE_DBG _pico_printf( PICO_NORMAL, " ADDING additional LL XYZ:%d DUP:%d NewXYZ:%d ST:%d\n", triangle->index_xyz[j], dups, dups + (fm_head->numXYZ), triangle->index_st[j]); #endif triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk dups++; } } #ifdef FM_VERBOSE_DBG _pico_printf( PICO_NORMAL, "---> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]); #endif } triangle++; } // malloc and build array for Dup STs p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc(sizeof(index_DUP_LUT_t) * dups); if (p_index_LUT_DUPS == NULL) _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n"); dup_index = 0; for(i=0; inumXYZ; i++) { p_index_LUT2 = p_index_LUT[i].next; while (p_index_LUT2 != NULL) { p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i; p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST; dup_index++; p_index_LUT2 = p_index_LUT2->next; } } #ifdef FM_VERBOSE_DBG _pico_printf( PICO_NORMAL, " Dups = %d\n", dups); _pico_printf( PICO_NORMAL, " Dup Index = %d\n", dup_index); #endif for(i=0; inumXYZ; i++) { #ifdef FM_VERBOSE_DBG _pico_printf( PICO_NORMAL, "Vert: %4d\t%4d",i, p_index_LUT[i].ST); #endif if (p_index_LUT[i].next != NULL) { p_index_LUT2 = p_index_LUT[i].next; do { #ifdef FM_VERBOSE_DBG _pico_printf( PICO_NORMAL, " %4d %4d", p_index_LUT2->Vert, p_index_LUT2->ST); #endif p_index_LUT2 = p_index_LUT2->next; } while ( p_index_LUT2 != NULL); } #ifdef FM_VERBOSE_DBG _pico_printf( PICO_NORMAL, "\n"); #endif } #ifdef FM_VERBOSE_DBG for(i=0; inumTris; i++) { for(j=0; j<3; j++) _pico_printf( PICO_NORMAL, "Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]); _pico_printf( PICO_NORMAL, "\n"); triangle++; } #endif // Build Picomodel triangle = tri_verts; for( j = 0; j < fm_head->numTris; j++, triangle++ ) { PicoSetSurfaceIndex( picoSurface, j*3 , triangle->index_xyz[0] ); PicoSetSurfaceIndex( picoSurface, j*3+1 , triangle->index_xyz[1] ); PicoSetSurfaceIndex( picoSurface, j*3+2 , triangle->index_xyz[2] ); } vert = (fm_vert_normal_t*) ((picoByte_t*) (frame->verts) ); for(i=0; i< fm_head->numXYZ; i++, vert++) { /* set vertex origin */ xyz[ 0 ] = vert->v[0] * frame->header.scale[0] + frame->header.translate[0]; xyz[ 1 ] = vert->v[1] * frame->header.scale[1] + frame->header.translate[1]; xyz[ 2 ] = vert->v[2] * frame->header.scale[2] + frame->header.translate[2]; PicoSetSurfaceXYZ( picoSurface, i , xyz ); /* set normal */ normal[ 0 ] = fm_normals[vert->lightnormalindex][0]; normal[ 1 ] = fm_normals[vert->lightnormalindex][1]; normal[ 2 ] = fm_normals[vert->lightnormalindex][2]; PicoSetSurfaceNormal( picoSurface, i , normal ); /* set st coords */ st[ 0 ] = ((texCoord[p_index_LUT[i].ST].s) / ((float)fm_head->skinWidth)); st[ 1 ] = (texCoord[p_index_LUT[i].ST].t / ((float)fm_head->skinHeight)); PicoSetSurfaceST( picoSurface, 0, i , st ); } if (dups) { for(i=0; iverts[j].v[0] * frame->header.scale[0] + frame->header.translate[0]; xyz[ 1 ] = frame->verts[j].v[1] * frame->header.scale[1] + frame->header.translate[1]; xyz[ 2 ] = frame->verts[j].v[2] * frame->header.scale[2] + frame->header.translate[2]; PicoSetSurfaceXYZ( picoSurface, i + fm_head->numXYZ , xyz ); /* set normal */ normal[ 0 ] = fm_normals[frame->verts[j].lightnormalindex][0]; normal[ 1 ] = fm_normals[frame->verts[j].lightnormalindex][1]; normal[ 2 ] = fm_normals[frame->verts[j].lightnormalindex][2]; PicoSetSurfaceNormal( picoSurface, i + fm_head->numXYZ , normal ); /* set st coords */ st[ 0 ] = ((texCoord[p_index_LUT_DUPS[i].ST].s) / ((float)fm_head->skinWidth)); st[ 1 ] = (texCoord[p_index_LUT_DUPS[i].ST].t / ((float)fm_head->skinHeight)); PicoSetSurfaceST( picoSurface, 0, i + fm_head->numXYZ , st ); } } /* set color */ PicoSetSurfaceColor( picoSurface, 0, 0, color ); // Free up malloc'ed LL entries for(i=0; inumXYZ; i++) { if(p_index_LUT[i].next != NULL) { p_index_LUT2 = p_index_LUT[i].next; do { p_index_LUT3 = p_index_LUT2->next; _pico_free(p_index_LUT2); p_index_LUT2 = p_index_LUT3; dups--; } while (p_index_LUT2 != NULL); } } if (dups) _pico_printf(PICO_WARNING, " Not all LL mallocs freed\n"); // Free malloc'ed LUTs _pico_free(p_index_LUT); _pico_free(p_index_LUT_DUPS); /* return the new pico model */ return picoModel; } /* pico file format module definition */ const picoModule_t picoModuleFM = { "0.85", /* module version string */ "Heretic 2 FM", /* module display name */ "Nurail", /* author's name */ "2003 Nurail", /* module copyright */ { "fm", NULL, NULL, NULL /* default extensions to use */ }, _fm_canload, /* validation routine */ _fm_load, /* load routine */ NULL, /* save validation routine */ NULL /* save routine */ }; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/pm_fm.h������������������������������������������������������������0000664�0000000�0000000�00000026645�13217505464�0020101�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ // This header file is based from the following: /* FlexModel.H - Header file for FlexModel file structure By Chris Burke serotonin@earthlink.net */ #ifndef __PM_FM_H__ #define __PM_FM_H__ #include "picointernal.h" // // Absolute limits (from QData / QMView source) // #define MAX_FM_TRIANGLES 2048 #define MAX_FM_VERTS 2048 #define MAX_FM_FRAMES 2048 #define MAX_FM_SKINS 64 #define MAX_FM_SKINNAME 64 #define MAX_FM_MESH_NODES 16 #define DTRIVERTX_V0 0 #define DTRIVERTX_V1 1 #define DTRIVERTX_V2 2 #define DTRIVERTX_LNI 3 #define DTRIVERTX_SIZE 4 #define SKINPAGE_WIDTH 640 #define SKINPAGE_HEIGHT 480 #define ENCODED_WIDTH_X 92 #define ENCODED_WIDTH_Y 475 #define ENCODED_HEIGHT_X 128 #define ENCODED_HEIGHT_Y 475 #define SCALE_ADJUST_FACTOR 0.96 #define INFO_HEIGHT 5 #define INFO_Y (SKINPAGE_HEIGHT-INFO_HEIGHT) #ifndef byte #define byte unsigned char #endif // // Generic header on every chunk // #define FM_MAXCHUNKIDENT 32L typedef struct { char ident[FM_MAXCHUNKIDENT]; unsigned int version; unsigned int size; } fm_chunk_header_t; // // The format of the "header" chunk // #define FM_HEADERCHUNKNAME "header" #define FM_HEADERCHUNKVER 2 #define FM_HEADERCHUNKSIZE 40 typedef struct { int skinWidth; // in pixels int skinHeight; // in pixels int frameSize; // size of each frame (in bytes) int numSkins; // number of skins int numXYZ; // number of unique vertices in 3D space int numST; // number of unique vertices in texture space int numTris; // number of unique triangles int numGLCmds; // # 32-bit elements in strip/fan command list int numFrames; // number of animation frames int numMeshNodes; // number of mesh nodes } fm_header_t; // // The format of an entry in the "skin" chunk. // The number of entries is given in the fmheader chunk // #define FM_SKINCHUNKNAME "skin" #define FM_SKINCHUNKVER 1 #define FM_MAXPATHLENGTH 64L #define FM_SKINPATHSIZE (FM_MAXPATHLENGTH) typedef struct { char path[FM_SKINPATHSIZE]; // path, relative to 'base' } fm_skinpath_t; // // The format of the "st coord" chunk. This is a list // of unique skin texture (u, v) coordinates to be mapped // to verteces of the model // #define FM_STCOORDCHUNKNAME "st coord" #define FM_STCOORDCHUNKVER 1 #define FM_STCOORDUVSIZE (2L + 2L) typedef struct { short s; short t; } fm_st_t; // // The format of the "tris" chunk. This is a list of vertex indeces // in 3D space, and the corresponding vertex indeces in texture space. // #define FM_TRISCHUNKNAME "tris" #define FM_TRISCHUNKVER 1 #define FM_TRISINFOSIZE (2L*3 + 2L*3) typedef struct { short index_xyz[3]; short index_st[3]; } fm_xyz_st_t; // // The format of the "frames" chunk. This is a list of animation // frames, each specifying the coordinates and "light normal" index // of every vertex of the model in 3D space. // #define FM_FRAMESCHUNKNAME "frames" #define FM_FRAMESCHUNKVER 1 #define FM_NUMVERTEXNORMALS 162 // Frame info typedef struct { byte v[3]; // scaled by header info byte lightnormalindex; // index in canned table of closest vertex normal } fm_vert_normal_t; typedef struct { float scale[3]; // multiply byte verts by this float translate[3]; // then add this char name[16]; // frame name } fm_framehdr_t; typedef struct { fm_framehdr_t header; // One header per frame fm_vert_normal_t verts[1]; // variable number of these } fm_frame_t; typedef struct { fm_chunk_header_t *fm_header_hdr; fm_header_t *fm_header; fm_chunk_header_t *fm_skin_hdr; fm_skinpath_t *fm_skin; fm_chunk_header_t *fm_st_hdr; fm_st_t *fm_st; fm_chunk_header_t *fm_tri_hdr; fm_xyz_st_t *fm_tri; fm_chunk_header_t *fm_frame_hdr; fm_frame_t *fm_frame; } fm_t; float fm_normals[FM_NUMVERTEXNORMALS][3] = { {-0.525731f, 0.000000f, 0.850651f}, {-0.442863f, 0.238856f, 0.864188f}, {-0.295242f, 0.000000f, 0.955423f}, {-0.309017f, 0.500000f, 0.809017f}, {-0.162460f, 0.262866f, 0.951056f}, {0.000000f, 0.000000f, 1.000000f}, {0.000000f, 0.850651f, 0.525731f}, {-0.147621f, 0.716567f, 0.681718f}, {0.147621f, 0.716567f, 0.681718f}, {0.000000f, 0.525731f, 0.850651f}, {0.309017f, 0.500000f, 0.809017f}, {0.525731f, 0.000000f, 0.850651f}, {0.295242f, 0.000000f, 0.955423f}, {0.442863f, 0.238856f, 0.864188f}, {0.162460f, 0.262866f, 0.951056f}, {-0.681718f, 0.147621f, 0.716567f}, {-0.809017f, 0.309017f, 0.500000f}, {-0.587785f, 0.425325f, 0.688191f}, {-0.850651f, 0.525731f, 0.000000f}, {-0.864188f, 0.442863f, 0.238856f}, {-0.716567f, 0.681718f, 0.147621f}, {-0.688191f, 0.587785f, 0.425325f}, {-0.500000f, 0.809017f, 0.309017f}, {-0.238856f, 0.864188f, 0.442863f}, {-0.425325f, 0.688191f, 0.587785f}, {-0.716567f, 0.681718f, -0.147621f}, {-0.500000f, 0.809017f, -0.309017f}, {-0.525731f, 0.850651f, 0.000000f}, {0.000000f, 0.850651f, -0.525731f}, {-0.238856f, 0.864188f, -0.442863f}, {0.000000f, 0.955423f, -0.295242f}, {-0.262866f, 0.951056f, -0.162460f}, {0.000000f, 1.000000f, 0.000000f}, {0.000000f, 0.955423f, 0.295242f}, {-0.262866f, 0.951056f, 0.162460f}, {0.238856f, 0.864188f, 0.442863f}, {0.262866f, 0.951056f, 0.162460f}, {0.500000f, 0.809017f, 0.309017f}, {0.238856f, 0.864188f, -0.442863f}, {0.262866f, 0.951056f, -0.162460f}, {0.500000f, 0.809017f, -0.309017f}, {0.850651f, 0.525731f, 0.000000f}, {0.716567f, 0.681718f, 0.147621f}, {0.716567f, 0.681718f, -0.147621f}, {0.525731f, 0.850651f, 0.000000f}, {0.425325f, 0.688191f, 0.587785f}, {0.864188f, 0.442863f, 0.238856f}, {0.688191f, 0.587785f, 0.425325f}, {0.809017f, 0.309017f, 0.500000f}, {0.681718f, 0.147621f, 0.716567f}, {0.587785f, 0.425325f, 0.688191f}, {0.955423f, 0.295242f, 0.000000f}, {1.000000f, 0.000000f, 0.000000f}, {0.951056f, 0.162460f, 0.262866f}, {0.850651f, -0.525731f, 0.000000f}, {0.955423f, -0.295242f, 0.000000f}, {0.864188f, -0.442863f, 0.238856f}, {0.951056f, -0.162460f, 0.262866f}, {0.809017f, -0.309017f, 0.500000f}, {0.681718f, -0.147621f, 0.716567f}, {0.850651f, 0.000000f, 0.525731f}, {0.864188f, 0.442863f, -0.238856f}, {0.809017f, 0.309017f, -0.500000f}, {0.951056f, 0.162460f, -0.262866f}, {0.525731f, 0.000000f, -0.850651f}, {0.681718f, 0.147621f, -0.716567f}, {0.681718f, -0.147621f, -0.716567f}, {0.850651f, 0.000000f, -0.525731f}, {0.809017f, -0.309017f, -0.500000f}, {0.864188f, -0.442863f, -0.238856f}, {0.951056f, -0.162460f, -0.262866f}, {0.147621f, 0.716567f, -0.681718f}, {0.309017f, 0.500000f, -0.809017f}, {0.425325f, 0.688191f, -0.587785f}, {0.442863f, 0.238856f, -0.864188f}, {0.587785f, 0.425325f, -0.688191f}, {0.688191f, 0.587785f, -0.425325f}, {-0.147621f, 0.716567f, -0.681718f}, {-0.309017f, 0.500000f, -0.809017f}, {0.000000f, 0.525731f, -0.850651f}, {-0.525731f, 0.000000f, -0.850651f}, {-0.442863f, 0.238856f, -0.864188f}, {-0.295242f, 0.000000f, -0.955423f}, {-0.162460f, 0.262866f, -0.951056f}, {0.000000f, 0.000000f, -1.000000f}, {0.295242f, 0.000000f, -0.955423f}, {0.162460f, 0.262866f, -0.951056f}, {-0.442863f, -0.238856f, -0.864188f}, {-0.309017f, -0.500000f, -0.809017f}, {-0.162460f, -0.262866f, -0.951056f}, {0.000000f, -0.850651f, -0.525731f}, {-0.147621f, -0.716567f, -0.681718f}, {0.147621f, -0.716567f, -0.681718f}, {0.000000f, -0.525731f, -0.850651f}, {0.309017f, -0.500000f, -0.809017f}, {0.442863f, -0.238856f, -0.864188f}, {0.162460f, -0.262866f, -0.951056f}, {0.238856f, -0.864188f, -0.442863f}, {0.500000f, -0.809017f, -0.309017f}, {0.425325f, -0.688191f, -0.587785f}, {0.716567f, -0.681718f, -0.147621f}, {0.688191f, -0.587785f, -0.425325f}, {0.587785f, -0.425325f, -0.688191f}, {0.000000f, -0.955423f, -0.295242f}, {0.000000f, -1.000000f, 0.000000f}, {0.262866f, -0.951056f, -0.162460f}, {0.000000f, -0.850651f, 0.525731f}, {0.000000f, -0.955423f, 0.295242f}, {0.238856f, -0.864188f, 0.442863f}, {0.262866f, -0.951056f, 0.162460f}, {0.500000f, -0.809017f, 0.309017f}, {0.716567f, -0.681718f, 0.147621f}, {0.525731f, -0.850651f, 0.000000f}, {-0.238856f, -0.864188f, -0.442863f}, {-0.500000f, -0.809017f, -0.309017f}, {-0.262866f, -0.951056f, -0.162460f}, {-0.850651f, -0.525731f, 0.000000f}, {-0.716567f, -0.681718f, -0.147621f}, {-0.716567f, -0.681718f, 0.147621f}, {-0.525731f, -0.850651f, 0.000000f}, {-0.500000f, -0.809017f, 0.309017f}, {-0.238856f, -0.864188f, 0.442863f}, {-0.262866f, -0.951056f, 0.162460f}, {-0.864188f, -0.442863f, 0.238856f}, {-0.809017f, -0.309017f, 0.500000f}, {-0.688191f, -0.587785f, 0.425325f}, {-0.681718f, -0.147621f, 0.716567f}, {-0.442863f, -0.238856f, 0.864188f}, {-0.587785f, -0.425325f, 0.688191f}, {-0.309017f, -0.500000f, 0.809017f}, {-0.147621f, -0.716567f, 0.681718f}, {-0.425325f, -0.688191f, 0.587785f}, {-0.162460f, -0.262866f, 0.951056f}, {0.442863f, -0.238856f, 0.864188f}, {0.162460f, -0.262866f, 0.951056f}, {0.309017f, -0.500000f, 0.809017f}, {0.147621f, -0.716567f, 0.681718f}, {0.000000f, -0.525731f, 0.850651f}, {0.425325f, -0.688191f, 0.587785f}, {0.587785f, -0.425325f, 0.688191f}, {0.688191f, -0.587785f, 0.425325f}, {-0.955423f, 0.295242f, 0.000000f}, {-0.951056f, 0.162460f, 0.262866f}, {-1.000000f, 0.000000f, 0.000000f}, {-0.850651f, 0.000000f, 0.525731f}, {-0.955423f, -0.295242f, 0.000000f}, {-0.951056f, -0.162460f, 0.262866f}, {-0.864188f, 0.442863f, -0.238856f}, {-0.951056f, 0.162460f, -0.262866f}, {-0.809017f, 0.309017f, -0.500000f}, {-0.864188f, -0.442863f, -0.238856f}, {-0.951056f, -0.162460f, -0.262866f}, {-0.809017f, -0.309017f, -0.500000f}, {-0.681718f, 0.147621f, -0.716567f}, {-0.681718f, -0.147621f, -0.716567f}, {-0.850651f, 0.000000f, -0.525731f}, {-0.688191f, 0.587785f, -0.425325f}, {-0.587785f, 0.425325f, -0.688191f}, {-0.425325f, 0.688191f, -0.587785f}, {-0.425325f, -0.688191f, -0.587785f}, {-0.587785f, -0.425325f, -0.688191f}, {-0.688191f, -0.587785f, -0.425325f}, }; #endif �������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/pm_lwo.c�����������������������������������������������������������0000664�0000000�0000000�00000031527�13217505464�0020266�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define PM_LWO_C /* dependencies */ #include "picointernal.h" #include "lwo/lwo2.h" /* uncomment when debugging this module */ /*#define DEBUG_PM_LWO*/ #ifdef DEBUG_PM_LWO #include "time.h" #endif /* helper functions */ static const char *lwo_lwIDToStr( unsigned int lwID ) { static char lwIDStr[5]; if (!lwID) { return "n/a"; } lwIDStr[ 0 ] = (char)((lwID) >> 24); lwIDStr[ 1 ] = (char)((lwID) >> 16); lwIDStr[ 2 ] = (char)((lwID) >> 8); lwIDStr[ 3 ] = (char)((lwID)); lwIDStr[ 4 ] = '\0'; return lwIDStr; } /* _lwo_canload() validates a LightWave Object model file. btw, i use the preceding underscore cause it's a static func referenced by one structure only. */ static int _lwo_canload( PM_PARAMS_CANLOAD ) { picoMemStream_t *s; unsigned int failID = 0; int failpos = -1; int ret; /* create a new pico memorystream */ s = _pico_new_memstream( (picoByte_t *)buffer, bufSize ); if (s == NULL) { return PICO_PMV_ERROR_MEMORY; } ret = lwValidateObject( fileName, s, &failID, &failpos ); _pico_free_memstream( s ); return ret; } /* _lwo_load() loads a LightWave Object model file. */ static picoModel_t *_lwo_load( PM_PARAMS_LOAD ) { picoMemStream_t *s; unsigned int failID = 0; int failpos = -1; lwObject *obj; lwSurface *surface; lwLayer *layer; lwPoint *pt; lwPolygon *pol; lwPolVert *v; lwVMapPt *vm; char name[ 256 ]; int i, j, k, numverts; picoModel_t *picoModel; picoSurface_t *picoSurface; picoShader_t *picoShader; picoVec3_t xyz, normal; picoVec2_t st; picoColor_t color; int defaultSTAxis[ 2 ]; picoVec2_t defaultXYZtoSTScale; picoVertexCombinationHash_t **hashTable; picoVertexCombinationHash_t *vertexCombinationHash; int surfacePolyCount; #ifdef DEBUG_PM_LWO clock_t load_start, load_finish, convert_start, convert_finish; double load_elapsed, convert_elapsed; load_start = clock(); #endif /* do frame check */ if( frameNum < 0 || frameNum >= 1 ) { _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" ); return NULL; } /* create a new pico memorystream */ s = _pico_new_memstream( (picoByte_t *)buffer, bufSize ); if (s == NULL) { return NULL; } obj = lwGetObject( fileName, s, &failID, &failpos ); _pico_free_memstream( s ); if( !obj ) { _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos ); return NULL; } #ifdef DEBUG_PM_LWO convert_start = load_finish = clock(); load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC; #endif /* ------------------------------------------------- pico model creation ------------------------------------------------- */ /* create a new pico model */ picoModel = PicoNewModel(); if (picoModel == NULL) { _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); return NULL; } /* do model setup */ PicoSetModelFrameNum( picoModel, frameNum ); PicoSetModelNumFrames( picoModel, 1 ); PicoSetModelName( picoModel, fileName ); PicoSetModelFileName( picoModel, fileName ); /* create all polygons from layer[ 0 ] that belong to this surface */ layer = &obj->layer[0]; /* warn the user that other layers are discarded */ if (obj->nlayers > 1) { _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers ); } /* initialize dummy normal */ normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f; /* setup default st map */ st[ 0 ] = st[ 1 ] = 0.f; /* st[0] holds max, st[1] holds max par one */ defaultSTAxis[ 0 ] = 0; defaultSTAxis[ 1 ] = 1; for( i = 0; i < 3; i++ ) { float min = layer->bbox[ i ]; float max = layer->bbox[ i + 3 ]; float size = max - min; if (size > st[ 0 ]) { defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ]; defaultSTAxis[ 0 ] = i; st[ 1 ] = st[ 0 ]; st[ 0 ] = size; } else if (size > st[ 1 ]) { defaultSTAxis[ 1 ] = i; st[ 1 ] = size; } } defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ]; defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ]; /* LWO surfaces become pico surfaces */ surface = obj->surf; while (surface) { /* allocate new pico surface */ picoSurface = PicoNewSurface( picoModel ); if (picoSurface == NULL) { _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); PicoFreeModel( picoModel ); lwFreeObject( obj ); return NULL; } /* LWO model surfaces are all triangle meshes */ PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); /* set surface name */ PicoSetSurfaceName( picoSurface, surface->name ); /* create new pico shader */ picoShader = PicoNewShader( picoModel ); if (picoShader == NULL) { _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); PicoFreeModel( picoModel ); lwFreeObject( obj ); return NULL; } /* detox and set shader name */ strncpy( name, surface->name, sizeof(name) ); _pico_first_token( name ); _pico_setfext( name, "" ); _pico_unixify( name ); PicoSetShaderName( picoShader, name ); /* associate current surface with newly created shader */ PicoSetSurfaceShader( picoSurface, picoShader ); /* copy indices and vertex data */ numverts = 0; hashTable = PicoNewVertexCombinationHashTable(); if (hashTable == NULL) { _pico_printf( PICO_ERROR, "Unable to allocate hash table" ); PicoFreeModel( picoModel ); lwFreeObject( obj ); return NULL; } /* Local polygon index for this surface. We don't want to use i in the loop * since that is the global poly index for the entire layer */ surfacePolyCount = 0; for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ ) { /* does this polygon belong to this surface? */ if (pol->surf != surface) continue; /* we only support polygons of the FACE type */ if (pol->type != ID_FACE) { _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) ); continue; } /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */ if (pol->nverts != 3) { _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts ); continue; } /* We haven't discarded this polygon, so bump the surface polycount */ surfacePolyCount++; for( j = 0, v = pol->v; j < 3; j++, v++ ) { pt = &layer->point.pt[ v->index ]; /* setup data */ xyz[ 0 ] = pt->pos[ 0 ]; xyz[ 1 ] = pt->pos[ 2 ]; xyz[ 2 ] = pt->pos[ 1 ]; /* doom3 lwo data doesn't seem to have smoothing-angle information */ #if 0 if(surface->smooth <= 0) { /* use face normals */ normal[ 0 ] = v->norm[ 0 ]; normal[ 1 ] = v->norm[ 2 ]; normal[ 2 ] = v->norm[ 1 ]; } else #endif { /* smooth normals later */ normal[ 0 ] = 0; normal[ 1 ] = 0; normal[ 2 ] = 0; } st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ]; st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ]; color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF); color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF); color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF); color[ 3 ] = 0xFF; /* set from points */ for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ ) { if (vm->vmap->type == LWID_('T','X','U','V')) { /* set st coords */ st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ]; st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ]; } else if (vm->vmap->type == LWID_('R','G','B','A')) { /* set rgba */ color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF); color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF); color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF); color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF); } } /* override with polygon data */ for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ ) { if (vm->vmap->type == LWID_('T','X','U','V')) { /* set st coords */ st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ]; st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ]; } else if (vm->vmap->type == LWID_('R','G','B','A')) { /* set rgba */ color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF); color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF); color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF); color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF); } } /* find vertex in this surface and if we can't find it there create it */ vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color ); if (vertexCombinationHash) { /* found an existing one */ PicoSetSurfaceIndex( picoSurface, (surfacePolyCount * 3 + j ), vertexCombinationHash->index ); } else { /* it is a new one */ vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts ); if (vertexCombinationHash == NULL) { _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" ); PicoFreeVertexCombinationHashTable( hashTable ); PicoFreeModel( picoModel ); lwFreeObject( obj ); return NULL; } /* add the vertex to this surface */ PicoSetSurfaceXYZ( picoSurface, numverts, xyz ); /* set dummy normal */ PicoSetSurfaceNormal( picoSurface, numverts, normal ); /* set color */ PicoSetSurfaceColor( picoSurface, 0, numverts, color ); /* set st coords */ PicoSetSurfaceST( picoSurface, 0, numverts, st ); /* set index */ PicoSetSurfaceIndex( picoSurface, (surfacePolyCount * 3 + j ), (picoIndex_t) numverts ); numverts++; } } } /* free the hashtable */ PicoFreeVertexCombinationHashTable( hashTable ); /* get next surface */ surface = surface->next; } #ifdef DEBUG_PM_LWO load_start = convert_finish = clock(); #endif lwFreeObject( obj ); #ifdef DEBUG_PM_LWO load_finish = clock(); load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC; convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC; _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed ); #endif /* return the new pico model */ return picoModel; } /* pico file format module definition */ const picoModule_t picoModuleLWO = { "1.0", /* module version string */ "LightWave Object", /* module display name */ "Arnout van Meer", /* author's name */ "2003 Arnout van Meer, 2000 Ernie Wright", /* module copyright */ { "lwo", NULL, NULL, NULL /* default extensions to use */ }, _lwo_canload, /* validation routine */ _lwo_load, /* load routine */ NULL, /* save validation routine */ NULL /* save routine */ }; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/pm_md2.c�����������������������������������������������������������0000664�0000000�0000000�00000043421�13217505464�0020143�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* Nurail: Used pm_md3.c (Randy Reddig) as a template. */ /* marker */ #define PM_MD2_C /* dependencies */ #include "picointernal.h" #ifdef WIN32 #include #define snprintf _snprintf #endif /* md2 model format */ static const char* MD2_MAGIC = "IDP2"; #define MD2_VERSION 8 #define MD2_NUMVERTEXNORMALS 162 #define MD2_MAX_SKINNAME 64 #define MD2_MAX_TRIANGLES 4096 #define MD2_MAX_VERTS 2048 #define MD2_MAX_FRAMES 512 #define MD2_MAX_MD2SKINS 32 #define MD2_MAX_SKINNAME 64 #ifndef byte #define byte unsigned char #endif typedef struct index_LUT_s { short Vert; short ST; } index_LUT_t; typedef struct index_DUP_LUT_s { short ST; short OldVert; } index_DUP_LUT_t; typedef struct { short s; short t; } md2St_t; typedef struct { short index_xyz[3]; short index_st[3]; } md2Triangle_t; typedef struct { byte v[3]; // scaled byte to fit in frame mins/maxs byte lightnormalindex; } md2XyzNormal_t; typedef struct md2Frame_s { float scale[3]; // multiply byte verts by this float translate[3]; // then add this char name[16]; // frame name from grabbing md2XyzNormal_t verts[1]; // variable sized } md2Frame_t; /* md2 model file md2 structure */ typedef struct md2_s { char magic[ 4 ]; int version; int skinWidth; int skinHeight; int frameSize; int numSkins; int numXYZ; int numST; int numTris; int numGLCmds; int numFrames; int ofsSkins; int ofsST; int ofsTris; int ofsFrames; int ofsGLCmds; int ofsEnd; } md2_t; float md2_normals[ MD2_NUMVERTEXNORMALS ][ 3 ] = { { -0.525731f, 0.000000f, 0.850651f }, { -0.442863f, 0.238856f, 0.864188f }, { -0.295242f, 0.000000f, 0.955423f }, { -0.309017f, 0.500000f, 0.809017f }, { -0.162460f, 0.262866f, 0.951056f }, { 0.000000f, 0.000000f, 1.000000f }, { 0.000000f, 0.850651f, 0.525731f }, { -0.147621f, 0.716567f, 0.681718f }, { 0.147621f, 0.716567f, 0.681718f }, { 0.000000f, 0.525731f, 0.850651f }, { 0.309017f, 0.500000f, 0.809017f }, { 0.525731f, 0.000000f, 0.850651f }, { 0.295242f, 0.000000f, 0.955423f }, { 0.442863f, 0.238856f, 0.864188f }, { 0.162460f, 0.262866f, 0.951056f }, { -0.681718f, 0.147621f, 0.716567f }, { -0.809017f, 0.309017f, 0.500000f }, { -0.587785f, 0.425325f, 0.688191f }, { -0.850651f, 0.525731f, 0.000000f }, { -0.864188f, 0.442863f, 0.238856f }, { -0.716567f, 0.681718f, 0.147621f }, { -0.688191f, 0.587785f, 0.425325f }, { -0.500000f, 0.809017f, 0.309017f }, { -0.238856f, 0.864188f, 0.442863f }, { -0.425325f, 0.688191f, 0.587785f }, { -0.716567f, 0.681718f, -0.147621f }, { -0.500000f, 0.809017f, -0.309017f }, { -0.525731f, 0.850651f, 0.000000f }, { 0.000000f, 0.850651f, -0.525731f }, { -0.238856f, 0.864188f, -0.442863f }, { 0.000000f, 0.955423f, -0.295242f }, { -0.262866f, 0.951056f, -0.162460f }, { 0.000000f, 1.000000f, 0.000000f }, { 0.000000f, 0.955423f, 0.295242f }, { -0.262866f, 0.951056f, 0.162460f }, { 0.238856f, 0.864188f, 0.442863f }, { 0.262866f, 0.951056f, 0.162460f }, { 0.500000f, 0.809017f, 0.309017f }, { 0.238856f, 0.864188f, -0.442863f }, { 0.262866f, 0.951056f, -0.162460f }, { 0.500000f, 0.809017f, -0.309017f }, { 0.850651f, 0.525731f, 0.000000f }, { 0.716567f, 0.681718f, 0.147621f }, { 0.716567f, 0.681718f, -0.147621f }, { 0.525731f, 0.850651f, 0.000000f }, { 0.425325f, 0.688191f, 0.587785f }, { 0.864188f, 0.442863f, 0.238856f }, { 0.688191f, 0.587785f, 0.425325f }, { 0.809017f, 0.309017f, 0.500000f }, { 0.681718f, 0.147621f, 0.716567f }, { 0.587785f, 0.425325f, 0.688191f }, { 0.955423f, 0.295242f, 0.000000f }, { 1.000000f, 0.000000f, 0.000000f }, { 0.951056f, 0.162460f, 0.262866f }, { 0.850651f, -0.525731f, 0.000000f }, { 0.955423f, -0.295242f, 0.000000f }, { 0.864188f, -0.442863f, 0.238856f }, { 0.951056f, -0.162460f, 0.262866f }, { 0.809017f, -0.309017f, 0.500000f }, { 0.681718f, -0.147621f, 0.716567f }, { 0.850651f, 0.000000f, 0.525731f }, { 0.864188f, 0.442863f, -0.238856f }, { 0.809017f, 0.309017f, -0.500000f }, { 0.951056f, 0.162460f, -0.262866f }, { 0.525731f, 0.000000f, -0.850651f }, { 0.681718f, 0.147621f, -0.716567f }, { 0.681718f, -0.147621f, -0.716567f }, { 0.850651f, 0.000000f, -0.525731f }, { 0.809017f, -0.309017f, -0.500000f }, { 0.864188f, -0.442863f, -0.238856f }, { 0.951056f, -0.162460f, -0.262866f }, { 0.147621f, 0.716567f, -0.681718f }, { 0.309017f, 0.500000f, -0.809017f }, { 0.425325f, 0.688191f, -0.587785f }, { 0.442863f, 0.238856f, -0.864188f }, { 0.587785f, 0.425325f, -0.688191f }, { 0.688191f, 0.587785f, -0.425325f }, { -0.147621f, 0.716567f, -0.681718f }, { -0.309017f, 0.500000f, -0.809017f }, { 0.000000f, 0.525731f, -0.850651f }, { -0.525731f, 0.000000f, -0.850651f }, { -0.442863f, 0.238856f, -0.864188f }, { -0.295242f, 0.000000f, -0.955423f }, { -0.162460f, 0.262866f, -0.951056f }, { 0.000000f, 0.000000f, -1.000000f }, { 0.295242f, 0.000000f, -0.955423f }, { 0.162460f, 0.262866f, -0.951056f }, { -0.442863f, -0.238856f, -0.864188f }, { -0.309017f, -0.500000f, -0.809017f }, { -0.162460f, -0.262866f, -0.951056f }, { 0.000000f, -0.850651f, -0.525731f }, { -0.147621f, -0.716567f, -0.681718f }, { 0.147621f, -0.716567f, -0.681718f }, { 0.000000f, -0.525731f, -0.850651f }, { 0.309017f, -0.500000f, -0.809017f }, { 0.442863f, -0.238856f, -0.864188f }, { 0.162460f, -0.262866f, -0.951056f }, { 0.238856f, -0.864188f, -0.442863f }, { 0.500000f, -0.809017f, -0.309017f }, { 0.425325f, -0.688191f, -0.587785f }, { 0.716567f, -0.681718f, -0.147621f }, { 0.688191f, -0.587785f, -0.425325f }, { 0.587785f, -0.425325f, -0.688191f }, { 0.000000f, -0.955423f, -0.295242f }, { 0.000000f, -1.000000f, 0.000000f }, { 0.262866f, -0.951056f, -0.162460f }, { 0.000000f, -0.850651f, 0.525731f }, { 0.000000f, -0.955423f, 0.295242f }, { 0.238856f, -0.864188f, 0.442863f }, { 0.262866f, -0.951056f, 0.162460f }, { 0.500000f, -0.809017f, 0.309017f }, { 0.716567f, -0.681718f, 0.147621f }, { 0.525731f, -0.850651f, 0.000000f }, { -0.238856f, -0.864188f, -0.442863f }, { -0.500000f, -0.809017f, -0.309017f }, { -0.262866f, -0.951056f, -0.162460f }, { -0.850651f, -0.525731f, 0.000000f }, { -0.716567f, -0.681718f, -0.147621f }, { -0.716567f, -0.681718f, 0.147621f }, { -0.525731f, -0.850651f, 0.000000f }, { -0.500000f, -0.809017f, 0.309017f }, { -0.238856f, -0.864188f, 0.442863f }, { -0.262866f, -0.951056f, 0.162460f }, { -0.864188f, -0.442863f, 0.238856f }, { -0.809017f, -0.309017f, 0.500000f }, { -0.688191f, -0.587785f, 0.425325f }, { -0.681718f, -0.147621f, 0.716567f }, { -0.442863f, -0.238856f, 0.864188f }, { -0.587785f, -0.425325f, 0.688191f }, { -0.309017f, -0.500000f, 0.809017f }, { -0.147621f, -0.716567f, 0.681718f }, { -0.425325f, -0.688191f, 0.587785f }, { -0.162460f, -0.262866f, 0.951056f }, { 0.442863f, -0.238856f, 0.864188f }, { 0.162460f, -0.262866f, 0.951056f }, { 0.309017f, -0.500000f, 0.809017f }, { 0.147621f, -0.716567f, 0.681718f }, { 0.000000f, -0.525731f, 0.850651f }, { 0.425325f, -0.688191f, 0.587785f }, { 0.587785f, -0.425325f, 0.688191f }, { 0.688191f, -0.587785f, 0.425325f }, { -0.955423f, 0.295242f, 0.000000f }, { -0.951056f, 0.162460f, 0.262866f }, { -1.000000f, 0.000000f, 0.000000f }, { -0.850651f, 0.000000f, 0.525731f }, { -0.955423f, -0.295242f, 0.000000f }, { -0.951056f, -0.162460f, 0.262866f }, { -0.864188f, 0.442863f, -0.238856f }, { -0.951056f, 0.162460f, -0.262866f }, { -0.809017f, 0.309017f, -0.500000f }, { -0.864188f, -0.442863f, -0.238856f }, { -0.951056f, -0.162460f, -0.262866f }, { -0.809017f, -0.309017f, -0.500000f }, { -0.681718f, 0.147621f, -0.716567f }, { -0.681718f, -0.147621f, -0.716567f }, { -0.850651f, 0.000000f, -0.525731f }, { -0.688191f, 0.587785f, -0.425325f }, { -0.587785f, 0.425325f, -0.688191f }, { -0.425325f, 0.688191f, -0.587785f }, { -0.425325f, -0.688191f, -0.587785f }, { -0.587785f, -0.425325f, -0.688191f }, { -0.688191f, -0.587785f, -0.425325f }, }; // _md2_canload() static int _md2_canload( PM_PARAMS_CANLOAD ) { md2_t *md2; /* to keep the compiler happy */ *fileName = *fileName; /* sanity check */ if( bufSize < ( sizeof( *md2 ) * 2) ) return PICO_PMV_ERROR_SIZE; /* set as md2 */ md2 = (md2_t*) buffer; /* check md2 magic */ if (md2->magic[0] != MD2_MAGIC[0] || md2->magic[1] != MD2_MAGIC[1] || md2->magic[2] != MD2_MAGIC[2] || md2->magic[3] != MD2_MAGIC[3]) { return PICO_PMV_ERROR_IDENT; } /* check md2 version */ if( _pico_little_long( md2->version ) != MD2_VERSION ) return PICO_PMV_ERROR_VERSION; /* file seems to be a valid md2 */ return PICO_PMV_OK; } // _md2_load() loads a quake2 md2 model file. static picoModel_t *_md2_load( PM_PARAMS_LOAD ) { int i, j; /*short tot_numVerts; -- silence compiler warning */ index_LUT_t *p_index_LUT; md2Triangle_t *p_md2Triangle; char skinname[ MD2_MAX_SKINNAME ]; md2_t *md2; md2St_t *texCoord; md2Frame_t *frame; md2Triangle_t *triangle; md2XyzNormal_t *vertex; picoByte_t *bb; picoModel_t *picoModel; picoSurface_t *picoSurface; picoShader_t *picoShader; picoVec3_t xyz, normal; picoVec2_t st; picoColor_t color; /* set as md2 */ bb = (picoByte_t*) buffer; md2 = (md2_t*) buffer; /* check ident and version */ if (md2->magic[0] != MD2_MAGIC[0] || md2->magic[1] != MD2_MAGIC[1] || md2->magic[2] != MD2_MAGIC[2] || md2->magic[3] != MD2_MAGIC[3] || _pico_little_long(md2->version) != MD2_VERSION) { /* not an md2 file (todo: set error) */ _pico_printf( PICO_ERROR, "%s is not an MD2 File!", fileName ); return NULL; } // swap md2 md2->version = _pico_little_long( md2->version ); md2->skinWidth = _pico_little_long( md2->skinWidth ); md2->skinHeight = _pico_little_long( md2->skinHeight ); md2->frameSize = _pico_little_long( md2->frameSize ); md2->numSkins = _pico_little_long( md2->numSkins ); md2->numXYZ = _pico_little_long( md2->numXYZ ); md2->numST = _pico_little_long( md2->numST ); md2->numTris = _pico_little_long( md2->numTris ); md2->numGLCmds = _pico_little_long( md2->numGLCmds ); md2->numFrames = _pico_little_long( md2->numFrames ); md2->ofsSkins = _pico_little_long( md2->ofsSkins ); md2->ofsST = _pico_little_long( md2->ofsST ); md2->ofsTris = _pico_little_long( md2->ofsTris ); md2->ofsFrames = _pico_little_long( md2->ofsFrames ); md2->ofsGLCmds = _pico_little_long( md2->ofsGLCmds ); md2->ofsEnd = _pico_little_long( md2->ofsEnd ); // do frame check if( md2->numFrames < 1 ) { _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName ); return NULL; } if( frameNum < 0 || frameNum >= md2->numFrames ) { _pico_printf( PICO_ERROR, "Invalid or out-of-range MD2 frame specified" ); return NULL; } // Setup Frame frame = (md2Frame_t *) (bb + md2->ofsFrames + (sizeof(md2Frame_t) * frameNum)); // swap frame scale and translation for( i = 0; i < 3; i++ ) { frame->scale[ i ] = _pico_little_float( frame->scale[ i ] ); frame->translate[ i ] = _pico_little_float( frame->translate[ i ] ); } // swap triangles triangle = (md2Triangle_t *) ((picoByte_t *) (bb + md2->ofsTris) ); for( i = 0; i < md2->numTris; i++, triangle++ ) { for( j = 0; j < 3; j++ ) { triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] ); triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] ); } } // swap st coords texCoord = (md2St_t*) ((picoByte_t *) (bb + md2->ofsST) ); for( i = 0; i < md2->numST; i++, texCoord++ ) { texCoord->s = _pico_little_short( texCoord->s ); texCoord->t = _pico_little_short( texCoord->t ); } // Print out md2 values _pico_printf(PICO_VERBOSE,"Skins: %d Verts: %d STs: %d Triangles: %d Frames: %d\nSkin Name \"%s\"\n", md2->numSkins, md2->numXYZ, md2->numST, md2->numTris, md2->numFrames, &skinname ); /* create new pico model */ picoModel = PicoNewModel(); if( picoModel == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); return NULL; } /* do model setup */ PicoSetModelFrameNum( picoModel, frameNum ); PicoSetModelNumFrames( picoModel, md2->numFrames ); /* sea */ PicoSetModelName( picoModel, fileName ); PicoSetModelFileName( picoModel, fileName ); for (i = 0; i < md2->numSkins; i++) { char *offsetSkin = (char*) (bb + md2->ofsSkins) + i * MD2_MAX_SKINNAME; /* set Skin Name */ strncpy(skinname, offsetSkin, MD2_MAX_SKINNAME); /* detox Skin name */ if (skinname[0] == '.') {/* special case ufoai skinpath */ char path[MD2_MAX_SKINNAME]; char skinnameRelative[MD2_MAX_SKINNAME]; strncpy(path, fileName, MD2_MAX_SKINNAME); strncpy(skinnameRelative, skinname, MD2_MAX_SKINNAME); _pico_unixify(path); for (j = MD2_MAX_SKINNAME; j--;) {/* skip filename */ if (path[j] == '/') break; path[j] = '\0'; } snprintf(skinname, MD2_MAX_SKINNAME, "%s%s", path, &skinnameRelative[1]); } _pico_setfext(skinname, ""); picoShader = PicoNewShader(picoModel); if (picoShader == NULL) { _pico_printf(PICO_ERROR, "Unable to allocate a new model shader"); PicoFreeModel(picoModel); return NULL; } PicoSetShaderName(picoShader, skinname); } // allocate new pico surface picoSurface = PicoNewSurface( picoModel ); if( picoSurface == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); PicoFreeModel( picoModel ); return NULL; } PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); PicoSetSurfaceName( picoSurface, frame->name ); picoShader = PicoNewShader( picoModel ); if( picoShader == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); PicoFreeModel( picoModel ); return NULL; } PicoSetShaderName( picoShader, skinname ); // associate current surface with newly created shader PicoSetSurfaceShader( picoSurface, picoShader ); // Init LUT for Verts p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * md2->numXYZ); for(i=0; inumXYZ; i++) { p_index_LUT[i].Vert = -1; p_index_LUT[i].ST = -1; } /* Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert. */ /* tot_numVerts = md2->numXYZ; -- silence compiler warning */ for (i = 0; i < md2->numTris; i++) { p_md2Triangle = (md2Triangle_t *) (bb + md2->ofsTris + (sizeof(md2Triangle_t) * i)); for (j = 0; j < 3; j++) { if (p_index_LUT[p_md2Triangle->index_xyz[j]].ST == -1) /* No Main Entry */ p_index_LUT[p_md2Triangle->index_xyz[j]].ST = p_md2Triangle->index_st[j]; } } /* Build Picomodel */ triangle = (md2Triangle_t *) ((picoByte_t *) (bb + md2->ofsTris)); for (j = 0; j < md2->numTris; j++, triangle++) { PicoSetSurfaceIndex(picoSurface, j * 3, triangle->index_xyz[0]); PicoSetSurfaceIndex(picoSurface, j * 3 + 1, triangle->index_xyz[1]); PicoSetSurfaceIndex(picoSurface, j * 3 + 2, triangle->index_xyz[2]); } _pico_set_color(color, 255, 255, 255, 255); texCoord = (md2St_t*) ((picoByte_t *) (bb + md2->ofsST)); vertex = (md2XyzNormal_t*) ((picoByte_t*) (frame->verts)); for (i = 0; i < md2->numXYZ; i++, vertex++) { /* set vertex origin */ xyz[0] = vertex->v[0] * frame->scale[0] + frame->translate[0]; xyz[1] = vertex->v[1] * frame->scale[1] + frame->translate[1]; xyz[2] = vertex->v[2] * frame->scale[2] + frame->translate[2]; PicoSetSurfaceXYZ(picoSurface, i, xyz); /* set normal */ normal[0] = md2_normals[vertex->lightnormalindex][0]; normal[1] = md2_normals[vertex->lightnormalindex][1]; normal[2] = md2_normals[vertex->lightnormalindex][2]; PicoSetSurfaceNormal(picoSurface, i, normal); /* set st coords */ st[0] = (float) texCoord[p_index_LUT[i].ST].s / (float) md2->skinWidth; st[1] = (float) texCoord[p_index_LUT[i].ST].t / (float) md2->skinHeight; PicoSetSurfaceST(picoSurface, 0, i, st); /* set color */ PicoSetSurfaceColor(picoSurface, 0, i, color); } /* Free malloc'ed LUTs */ _pico_free(p_index_LUT); /* return the new pico model */ return picoModel; } /* pico file format module definition */ const picoModule_t picoModuleMD2 = { "0.875", /* module version string */ "Quake 2 MD2", /* module display name */ "Nurail", /* author's name */ "2003 Nurail", /* module copyright */ { "md2", NULL, NULL, NULL /* default extensions to use */ }, _md2_canload, /* validation routine */ _md2_load, /* load routine */ NULL, /* save validation routine */ NULL /* save routine */ }; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/pm_md3.c�����������������������������������������������������������0000664�0000000�0000000�00000030304�13217505464�0020140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define PM_MD3_C /* dependencies */ #include "picointernal.h" /* md3 model format */ static const char* MD3_MAGIC = "IDP3"; #define MD3_VERSION 15 /* md3 vertex scale */ #define MD3_SCALE (1.0f / 64.0f) /* md3 model frame information */ typedef struct md3Frame_s { float bounds[ 2 ][ 3 ]; float localOrigin[ 3 ]; float radius; char creator[ 16 ]; } md3Frame_t; /* md3 model tag information */ typedef struct md3Tag_s { char name[ 64 ]; float origin[ 3 ]; float axis[ 3 ][ 3 ]; } md3Tag_t; /* md3 surface md3 (one object mesh) */ typedef struct md3Surface_s { char magic[ 4 ]; char name[ 64 ]; /* polyset name */ int flags; int numFrames; /* all model surfaces should have the same */ int numShaders; /* all model surfaces should have the same */ int numVerts; int numTriangles; int ofsTriangles; int ofsShaders; /* offset from start of md3Surface_t */ int ofsSt; /* texture coords are common for all frames */ int ofsVertexes; /* numVerts * numFrames */ int ofsEnd; /* next surface follows */ } md3Surface_t; typedef struct md3Shader_s { char name[ 64 ]; int shaderIndex; /* for ingame use */ } md3Shader_t; typedef struct md3Triangle_s { int indexes[ 3 ]; } md3Triangle_t; typedef struct md3TexCoord_s { float st[ 2 ]; } md3TexCoord_t; typedef struct md3Vertex_s { short xyz[ 3 ]; short normal; } md3Vertex_t; /* md3 model file md3 structure */ typedef struct md3_s { char magic[ 4 ]; /* MD3_MAGIC */ int version; char name[ 64 ]; /* model name */ int flags; int numFrames; int numTags; int numSurfaces; int numSkins; /* number of skins for the mesh */ int ofsFrames; /* offset for first frame */ int ofsTags; /* numFrames * numTags */ int ofsSurfaces; /* first surface, others follow */ int ofsEnd; /* end of file */ } md3_t; /* _md3_canload() validates a quake3 arena md3 model file. btw, i use the preceding underscore cause it's a static func referenced by one structure only. */ static int _md3_canload( PM_PARAMS_CANLOAD ) { md3_t *md3; /* to keep the compiler happy */ *fileName = *fileName; /* sanity check */ if( bufSize < ( sizeof( *md3 ) * 2) ) return PICO_PMV_ERROR_SIZE; /* set as md3 */ md3 = (md3_t*) buffer; /* check md3 magic */ if (md3->magic[0] != MD3_MAGIC[0] || md3->magic[1] != MD3_MAGIC[1] || md3->magic[2] != MD3_MAGIC[2] || md3->magic[3] != MD3_MAGIC[3]) { return PICO_PMV_ERROR_IDENT; } /* check md3 version */ if( _pico_little_long( md3->version ) != MD3_VERSION ) return PICO_PMV_ERROR_VERSION; /* file seems to be a valid md3 */ return PICO_PMV_OK; } /* _md3_load() loads a quake3 arena md3 model file. */ static picoModel_t *_md3_load( PM_PARAMS_LOAD ) { int i, j; picoByte_t *bb; md3_t *md3; md3Surface_t *surface; md3Shader_t *shader; md3TexCoord_t *texCoord; md3Frame_t *frame; md3Triangle_t *triangle; md3Vertex_t *vertex; double lat, lng; picoModel_t *picoModel; picoSurface_t *picoSurface; picoShader_t *picoShader; picoVec3_t xyz, normal; picoVec2_t st; picoColor_t color; /* ------------------------------------------------- md3 loading ------------------------------------------------- */ /* set as md3 */ bb = (picoByte_t*) buffer; md3 = (md3_t*) buffer; /* check ident and version */ if (md3->magic[0] != MD3_MAGIC[0] || md3->magic[1] != MD3_MAGIC[1] || md3->magic[2] != MD3_MAGIC[2] || md3->magic[3] != MD3_MAGIC[3] || _pico_little_long( md3->version ) != MD3_VERSION) { /* not an md3 file (todo: set error) */ return NULL; } /* swap md3; sea: swaps fixed */ md3->version = _pico_little_long( md3->version ); md3->numFrames = _pico_little_long( md3->numFrames ); md3->numTags = _pico_little_long( md3->numTags ); md3->numSurfaces = _pico_little_long( md3->numSurfaces ); md3->numSkins = _pico_little_long( md3->numSkins ); md3->ofsFrames = _pico_little_long( md3->ofsFrames ); md3->ofsTags = _pico_little_long( md3->ofsTags ); md3->ofsSurfaces = _pico_little_long( md3->ofsSurfaces ); md3->ofsEnd = _pico_little_long( md3->ofsEnd ); /* do frame check */ if( md3->numFrames < 1 ) { _pico_printf( PICO_ERROR, "MD3 with 0 frames" ); return NULL; } if( frameNum < 0 || frameNum >= md3->numFrames ) { _pico_printf( PICO_ERROR, "Invalid or out-of-range MD3 frame specified" ); return NULL; } /* swap frames */ frame = (md3Frame_t*) (bb + md3->ofsFrames ); for( i = 0; i < md3->numFrames; i++, frame++ ) { frame->radius = _pico_little_float( frame->radius ); for( j = 0; j < 3; j++ ) { frame->bounds[ 0 ][ j ] = _pico_little_float( frame->bounds[ 0 ][ j ] ); frame->bounds[ 1 ][ j ] = _pico_little_float( frame->bounds[ 1 ][ j ] ); frame->localOrigin[ j ] = _pico_little_float( frame->localOrigin[ j ] ); } } /* swap surfaces */ surface = (md3Surface_t*) (bb + md3->ofsSurfaces); for( i = 0; i < md3->numSurfaces; i++ ) { /* swap surface md3; sea: swaps fixed */ surface->flags = _pico_little_long( surface->flags ); surface->numFrames = _pico_little_long( surface->numFrames ); surface->numShaders = _pico_little_long( surface->numShaders ); surface->numTriangles = _pico_little_long( surface->numTriangles ); surface->ofsTriangles = _pico_little_long( surface->ofsTriangles ); surface->numVerts = _pico_little_long( surface->numVerts ); surface->ofsShaders = _pico_little_long( surface->ofsShaders ); surface->ofsSt = _pico_little_long( surface->ofsSt ); surface->ofsVertexes = _pico_little_long( surface->ofsVertexes ); surface->ofsEnd = _pico_little_long( surface->ofsEnd ); /* swap triangles */ triangle = (md3Triangle_t*) ((picoByte_t*) surface + surface->ofsTriangles); for( j = 0; j < surface->numTriangles; j++, triangle++ ) { /* sea: swaps fixed */ triangle->indexes[ 0 ] = _pico_little_long( triangle->indexes[ 0 ] ); triangle->indexes[ 1 ] = _pico_little_long( triangle->indexes[ 1 ] ); triangle->indexes[ 2 ] = _pico_little_long( triangle->indexes[ 2 ] ); } /* swap st coords */ texCoord = (md3TexCoord_t*) ((picoByte_t*) surface + surface->ofsSt); for( j = 0; j < surface->numVerts; j++, texCoord++ ) { texCoord->st[ 0 ] = _pico_little_float( texCoord->st[ 0 ] ); texCoord->st[ 1 ] = _pico_little_float( texCoord->st[ 1 ] ); } /* swap xyz/normals */ vertex = (md3Vertex_t*) ((picoByte_t*) surface + surface->ofsVertexes); for( j = 0; j < (surface->numVerts * surface->numFrames); j++, vertex++) { vertex->xyz[ 0 ] = _pico_little_short( vertex->xyz[ 0 ] ); vertex->xyz[ 1 ] = _pico_little_short( vertex->xyz[ 1 ] ); vertex->xyz[ 2 ] = _pico_little_short( vertex->xyz[ 2 ] ); vertex->normal = _pico_little_short( vertex->normal ); } /* get next surface */ surface = (md3Surface_t*) ((picoByte_t*) surface + surface->ofsEnd); } /* ------------------------------------------------- pico model creation ------------------------------------------------- */ /* create new pico model */ picoModel = PicoNewModel(); if( picoModel == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); return NULL; } /* do model setup */ PicoSetModelFrameNum( picoModel, frameNum ); PicoSetModelNumFrames( picoModel, md3->numFrames ); /* sea */ PicoSetModelName( picoModel, fileName ); PicoSetModelFileName( picoModel, fileName ); /* md3 surfaces become picomodel surfaces */ surface = (md3Surface_t*) (bb + md3->ofsSurfaces); /* run through md3 surfaces */ for( i = 0; i < md3->numSurfaces; i++ ) { /* allocate new pico surface */ picoSurface = PicoNewSurface( picoModel ); if( picoSurface == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); PicoFreeModel( picoModel ); /* sea */ return NULL; } /* md3 model surfaces are all triangle meshes */ PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); /* set surface name */ PicoSetSurfaceName( picoSurface, surface->name ); /* create new pico shader -sea */ picoShader = PicoNewShader( picoModel ); if( picoShader == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); PicoFreeModel( picoModel ); return NULL; } /* detox and set shader name */ shader = (md3Shader_t*) ((picoByte_t*) surface + surface->ofsShaders); _pico_setfext( shader->name, "" ); _pico_unixify( shader->name ); PicoSetShaderName( picoShader, shader->name ); /* associate current surface with newly created shader */ PicoSetSurfaceShader( picoSurface, picoShader ); /* copy indexes */ triangle = (md3Triangle_t *) ((picoByte_t*) surface + surface->ofsTriangles); for( j = 0; j < surface->numTriangles; j++, triangle++ ) { PicoSetSurfaceIndex( picoSurface, (j * 3 + 0), (picoIndex_t) triangle->indexes[ 0 ] ); PicoSetSurfaceIndex( picoSurface, (j * 3 + 1), (picoIndex_t) triangle->indexes[ 1 ] ); PicoSetSurfaceIndex( picoSurface, (j * 3 + 2), (picoIndex_t) triangle->indexes[ 2 ] ); } /* copy vertexes */ texCoord = (md3TexCoord_t*) ((picoByte_t *) surface + surface->ofsSt); vertex = (md3Vertex_t*) ((picoByte_t*) surface + surface->ofsVertexes + surface->numVerts * frameNum * sizeof( md3Vertex_t ) ); _pico_set_color( color, 255, 255, 255, 255 ); for( j = 0; j < surface->numVerts; j++, texCoord++, vertex++ ) { /* set vertex origin */ xyz[ 0 ] = MD3_SCALE * vertex->xyz[ 0 ]; xyz[ 1 ] = MD3_SCALE * vertex->xyz[ 1 ]; xyz[ 2 ] = MD3_SCALE * vertex->xyz[ 2 ]; PicoSetSurfaceXYZ( picoSurface, j, xyz ); /* decode lat/lng normal to 3 float normal */ lat = (float) ((vertex->normal >> 8) & 0xff); lng = (float) (vertex->normal & 0xff); lat *= PICO_PI / 128; lng *= PICO_PI / 128; normal[ 0 ] = (picoVec_t) cos( lat ) * (picoVec_t) sin( lng ); normal[ 1 ] = (picoVec_t) sin( lat ) * (picoVec_t) sin( lng ); normal[ 2 ] = (picoVec_t) cos( lng ); PicoSetSurfaceNormal( picoSurface, j, normal ); /* set st coords */ st[ 0 ] = texCoord->st[ 0 ]; st[ 1 ] = texCoord->st[ 1 ]; PicoSetSurfaceST( picoSurface, 0, j, st ); /* set color */ PicoSetSurfaceColor( picoSurface, 0, j, color ); } /* get next surface */ surface = (md3Surface_t*) ((picoByte_t*) surface + surface->ofsEnd); } /* return the new pico model */ return picoModel; } /* pico file format module definition */ const picoModule_t picoModuleMD3 = { "1.3", /* module version string */ "Quake 3 Arena", /* module display name */ "Randy Reddig", /* author's name */ "2002 Randy Reddig", /* module copyright */ { "md3", NULL, NULL, NULL /* default extensions to use */ }, _md3_canload, /* validation routine */ _md3_load, /* load routine */ NULL, /* save validation routine */ NULL /* save routine */ }; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������DarkRadiant-2.5.0/libs/picomodel/pm_mdc.c�����������������������������������������������������������0000664�0000000�0000000�00000060000�13217505464�0020214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define PM_MDC_C /* dependencies */ #include "picointernal.h" /* mdc model format */ static const char* MDC_MAGIC = "IDPC"; #define MDC_VERSION 2 /* mdc vertex scale */ #define MDC_SCALE (1.0f / 64.0f) #define MDC_MAX_OFS 127.0f #define MDC_DIST_SCALE 0.05f /* mdc decoding normal table */ double mdcNormals[ 256 ][ 3 ] = { { 1.000000, 0.000000, 0.000000 }, { 0.980785, 0.195090, 0.000000 }, { 0.923880, 0.382683, 0.000000 }, { 0.831470, 0.555570, 0.000000 }, { 0.707107, 0.707107, 0.000000 }, { 0.555570, 0.831470, 0.000000 }, { 0.382683, 0.923880, 0.000000 }, { 0.195090, 0.980785, 0.000000 }, { -0.000000, 1.000000, 0.000000 }, { -0.195090, 0.980785, 0.000000 }, { -0.382683, 0.923880, 0.000000 }, { -0.555570, 0.831470, 0.000000 }, { -0.707107, 0.707107, 0.000000 }, { -0.831470, 0.555570, 0.000000 }, { -0.923880, 0.382683, 0.000000 }, { -0.980785, 0.195090, 0.000000 }, { -1.000000, -0.000000, 0.000000 }, { -0.980785, -0.195090, 0.000000 }, { -0.923880, -0.382683, 0.000000 }, { -0.831470, -0.555570, 0.000000 }, { -0.707107, -0.707107, 0.000000 }, { -0.555570, -0.831469, 0.000000 }, { -0.382684, -0.923880, 0.000000 }, { -0.195090, -0.980785, 0.000000 }, { 0.000000, -1.000000, 0.000000 }, { 0.195090, -0.980785, 0.000000 }, { 0.382684, -0.923879, 0.000000 }, { 0.555570, -0.831470, 0.000000 }, { 0.707107, -0.707107, 0.000000 }, { 0.831470, -0.555570, 0.000000 }, { 0.923880, -0.382683, 0.000000 }, { 0.980785, -0.195090, 0.000000 }, { 0.980785, 0.000000, -0.195090 }, { 0.956195, 0.218245, -0.195090 }, { 0.883657, 0.425547, -0.195090 }, { 0.766809, 0.611510, -0.195090 }, { 0.611510, 0.766809, -0.195090 }, { 0.425547, 0.883657, -0.195090 }, { 0.218245, 0.956195, -0.195090 }, { -0.000000, 0.980785, -0.195090 }, { -0.218245, 0.956195, -0.195090 }, { -0.425547, 0.883657, -0.195090 }, { -0.611510, 0.766809, -0.195090 }, { -0.766809, 0.611510, -0.195090 }, { -0.883657, 0.425547, -0.195090 }, { -0.956195, 0.218245, -0.195090 }, { -0.980785, -0.000000, -0.195090 }, { -0.956195, -0.218245, -0.195090 }, { -0.883657, -0.425547, -0.195090 }, { -0.766809, -0.611510, -0.195090 }, { -0.611510, -0.766809, -0.195090 }, { -0.425547, -0.883657, -0.195090 }, { -0.218245, -0.956195, -0.195090 }, { 0.000000, -0.980785, -0.195090 }, { 0.218245, -0.956195, -0.195090 }, { 0.425547, -0.883657, -0.195090 }, { 0.611510, -0.766809, -0.195090 }, { 0.766809, -0.611510, -0.195090 }, { 0.883657, -0.425547, -0.195090 }, { 0.956195, -0.218245, -0.195090 }, { 0.923880, 0.000000, -0.382683 }, { 0.892399, 0.239118, -0.382683 }, { 0.800103, 0.461940, -0.382683 }, { 0.653281, 0.653281, -0.382683 }, { 0.461940, 0.800103, -0.382683 }, { 0.239118, 0.892399, -0.382683 }, { -0.000000, 0.923880, -0.382683 }, { -0.239118, 0.892399, -0.382683 }, { -0.461940, 0.800103, -0.382683 }, { -0.653281, 0.653281, -0.382683 }, { -0.800103, 0.461940, -0.382683 }, { -0.892399, 0.239118, -0.382683 }, { -0.923880, -0.000000, -0.382683 }, { -0.892399, -0.239118, -0.382683 }, { -0.800103, -0.461940, -0.382683 }, { -0.653282, -0.653281, -0.382683 }, { -0.461940, -0.800103, -0.382683 }, { -0.239118, -0.892399, -0.382683 }, { 0.000000, -0.923880, -0.382683 }, { 0.239118, -0.892399, -0.382683 }, { 0.461940, -0.800103, -0.382683 }, { 0.653281, -0.653282, -0.382683 }, { 0.800103, -0.461940, -0.382683 }, { 0.892399, -0.239117, -0.382683 }, { 0.831470, 0.000000, -0.555570 }, { 0.790775, 0.256938, -0.555570 }, { 0.672673, 0.488726, -0.555570 }, { 0.488726, 0.672673, -0.555570 }, { 0.256938, 0.790775, -0.555570 }, { -0.000000, 0.831470, -0.555570 }, { -0.256938, 0.790775, -0.555570 }, { -0.488726, 0.672673, -0.555570 }, { -0.672673, 0.488726, -0.555570 }, { -0.790775, 0.256938, -0.555570 }, { -0.831470, -0.000000, -0.555570 }, { -0.790775, -0.256938, -0.555570 }, { -0.672673, -0.488726, -0.555570 }, { -0.488725, -0.672673, -0.555570 }, { -0.256938, -0.790775, -0.555570 }, { 0.000000, -0.831470, -0.555570 }, { 0.256938, -0.790775, -0.555570 }, { 0.488725, -0.672673, -0.555570 }, { 0.672673, -0.488726, -0.555570 }, { 0.790775, -0.256938, -0.555570 }, { 0.707107, 0.000000, -0.707107 }, { 0.653281, 0.270598, -0.707107 }, { 0.500000, 0.500000, -0.707107 }, { 0.270598, 0.653281, -0.707107 }, { -0.000000, 0.707107, -0.707107 }, { -0.270598, 0.653282, -0.707107 }, { -0.500000, 0.500000, -0.707107 }, { -0.653281, 0.270598, -0.707107 }, { -0.707107, -0.000000, -0.707107 }, { -0.653281, -0.270598, -0.707107 }, { -0.500000, -0.500000, -0.707107 }, { -0.270598, -0.653281, -0.707107 }, { 0.000000, -0.707107, -0.707107 }, { 0.270598, -0.653281, -0.707107 }, { 0.500000, -0.500000, -0.707107 }, { 0.653282, -0.270598, -0.707107 }, { 0.555570, 0.000000, -0.831470 }, { 0.481138, 0.277785, -0.831470 }, { 0.277785, 0.481138, -0.831470 }, { -0.000000, 0.555570, -0.831470 }, { -0.277785, 0.481138, -0.831470 }, { -0.481138, 0.277785, -0.831470 }, { -0.555570, -0.000000, -0.831470 }, { -0.481138, -0.277785, -0.831470 }, { -0.277785, -0.481138, -0.831470 }, { 0.000000, -0.555570, -0.831470 }, { 0.277785, -0.481138, -0.831470 }, { 0.481138, -0.277785, -0.831470 }, { 0.382683, 0.000000, -0.923880 }, { 0.270598, 0.270598, -0.923880 }, { -0.000000, 0.382683, -0.923880 }, { -0.270598, 0.270598, -0.923880 }, { -0.382683, -0.000000, -0.923880 }, { -0.270598, -0.270598, -0.923880 }, { 0.000000, -0.382683, -0.923880 }, { 0.270598, -0.270598, -0.923880 }, { 0.195090, 0.000000, -0.980785 }, { -0.000000, 0.195090, -0.980785 }, { -0.195090, -0.000000, -0.980785 }, { 0.000000, -0.195090, -0.980785 }, { 0.980785, 0.000000, 0.195090 }, { 0.956195, 0.218245, 0.195090 }, { 0.883657, 0.425547, 0.195090 }, { 0.766809, 0.611510, 0.195090 }, { 0.611510, 0.766809, 0.195090 }, { 0.425547, 0.883657, 0.195090 }, { 0.218245, 0.956195, 0.195090 }, { -0.000000, 0.980785, 0.195090 }, { -0.218245, 0.956195, 0.195090 }, { -0.425547, 0.883657, 0.195090 }, { -0.611510, 0.766809, 0.195090 }, { -0.766809, 0.611510, 0.195090 }, { -0.883657, 0.425547, 0.195090 }, { -0.956195, 0.218245, 0.195090 }, { -0.980785, -0.000000, 0.195090 }, { -0.956195, -0.218245, 0.195090 }, { -0.883657, -0.425547, 0.195090 }, { -0.766809, -0.611510, 0.195090 }, { -0.611510, -0.766809, 0.195090 }, { -0.425547, -0.883657, 0.195090 }, { -0.218245, -0.956195, 0.195090 }, { 0.000000, -0.980785, 0.195090 }, { 0.218245, -0.956195, 0.195090 }, { 0.425547, -0.883657, 0.195090 }, { 0.611510, -0.766809, 0.195090 }, { 0.766809, -0.611510, 0.195090 }, { 0.883657, -0.425547, 0.195090 }, { 0.956195, -0.218245, 0.195090 }, { 0.923880, 0.000000, 0.382683 }, { 0.892399, 0.239118, 0.382683 }, { 0.800103, 0.461940, 0.382683 }, { 0.653281, 0.653281, 0.382683 }, { 0.461940, 0.800103, 0.382683 }, { 0.239118, 0.892399, 0.382683 }, { -0.000000, 0.923880, 0.382683 }, { -0.239118, 0.892399, 0.382683 }, { -0.461940, 0.800103, 0.382683 }, { -0.653281, 0.653281, 0.382683 }, { -0.800103, 0.461940, 0.382683 }, { -0.892399, 0.239118, 0.382683 }, { -0.923880, -0.000000, 0.382683 }, { -0.892399, -0.239118, 0.382683 }, { -0.800103, -0.461940, 0.382683 }, { -0.653282, -0.653281, 0.382683 }, { -0.461940, -0.800103, 0.382683 }, { -0.239118, -0.892399, 0.382683 }, { 0.000000, -0.923880, 0.382683 }, { 0.239118, -0.892399, 0.382683 }, { 0.461940, -0.800103, 0.382683 }, { 0.653281, -0.653282, 0.382683 }, { 0.800103, -0.461940, 0.382683 }, { 0.892399, -0.239117, 0.382683 }, { 0.831470, 0.000000, 0.555570 }, { 0.790775, 0.256938, 0.555570 }, { 0.672673, 0.488726, 0.555570 }, { 0.488726, 0.672673, 0.555570 }, { 0.256938, 0.790775, 0.555570 }, { -0.000000, 0.831470, 0.555570 }, { -0.256938, 0.790775, 0.555570 }, { -0.488726, 0.672673, 0.555570 }, { -0.672673, 0.488726, 0.555570 }, { -0.790775, 0.256938, 0.555570 }, { -0.831470, -0.000000, 0.555570 }, { -0.790775, -0.256938, 0.555570 }, { -0.672673, -0.488726, 0.555570 }, { -0.488725, -0.672673, 0.555570 }, { -0.256938, -0.790775, 0.555570 }, { 0.000000, -0.831470, 0.555570 }, { 0.256938, -0.790775, 0.555570 }, { 0.488725, -0.672673, 0.555570 }, { 0.672673, -0.488726, 0.555570 }, { 0.790775, -0.256938, 0.555570 }, { 0.707107, 0.000000, 0.707107 }, { 0.653281, 0.270598, 0.707107 }, { 0.500000, 0.500000, 0.707107 }, { 0.270598, 0.653281, 0.707107 }, { -0.000000, 0.707107, 0.707107 }, { -0.270598, 0.653282, 0.707107 }, { -0.500000, 0.500000, 0.707107 }, { -0.653281, 0.270598, 0.707107 }, { -0.707107, -0.000000, 0.707107 }, { -0.653281, -0.270598, 0.707107 }, { -0.500000, -0.500000, 0.707107 }, { -0.270598, -0.653281, 0.707107 }, { 0.000000, -0.707107, 0.707107 }, { 0.270598, -0.653281, 0.707107 }, { 0.500000, -0.500000, 0.707107 }, { 0.653282, -0.270598, 0.707107 }, { 0.555570, 0.000000, 0.831470 }, { 0.481138, 0.277785, 0.831470 }, { 0.277785, 0.481138, 0.831470 }, { -0.000000, 0.555570, 0.831470 }, { -0.277785, 0.481138, 0.831470 }, { -0.481138, 0.277785, 0.831470 }, { -0.555570, -0.000000, 0.831470 }, { -0.481138, -0.277785, 0.831470 }, { -0.277785, -0.481138, 0.831470 }, { 0.000000, -0.555570, 0.831470 }, { 0.277785, -0.481138, 0.831470 }, { 0.481138, -0.277785, 0.831470 }, { 0.382683, 0.000000, 0.923880 }, { 0.270598, 0.270598, 0.923880 }, { -0.000000, 0.382683, 0.923880 }, { -0.270598, 0.270598, 0.923880 }, { -0.382683, -0.000000, 0.923880 }, { -0.270598, -0.270598, 0.923880 }, { 0.000000, -0.382683, 0.923880 }, { 0.270598, -0.270598, 0.923880 }, { 0.195090, 0.000000, 0.980785 }, { -0.000000, 0.195090, 0.980785 }, { -0.195090, -0.000000, 0.980785 }, { 0.000000, -0.195090, 0.980785 } }; /* mdc model frame information */ typedef struct mdcFrame_s { float bounds[ 2 ][ 3 ]; float localOrigin[ 3 ]; float radius; char creator[ 16 ]; } mdcFrame_t; /* mdc model tag information */ typedef struct mdcTag_s { short xyz[3]; short angles[3]; } mdcTag_t; /* mdc surface mdc (one object mesh) */ typedef struct mdcSurface_s { char magic[ 4 ]; char name[ 64 ]; /* polyset name */ int flags; int numCompFrames; /* all surfaces in a model should have the same */ int numBaseFrames; /* ditto */ int numShaders; /* all model surfaces should have the same */ int numVerts; int numTriangles; int ofsTriangles; int ofsShaders; /* offset from start of mdcSurface_t */ int ofsSt; /* texture coords are common for all frames */ int ofsXyzNormals; /* numVerts * numBaseFrames */ int ofsXyzCompressed; /* numVerts * numCompFrames */ int ofsFrameBaseFrames; /* numFrames */ int ofsFrameCompFrames; /* numFrames */ int ofsEnd; /* next surface follows */ } mdcSurface_t; typedef struct mdcShader_s { char name[ 64 ]; int shaderIndex; /* for ingame use */ } mdcShader_t; typedef struct mdcTriangle_s { int indexes[ 3 ]; } mdcTriangle_t; typedef struct mdcTexCoord_s { float st[ 2 ]; } mdcTexCoord_t; typedef struct mdcVertex_s { short xyz[ 3 ]; short normal; } mdcVertex_t; typedef struct mdcXyzCompressed_s { unsigned int ofsVec; /* offset direction from the last base frame */ } mdcXyzCompressed_t; /* mdc model file mdc structure */ typedef struct mdc_s { char magic[ 4 ]; /* MDC_MAGIC */ int version; char name[ 64 ]; /* model name */ int flags; int numFrames; int numTags; int numSurfaces; int numSkins; /* number of skins for the mesh */ int ofsFrames; /* offset for first frame */ int ofsTagNames; /* numTags */ int ofsTags; /* numFrames * numTags */ int ofsSurfaces; /* first surface, others follow */ int ofsEnd; /* end of file */ } mdc_t; /* _mdc_canload() validates a Return to Castle Wolfenstein model file. btw, i use the preceding underscore cause it's a static func referenced by one structure only. */ static int _mdc_canload( PM_PARAMS_CANLOAD ) { mdc_t *mdc; /* to keep the compiler happy */ *fileName = *fileName; /* sanity check */ if( bufSize < ( sizeof( *mdc ) * 2) ) return PICO_PMV_ERROR_SIZE; /* set as mdc */ mdc = (mdc_t*) buffer; /* check mdc magic */ if (mdc->magic[0] != MDC_MAGIC[0] || mdc->magic[1] != MDC_MAGIC[1] || mdc->magic[2] != MDC_MAGIC[2] || mdc->magic[3] != MDC_MAGIC[3]) { return PICO_PMV_ERROR_IDENT; } /* check mdc version */ if( _pico_little_long( mdc->version ) != MDC_VERSION ) return PICO_PMV_ERROR_VERSION; /* file seems to be a valid mdc */ return PICO_PMV_OK; } /* _mdc_load() loads a Return to Castle Wolfenstein mdc model file. */ static picoModel_t *_mdc_load( PM_PARAMS_LOAD ) { int i, j; picoByte_t *bb; mdc_t *mdc; mdcSurface_t *surface; mdcShader_t *shader; mdcTexCoord_t *texCoord; mdcFrame_t *frame; mdcTriangle_t *triangle; mdcVertex_t *vertex; mdcXyzCompressed_t *vertexComp; short *mdcShort, *mdcCompVert; double lat, lng; picoModel_t *picoModel; picoSurface_t *picoSurface; picoShader_t *picoShader; picoVec3_t xyz, normal; picoVec2_t st; picoColor_t color; /* ------------------------------------------------- mdc loading ------------------------------------------------- */ vertexComp = NULL; mdcCompVert = NULL; /* set as mdc */ bb = (picoByte_t*) buffer; mdc = (mdc_t*) buffer; /* check ident and version */ if (mdc->magic[0] != MDC_MAGIC[0] || mdc->magic[1] != MDC_MAGIC[1] || mdc->magic[2] != MDC_MAGIC[2] || mdc->magic[3] != MDC_MAGIC[3] || _pico_little_long( mdc->version ) != MDC_VERSION) { /* not an mdc file (todo: set error) */ return NULL; } /* swap mdc */ mdc->version = _pico_little_long( mdc->version ); mdc->numFrames = _pico_little_long( mdc->numFrames ); mdc->numTags = _pico_little_long( mdc->numTags ); mdc->numSurfaces = _pico_little_long( mdc->numSurfaces ); mdc->numSkins = _pico_little_long( mdc->numSkins ); mdc->ofsFrames = _pico_little_long( mdc->ofsFrames ); mdc->ofsTags = _pico_little_long( mdc->ofsTags ); mdc->ofsTagNames = _pico_little_long( mdc->ofsTagNames ); mdc->ofsSurfaces = _pico_little_long( mdc->ofsSurfaces ); mdc->ofsEnd = _pico_little_long( mdc->ofsEnd ); /* do frame check */ if( mdc->numFrames < 1 ) { _pico_printf( PICO_ERROR, "MDC with 0 frames" ); return NULL; } if( frameNum < 0 || frameNum >= mdc->numFrames ) { _pico_printf( PICO_ERROR, "Invalid or out-of-range MDC frame specified" ); return NULL; } /* swap frames */ frame = (mdcFrame_t*) (bb + mdc->ofsFrames ); for( i = 0; i < mdc->numFrames; i++, frame++ ) { frame->radius = _pico_little_float( frame->radius ); for( j = 0; j < 3; j++ ) { frame->bounds[ 0 ][ j ] = _pico_little_float( frame->bounds[ 0 ][ j ] ); frame->bounds[ 1 ][ j ] = _pico_little_float( frame->bounds[ 1 ][ j ] ); frame->localOrigin[ j ] = _pico_little_float( frame->localOrigin[ j ] ); } } /* swap surfaces */ surface = (mdcSurface_t*) (bb + mdc->ofsSurfaces); for( i = 0; i < mdc->numSurfaces; i++ ) { /* swap surface mdc */ surface->flags = _pico_little_long( surface->flags ); surface->numBaseFrames = _pico_little_long( surface->numBaseFrames ); surface->numCompFrames = _pico_little_long( surface->numCompFrames ); surface->numShaders = _pico_little_long( surface->numShaders ); surface->numTriangles = _pico_little_long( surface->numTriangles ); surface->ofsTriangles = _pico_little_long( surface->ofsTriangles ); surface->numVerts = _pico_little_long( surface->numVerts ); surface->ofsShaders = _pico_little_long( surface->ofsShaders ); surface->ofsSt = _pico_little_long( surface->ofsSt ); surface->ofsXyzNormals = _pico_little_long( surface->ofsXyzNormals ); surface->ofsXyzCompressed = _pico_little_long( surface->ofsXyzCompressed ); surface->ofsFrameBaseFrames = _pico_little_long( surface->ofsFrameBaseFrames ); surface->ofsFrameCompFrames = _pico_little_long( surface->ofsFrameCompFrames ); surface->ofsEnd = _pico_little_long( surface->ofsEnd ); /* swap triangles */ triangle = (mdcTriangle_t*) ((picoByte_t*) surface + surface->ofsTriangles); for( j = 0; j < surface->numTriangles; j++, triangle++ ) { /* sea: swaps fixed */ triangle->indexes[ 0 ] = _pico_little_long( triangle->indexes[ 0 ] ); triangle->indexes[ 1 ] = _pico_little_long( triangle->indexes[ 1 ] ); triangle->indexes[ 2 ] = _pico_little_long( triangle->indexes[ 2 ] ); } /* swap st coords */ texCoord = (mdcTexCoord_t*) ((picoByte_t*) surface + surface->ofsSt); for( j = 0; j < surface->numVerts; j++, texCoord++ ) { texCoord->st[ 0 ] = _pico_little_float( texCoord->st[ 0 ] ); texCoord->st[ 1 ] = _pico_little_float( texCoord->st[ 1 ] ); } /* swap xyz/normals */ vertex = (mdcVertex_t*) ((picoByte_t*) surface + surface->ofsXyzNormals); for( j = 0; j < (surface->numVerts * surface->numBaseFrames); j++, vertex++) { vertex->xyz[ 0 ] = _pico_little_short( vertex->xyz[ 0 ] ); vertex->xyz[ 1 ] = _pico_little_short( vertex->xyz[ 1 ] ); vertex->xyz[ 2 ] = _pico_little_short( vertex->xyz[ 2 ] ); vertex->normal = _pico_little_short( vertex->normal ); } /* swap xyz/compressed */ vertexComp = (mdcXyzCompressed_t*) ((picoByte_t*) surface + surface->ofsXyzCompressed); for( j = 0; j < (surface->numVerts * surface->numCompFrames); j++, vertexComp++) { vertexComp->ofsVec = _pico_little_long( vertexComp->ofsVec ); } /* swap base frames */ mdcShort = (short *) ((picoByte_t*) surface + surface->ofsFrameBaseFrames); for( j = 0; j < mdc->numFrames; j++, mdcShort++) { *mdcShort = _pico_little_short( *mdcShort ); } /* swap compressed frames */ mdcShort = (short *) ((picoByte_t*) surface + surface->ofsFrameCompFrames); for( j = 0; j < mdc->numFrames; j++, mdcShort++) { *mdcShort = _pico_little_short( *mdcShort ); } /* get next surface */ surface = (mdcSurface_t*) ((picoByte_t*) surface + surface->ofsEnd); } /* ------------------------------------------------- pico model creation ------------------------------------------------- */ /* create new pico model */ picoModel = PicoNewModel(); if( picoModel == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); return NULL; } /* do model setup */ PicoSetModelFrameNum( picoModel, frameNum ); PicoSetModelNumFrames( picoModel, mdc->numFrames ); /* sea */ PicoSetModelName( picoModel, fileName ); PicoSetModelFileName( picoModel, fileName ); /* mdc surfaces become picomodel surfaces */ surface = (mdcSurface_t*) (bb + mdc->ofsSurfaces); /* run through mdc surfaces */ for( i = 0; i < mdc->numSurfaces; i++ ) { /* allocate new pico surface */ picoSurface = PicoNewSurface( picoModel ); if( picoSurface == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); PicoFreeModel( picoModel ); /* sea */ return NULL; } /* mdc model surfaces are all triangle meshes */ PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); /* set surface name */ PicoSetSurfaceName( picoSurface, surface->name ); /* create new pico shader -sea */ picoShader = PicoNewShader( picoModel ); if( picoShader == NULL ) { _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); PicoFreeModel( picoModel ); return NULL; } /* detox and set shader name */ shader = (mdcShader_t*) ((picoByte_t*) surface + surface->ofsShaders); _pico_setfext( shader->name, "" ); _pico_unixify( shader->name ); PicoSetShaderName( picoShader, shader->name ); /* associate current surface with newly created shader */ PicoSetSurfaceShader( picoSurface, picoShader ); /* copy indexes */ triangle = (mdcTriangle_t *) ((picoByte_t*) surface + surface->ofsTriangles); for( j = 0; j < surface->numTriangles; j++, triangle++ ) { PicoSetSurfaceIndex( picoSurface, (j * 3 + 0), (picoIndex_t) triangle->indexes[ 0 ] ); PicoSetSurfaceIndex( picoSurface, (j * 3 + 1), (picoIndex_t) triangle->indexes[ 1 ] ); PicoSetSurfaceIndex( picoSurface, (j * 3 + 2), (picoIndex_t) triangle->indexes[ 2 ] ); } /* copy vertexes */ texCoord = (mdcTexCoord_t*) ((picoByte_t *) surface + surface->ofsSt); mdcShort = (short *) ((picoByte_t *) surface + surface->ofsXyzNormals) + ((int)*((short *) ((picoByte_t *) surface + surface->ofsFrameBaseFrames) + frameNum) * surface->numVerts * 4); if( surface->numCompFrames > 0 ) { mdcCompVert = (short *) ((picoByte_t *) surface + surface->ofsFrameCompFrames) + frameNum; if( *mdcCompVert >= 0 ) vertexComp = (mdcXyzCompressed_t *) ((picoByte_t *) surface + surface->ofsXyzCompressed) + (*mdcCompVert * surface->numVerts); } _pico_set_color( color, 255, 255, 255, 255 ); for( j = 0; j < surface->numVerts; j++, texCoord++, mdcShort+=4 ) { /* set vertex origin */ xyz[ 0 ] = MDC_SCALE * mdcShort[ 0 ]; xyz[ 1 ] = MDC_SCALE * mdcShort[ 1 ]; xyz[ 2 ] = MDC_SCALE * mdcShort[ 2 ]; /* add compressed ofsVec */ if( surface->numCompFrames > 0 && *mdcCompVert >= 0 ) { xyz[ 0 ] += ((float) ((vertexComp->ofsVec) & 255) - MDC_MAX_OFS) * MDC_DIST_SCALE; xyz[ 1 ] += ((float) ((vertexComp->ofsVec >> 8) & 255) - MDC_MAX_OFS) * MDC_DIST_SCALE; xyz[ 2 ] += ((float) ((vertexComp->ofsVec >> 16) & 255) - MDC_MAX_OFS) * MDC_DIST_SCALE; PicoSetSurfaceXYZ( picoSurface, j, xyz ); normal[ 0 ] = (float) mdcNormals[ (vertexComp->ofsVec >> 24) ][ 0 ]; normal[ 1 ] = (float) mdcNormals[ (vertexComp->ofsVec >> 24) ][ 1 ]; normal[ 2 ] = (float) mdcNormals[ (vertexComp->ofsVec >> 24) ][ 2 ]; PicoSetSurfaceNormal( picoSurface, j, normal ); vertexComp++; } else { PicoSetSurfaceXYZ( picoSurface, j, xyz ); /* decode lat/lng normal to 3 float normal */ lat = (float) ((*(mdcShort + 3) >> 8) & 0xff); lng = (float) (*(mdcShort + 3) & 0xff); lat *= PICO_PI / 128; lng *= PICO_PI / 128; normal[ 0 ] = (picoVec_t) cos( lat ) * (picoVec_t) sin( lng ); normal[ 1 ] = (picoVec_t) sin( lat ) * (picoVec_t) sin( lng ); normal[ 2 ] = (picoVec_t) cos( lng ); PicoSetSurfaceNormal( picoSurface, j, normal ); } /* set st coords */ st[ 0 ] = texCoord->st[ 0 ]; st[ 1 ] = texCoord->st[ 1 ]; PicoSetSurfaceST( picoSurface, 0, j, st ); /* set color */ PicoSetSurfaceColor( picoSurface, 0, j, color ); } /* get next surface */ surface = (mdcSurface_t*) ((picoByte_t*) surface + surface->ofsEnd); } /* return the new pico model */ return picoModel; } /* pico file format module definition */ const picoModule_t picoModuleMDC = { "1.3", /* module version string */ "RtCW MDC", /* module display name */ "Arnout van Meer", /* author's name */ "2002 Arnout van Meer", /* module copyright */ { "mdc", NULL, NULL, NULL /* default extensions to use */ }, _mdc_canload, /* validation routine */ _mdc_load, /* load routine */ NULL, /* save validation routine */ NULL /* save routine */ }; DarkRadiant-2.5.0/libs/picomodel/pm_ms3d.c����������������������������������������������������������0000664�0000000�0000000�00000031703�13217505464�0020327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ----------------------------------------------------------------------------- PicoModel Library Copyright (c) 2002, Randy Reddig & seaw0lf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ /* marker */ #define PM_MS3D_C /* dependencies */ #include "picointernal.h" /* disable warnings */ #ifdef WIN32 #pragma warning( disable:4100 ) /* unref param */ #endif /* remarks: * - loader seems stable * todo: * - fix uv coordinate problem * - check for buffer overflows ('bufptr' accesses) */ /* uncomment when debugging this module */ #define DEBUG_PM_MS3D #define DEBUG_PM_MS3D_EX /* plain white */ static picoColor_t white = { 255,255,255,255 }; /* ms3d limits */ #define MS3D_MAX_VERTS 8192 #define MS3D_MAX_TRIS 16384 #define MS3D_MAX_GROUPS 128 #define MS3D_MAX_MATERIALS 128 #define MS3D_MAX_JOINTS 128 #define MS3D_MAX_KEYFRAMES 216 /* ms3d flags */ #define MS3D_SELECTED 1 #define MS3D_HIDDEN 2 #define MS3D_SELECTED2 4 #define MS3D_DIRTY 8 /* this freaky loader needs byte alignment */ #pragma pack(push, 1) /* ms3d header */ typedef struct SMsHeader { char magic[10]; int version; } TMsHeader; /* ms3d vertex */ typedef struct SMsVertex { unsigned char flags; /* sel, sel2, or hidden */ float xyz[3]; char boneID; /* -1 means 'no bone' */ unsigned char refCount; } TMsVertex; /* ms3d triangle */ typedef struct SMsTriangle { unsigned short flags; /* sel, sel2, or hidden */ unsigned short vertexIndices[3]; float vertexNormals[3][3]; float s[3]; float t[3]; unsigned char smoothingGroup; /* 1 - 32 */ unsigned char groupIndex; } TMsTriangle; /* ms3d material */ typedef struct SMsMaterial { char name[32]; float ambient[4]; float diffuse[4]; float specular[4]; float emissive[4]; float shininess; /* range 0..128 */ float transparency; /* range 0..1 */ unsigned char mode; char texture [128]; /* texture.bmp */ char alphamap[128]; /* alpha.bmp */ } TMsMaterial; // ms3d group (static part) // followed by a variable size block (see below) typedef struct SMsGroup { unsigned char flags; // sel, hidden char name[32]; unsigned short numTriangles; /* unsigned short triangleIndices[ numTriangles ]; char materialIndex; // -1 means 'no material' */ } TMsGroup; // ms3d joint typedef struct SMsJoint { unsigned char flags; char name[32]; char parentName[32]; float rotation[3]; float translation[3]; unsigned short numRotationKeyframes; unsigned short numTranslationKeyframes; } TMsJoint; // ms3d keyframe typedef struct SMsKeyframe { float time; float parameter[3]; } TMsKeyframe; /* restore previous data alignment */ #pragma pack(pop) /* _ms3d_canload: * validates a milkshape3d model file. */ static int _ms3d_canload( PM_PARAMS_CANLOAD ) { TMsHeader *hdr; /* to keep the compiler happy */ *fileName = *fileName; /* sanity check */ if (bufSize < sizeof(TMsHeader)) return PICO_PMV_ERROR_SIZE; /* get ms3d header */ hdr = (TMsHeader *)buffer; /* check ms3d magic */ if (strncmp(hdr->magic,"MS3D000000",10) != 0) return PICO_PMV_ERROR_IDENT; /* check ms3d version */ if (_pico_little_long(hdr->version) < 3 || _pico_little_long(hdr->version) > 4) { _pico_printf( PICO_ERROR,"MS3D file ignored. Only MS3D 1.3 and 1.4 is supported." ); return PICO_PMV_ERROR_VERSION; } /* file seems to be a valid ms3d */ return PICO_PMV_OK; } static unsigned char *GetWord( unsigned char *bufptr, int *out ) { if (bufptr == NULL) { *out = 0; return NULL; } *out = _pico_little_short( *(unsigned short *)bufptr ); return( bufptr + 2 ); } /* _ms3d_load: * loads a milkshape3d model file. */ static picoModel_t *_ms3d_load( PM_PARAMS_LOAD ) { picoModel_t *model; unsigned char *bufptr; int shaderRefs[ MS3D_MAX_GROUPS ]; int numGroups; int numMaterials; // unsigned char *ptrToGroups; int numVerts; unsigned char *ptrToVerts; int numTris; unsigned char *ptrToTris; int i,k,m; /* create new pico model */ model = PicoNewModel(); if (model == NULL) return NULL; /* do model setup */ PicoSetModelFrameNum( model, frameNum ); PicoSetModelName( model, fileName ); PicoSetModelFileName( model, fileName ); /* skip header */ bufptr = (unsigned char *)buffer + sizeof(TMsHeader); /* get number of vertices */ bufptr = GetWord( bufptr,&numVerts ); ptrToVerts = bufptr; #ifdef DEBUG_PM_MS3D printf("NumVertices: %d\n",numVerts); #endif /* swap verts */ for (i=0; ixyz[ 0 ] = _pico_little_float( vertex->xyz[ 0 ] ); vertex->xyz[ 1 ] = _pico_little_float( vertex->xyz[ 1 ] ); vertex->xyz[ 2 ] = _pico_little_float( vertex->xyz[ 2 ] ); #ifdef DEBUG_PM_MS3D_EX_ printf("Vertex: x: %f y: %f z: %f\n", msvd[i]->vertex[0], msvd[i]->vertex[1], msvd[i]->vertex[2]); #endif } /* get number of triangles */ bufptr = GetWord( bufptr,&numTris ); ptrToTris = bufptr; #ifdef DEBUG_PM_MS3D printf("NumTriangles: %d\n",numTris); #endif /* swap tris */ for (i=0; iflags = _pico_little_short( triangle->flags ); /* run through all tri verts */ for (k=0; k<3; k++) { /* swap tex coords */ triangle->s[ k ] = _pico_little_float( triangle->s[ k ] ); triangle->t[ k ] = _pico_little_float( triangle->t[ k ] ); /* swap fields */ triangle->vertexIndices[ k ] = _pico_little_short( triangle->vertexIndices[ k ] ); triangle->vertexNormals[ 0 ][ k ] = _pico_little_float( triangle->vertexNormals[ 0 ][ k ] ); triangle->vertexNormals[ 1 ][ k ] = _pico_little_float( triangle->vertexNormals[ 1 ][ k ] ); triangle->vertexNormals[ 2 ][ k ] = _pico_little_float( triangle->vertexNormals[ 2 ][ k ] ); /* check for out of range indices */ if (triangle->vertexIndices[ k ] >= numVerts) { _pico_printf( PICO_ERROR,"Vertex %d index %d out of range (%d, max %d)",i,k,triangle->vertexIndices[k],numVerts-1); PicoFreeModel( model ); return NULL; /* yuck */ } } } /* get number of groups */ bufptr = GetWord( bufptr,&numGroups ); // ptrToGroups = bufptr; #ifdef DEBUG_PM_MS3D printf("NumGroups: %d\n",numGroups); #endif /* run through all groups in model */ for (i=0; i {}` where U can be anything template class Base, typename T> #if !defined(_MSC_VER) using is_template_base_of = decltype(is_template_base_of_impl::check((remove_cv_t*)nullptr)); #else // MSVC2015 has trouble with decltype in template aliases struct is_template_base_of : decltype(is_template_base_of_impl::check((remove_cv_t*)nullptr)) { }; #endif /// Check if T is std::shared_ptr where U can be anything template struct is_shared_ptr : std::false_type { }; template struct is_shared_ptr> : std::true_type { }; /// Ignore that a variable is unused in compiler warnings inline void ignore_unused(const int *) { } NAMESPACE_END(detail) /// Returns a named pointer that is shared among all extension modules (using the same /// pybind11 version) running in the current interpreter. Names starting with underscores /// are reserved for internal usage. Returns `nullptr` if no matching entry was found. inline PYBIND11_NOINLINE void* get_shared_data(const std::string& name) { auto& internals = detail::get_internals(); auto it = internals.shared_data.find(name); return it != internals.shared_data.end() ? it->second : nullptr; } /// Set the shared data that can be later recovered by `get_shared_data()`. inline PYBIND11_NOINLINE void *set_shared_data(const std::string& name, void *data) { detail::get_internals().shared_data[name] = data; return data; } /// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if /// such entry exists. Otherwise, a new object of default-constructible type `T` is /// added to the shared data under the given name and a reference to it is returned. template T& get_or_create_shared_data(const std::string& name) { auto& internals = detail::get_internals(); auto it = internals.shared_data.find(name); T* ptr = (T*) (it != internals.shared_data.end() ? it->second : nullptr); if (!ptr) { ptr = new T(); internals.shared_data[name] = ptr; } return *ptr; } /// Fetch and hold an error which was already set in Python class error_already_set : public std::runtime_error { public: error_already_set() : std::runtime_error(detail::error_string()) { PyErr_Fetch(&type, &value, &trace); } error_already_set(const error_already_set &) = delete; error_already_set(error_already_set &&e) : std::runtime_error(e.what()), type(e.type), value(e.value), trace(e.trace) { e.type = e.value = e.trace = nullptr; } inline ~error_already_set(); // implementation in pybind11.h error_already_set& operator=(const error_already_set &) = delete; /// Give the error back to Python void restore() { PyErr_Restore(type, value, trace); type = value = trace = nullptr; } /// Clear the held Python error state (the C++ `what()` message remains intact) void clear() { restore(); PyErr_Clear(); } private: PyObject *type, *value, *trace; }; /// C++ bindings of builtin Python exceptions class builtin_exception : public std::runtime_error { public: using std::runtime_error::runtime_error; /// Set the error using the Python C API virtual void set_error() const = 0; }; #define PYBIND11_RUNTIME_EXCEPTION(name, type) \ class name : public builtin_exception { public: \ using builtin_exception::builtin_exception; \ name() : name("") { } \ void set_error() const override { PyErr_SetString(type, what()); } \ }; PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally [[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } [[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } template struct format_descriptor { }; NAMESPACE_BEGIN(detail) // Returns the index of the given type in the type char array below, and in the list in numpy.h // The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; // complex float,double,long double. Note that the long double types only participate when long // double is actually longer than double (it isn't under MSVC). // NB: not only the string below but also complex.h and numpy.h rely on this order. template struct is_fmt_numeric { static constexpr bool value = false; }; template struct is_fmt_numeric::value>> { static constexpr bool value = true; static constexpr int index = std::is_same::value ? 0 : 1 + ( std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); }; NAMESPACE_END(detail) template struct format_descriptor::value>> { static constexpr const char c = "?bBhHiIqQfdgFDG"[detail::is_fmt_numeric::index]; static constexpr const char value[2] = { c, '\0' }; static std::string format() { return std::string(1, c); } }; template constexpr const char format_descriptor< T, detail::enable_if_t::value>>::value[2]; NAMESPACE_BEGIN(detail) template struct compare_buffer_info { static bool compare(const buffer_info& b) { return b.format == format_descriptor::format() && b.itemsize == sizeof(T); } }; template struct compare_buffer_info::value>> { static bool compare(const buffer_info& b) { return b.itemsize == sizeof(T) && (b.format == format_descriptor::value || ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); } }; NAMESPACE_END(detail) /// RAII wrapper that temporarily clears any Python error state struct error_scope { PyObject *type, *value, *trace; error_scope() { PyErr_Fetch(&type, &value, &trace); } ~error_scope() { PyErr_Restore(type, value, trace); } }; /// Dummy destructor wrapper that can be used to expose classes with a private destructor struct nodelete { template void operator()(T*) { } }; // overload_cast requires variable templates: C++14 or MSVC #if defined(PYBIND11_CPP14) || defined(_MSC_VER) #define PYBIND11_OVERLOAD_CAST 1 NAMESPACE_BEGIN(detail) template struct overload_cast_impl { template constexpr auto operator()(Return (*pf)(Args...)) const noexcept -> decltype(pf) { return pf; } template constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept -> decltype(pmf) { return pmf; } template constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept -> decltype(pmf) { return pmf; } }; NAMESPACE_END(detail) /// Syntax sugar for resolving overloaded function pointers: /// - regular: static_cast(&Class::func) /// - sweet: overload_cast(&Class::func) template static constexpr detail::overload_cast_impl overload_cast = {}; // MSVC 2015 only accepts this particular initialization syntax for this variable template. /// Const member function selector for overload_cast /// - regular: static_cast(&Class::func) /// - sweet: overload_cast(&Class::func, const_) static constexpr auto const_ = std::true_type{}; #endif // overload_cast NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/pybind/pybind11/complex.h000066400000000000000000000027751321750546400214110ustar00rootroot00000000000000/* pybind11/complex.h: Complex number support Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "pybind11.h" #include /// glibc defines I as a macro which breaks things, e.g., boost template names #ifdef I # undef I #endif NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) // The format codes are already in the string in common.h, we just need to provide a specialization template struct is_fmt_numeric> { static constexpr bool value = true; static constexpr int index = is_fmt_numeric::index + 3; }; template class type_caster> { public: bool load(handle src, bool convert) { if (!src) return false; if (!convert && !PyComplex_Check(src.ptr())) return false; Py_complex result = PyComplex_AsCComplex(src.ptr()); if (result.real == -1.0 && PyErr_Occurred()) { PyErr_Clear(); return false; } value = std::complex((T) result.real, (T) result.imag); return true; } static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); } PYBIND11_TYPE_CASTER(std::complex, _("complex")); }; NAMESPACE_END(detail) NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/pybind/pybind11/descr.h000066400000000000000000000167621321750546400210430ustar00rootroot00000000000000/* pybind11/descr.h: Helper type for concatenating type signatures either at runtime (C++11) or compile time (C++14) Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "common.h" NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) #if defined(PYBIND11_CPP14) /* Concatenate type signatures at compile time using C++14 */ template class descr { template friend class descr; public: constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) : descr(text, types, make_index_sequence(), make_index_sequence()) { } constexpr const char *text() const { return m_text; } constexpr const std::type_info * const * types() const { return m_types; } template constexpr descr operator+(const descr &other) const { return concat(other, make_index_sequence(), make_index_sequence(), make_index_sequence(), make_index_sequence()); } protected: template constexpr descr( char const (&text) [Size1+1], const std::type_info * const (&types) [Size2+1], index_sequence, index_sequence) : m_text{text[Indices1]..., '\0'}, m_types{types[Indices2]..., nullptr } {} template constexpr descr concat(const descr &other, index_sequence, index_sequence, index_sequence, index_sequence) const { return descr( { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' }, { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr } ); } protected: char m_text[Size1 + 1]; const std::type_info * m_types[Size2 + 1]; }; template constexpr descr _(char const(&text)[Size]) { return descr(text, { nullptr }); } template struct int_to_str : int_to_str { }; template struct int_to_str<0, Digits...> { static constexpr auto digits = descr({ ('0' + Digits)..., '\0' }, { nullptr }); }; // Ternary description (like std::conditional) template constexpr enable_if_t> _(char const(&text1)[Size1], char const(&)[Size2]) { return _(text1); } template constexpr enable_if_t> _(char const(&)[Size1], char const(&text2)[Size2]) { return _(text2); } template constexpr enable_if_t> _(descr d, descr) { return d; } template constexpr enable_if_t> _(descr, descr d) { return d; } template auto constexpr _() -> decltype(int_to_str::digits) { return int_to_str::digits; } template constexpr descr<1, 1> _() { return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); } inline constexpr descr<0, 0> concat() { return _(""); } template auto constexpr concat(descr descr) { return descr; } template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } #define PYBIND11_DESCR constexpr auto #else /* Simpler C++11 implementation based on run-time memory allocation and copying */ class descr { public: PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { size_t nChars = len(text), nTypes = len(types); m_text = new char[nChars]; m_types = new const std::type_info *[nTypes]; memcpy(m_text, text, nChars * sizeof(char)); memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); } PYBIND11_NOINLINE descr friend operator+(descr &&d1, descr &&d2) { descr r; size_t nChars1 = len(d1.m_text), nTypes1 = len(d1.m_types); size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); r.m_text = new char[nChars1 + nChars2 - 1]; r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; memcpy(r.m_text, d1.m_text, (nChars1-1) * sizeof(char)); memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); memcpy(r.m_types, d1.m_types, (nTypes1-1) * sizeof(std::type_info *)); memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); delete[] d1.m_text; delete[] d1.m_types; delete[] d2.m_text; delete[] d2.m_types; return r; } char *text() { return m_text; } const std::type_info * * types() { return m_types; } protected: PYBIND11_NOINLINE descr() { } template static size_t len(const T *ptr) { // return length including null termination const T *it = ptr; while (*it++ != (T) 0) ; return static_cast(it - ptr); } const std::type_info **m_types = nullptr; char *m_text = nullptr; }; /* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ PYBIND11_NOINLINE inline descr _(const char *text) { const std::type_info *types[1] = { nullptr }; return descr(text, types); } template PYBIND11_NOINLINE enable_if_t _(const char *text1, const char *) { return _(text1); } template PYBIND11_NOINLINE enable_if_t _(char const *, const char *text2) { return _(text2); } template PYBIND11_NOINLINE enable_if_t _(descr d, descr) { return d; } template PYBIND11_NOINLINE enable_if_t _(descr, descr d) { return d; } template PYBIND11_NOINLINE descr _() { const std::type_info *types[2] = { &typeid(Type), nullptr }; return descr("%", types); } template PYBIND11_NOINLINE descr _() { const std::type_info *types[1] = { nullptr }; return descr(std::to_string(Size).c_str(), types); } PYBIND11_NOINLINE inline descr concat() { return _(""); } PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } #define PYBIND11_DESCR ::pybind11::detail::descr #endif NAMESPACE_END(detail) NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/pybind/pybind11/eigen.h000066400000000000000000000670751321750546400210350ustar00rootroot00000000000000/* pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "numpy.h" #if defined(__INTEL_COMPILER) # pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) #elif defined(__GNUG__) || defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wdeprecated-declarations" # if __GNUC__ >= 7 # pragma GCC diagnostic ignored "-Wint-in-bool-context" # endif #endif #include #include #if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant #endif // Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit // move constructors that break things. We could detect this an explicitly copy, but an extra copy // of matrices seems highly undesirable. static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); NAMESPACE_BEGIN(pybind11) // Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: using EigenDStride = Eigen::Stride; template using EigenDRef = Eigen::Ref; template using EigenDMap = Eigen::Map; NAMESPACE_BEGIN(detail) #if EIGEN_VERSION_AT_LEAST(3,3,0) using EigenIndex = Eigen::Index; #else using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; #endif // Matches Eigen::Map, Eigen::Ref, blocks, etc: template using is_eigen_dense_map = all_of, std::is_base_of, T>>; template using is_eigen_mutable_map = std::is_base_of, T>; template using is_eigen_dense_plain = all_of>, is_template_base_of>; template using is_eigen_sparse = is_template_base_of; // Test for objects inheriting from EigenBase that aren't captured by the above. This // basically covers anything that can be assigned to a dense matrix but that don't have a typical // matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and // SelfAdjointView fall into this category. template using is_eigen_other = all_of< is_template_base_of, negation, is_eigen_dense_plain, is_eigen_sparse>> >; // Captures numpy/eigen conformability status (returned by EigenProps::conformable()): template struct EigenConformable { bool conformable = false; EigenIndex rows = 0, cols = 0; EigenDStride stride{0, 0}; EigenConformable(bool fits = false) : conformable{fits} {} // Matrix type: EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride) : conformable{true}, rows{r}, cols{c}, stride(EigenRowMajor ? rstride : cstride /* outer stride */, EigenRowMajor ? cstride : rstride /* inner stride */) {} // Vector type: EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} template bool stride_compatible() const { // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || (EigenRowMajor ? cols : rows) == 1) && (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || (EigenRowMajor ? rows : cols) == 1); } operator bool() const { return conformable; } }; template struct eigen_extract_stride { using type = Type; }; template struct eigen_extract_stride> { using type = StrideType; }; template struct eigen_extract_stride> { using type = StrideType; }; // Helper struct for extracting information from an Eigen type template struct EigenProps { using Type = Type_; using Scalar = typename Type::Scalar; using StrideType = typename eigen_extract_stride::type; static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime, size = Type::SizeAtCompileTime; static constexpr bool row_major = Type::IsRowMajor, vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic, fixed = size != Eigen::Dynamic, // Fully-fixed size dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size template using if_zero = std::integral_constant; static constexpr EigenIndex inner_stride = if_zero::value, outer_stride = if_zero::value; static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; // Takes an input array and determines whether we can make it fit into the Eigen type. If // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). static EigenConformable conformable(const array &a) { const auto dims = a.ndim(); if (dims < 1 || dims > 2) return false; if (dims == 2) { // Matrix type: require exact match (or dynamic) EigenIndex np_rows = a.shape(0), np_cols = a.shape(1), np_rstride = a.strides(0) / sizeof(Scalar), np_cstride = a.strides(1) / sizeof(Scalar); if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) return false; return {np_rows, np_cols, np_rstride, np_cstride}; } // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever // is used, we want the (single) numpy stride value. const EigenIndex n = a.shape(0), stride = a.strides(0) / sizeof(Scalar); if (vector) { // Eigen type is a compile-time vector if (fixed && size != n) return false; // Vector size mismatch return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; } else if (fixed) { // The type has a fixed size, but is not a vector: abort return false; } else if (fixed_cols) { // Since this isn't a vector, cols must be != 1. We allow this only if it exactly // equals the number of elements (rows is Dynamic, and so 1 row is allowed). if (cols != n) return false; return {1, n, stride}; } else { // Otherwise it's either fully dynamic, or column dynamic; both become a column vector if (fixed_rows && rows != n) return false; return {n, 1, stride}; } } static PYBIND11_DESCR descriptor() { constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; constexpr bool show_order = is_eigen_dense_map::value; constexpr bool show_c_contiguous = show_order && requires_row_major; constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; return _("numpy.ndarray[") + npy_format_descriptor::name() + _("[") + _(_<(size_t) rows>(), _("m")) + _(", ") + _(_<(size_t) cols>(), _("n")) + _("]") + // For a reference type (e.g. Ref) we have other constraints that might need to be // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you // *gave* a numpy.ndarray of the right type and dimensions. _(", flags.writeable", "") + _(", flags.c_contiguous", "") + _(", flags.f_contiguous", "") + _("]"); } }; // Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, // otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { constexpr size_t elem_size = sizeof(typename props::Scalar); std::vector shape, strides; if (props::vector) { shape.push_back(src.size()); strides.push_back(elem_size * src.innerStride()); } else { shape.push_back(src.rows()); shape.push_back(src.cols()); strides.push_back(elem_size * src.rowStride()); strides.push_back(elem_size * src.colStride()); } array a(std::move(shape), std::move(strides), src.data(), base); if (!writeable) array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; return a.release(); } // Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that // reference the Eigen object's data with `base` as the python-registered base class (if omitted, // the base will be set to None, and lifetime management is up to the caller). The numpy array is // non-writeable if the given type is const. template handle eigen_ref_array(Type &src, handle parent = none()) { // none here is to get past array's should-we-copy detection, which currently always // copies when there is no base. Setting the base to None should be harmless. return eigen_array_cast(src, parent, !std::is_const::value); } // Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy // array that references the encapsulated data with a python-side reference to the capsule to tie // its destruction to that of any dependent python objects. Const-ness is determined by whether or // not the Type of the pointer given is const. template ::value>> handle eigen_encapsulate(Type *src) { capsule base(src, [](void *o) { delete static_cast(o); }); return eigen_ref_array(*src, base); } // Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense // types. template struct type_caster::value>> { using Scalar = typename Type::Scalar; using props = EigenProps; bool load(handle src, bool) { auto buf = array_t::ensure(src); if (!buf) return false; auto dims = buf.ndim(); if (dims < 1 || dims > 2) return false; auto fits = props::conformable(buf); if (!fits) return false; // Non-comformable vector/matrix types value = Eigen::Map(buf.data(), fits.rows, fits.cols, fits.stride); return true; } private: // Cast implementation template static handle cast_impl(CType *src, return_value_policy policy, handle parent) { switch (policy) { case return_value_policy::take_ownership: case return_value_policy::automatic: return eigen_encapsulate(src); case return_value_policy::move: return eigen_encapsulate(new CType(std::move(*src))); case return_value_policy::copy: return eigen_array_cast(*src); case return_value_policy::reference: case return_value_policy::automatic_reference: return eigen_ref_array(*src); case return_value_policy::reference_internal: return eigen_ref_array(*src, parent); default: throw cast_error("unhandled return_value_policy: should not happen!"); }; } public: // Normal returned non-reference, non-const value: static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { return cast_impl(&src, return_value_policy::move, parent); } // If you return a non-reference const, we mark the numpy array readonly: static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { return cast_impl(&src, return_value_policy::move, parent); } // lvalue reference return; default (automatic) becomes copy static handle cast(Type &src, return_value_policy policy, handle parent) { if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) policy = return_value_policy::copy; return cast_impl(&src, policy, parent); } // const lvalue reference return; default (automatic) becomes copy static handle cast(const Type &src, return_value_policy policy, handle parent) { if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) policy = return_value_policy::copy; return cast(&src, policy, parent); } // non-const pointer return static handle cast(Type *src, return_value_policy policy, handle parent) { return cast_impl(src, policy, parent); } // const pointer return static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast_impl(src, policy, parent); } static PYBIND11_DESCR name() { return type_descr(props::descriptor()); } operator Type*() { return &value; } operator Type&() { return value; } template using cast_op_type = cast_op_type; private: Type value; }; // Eigen Ref/Map classes have slightly different policy requirements, meaning we don't want to force // `move` when a Ref/Map rvalue is returned; we treat Ref<> sort of like a pointer (we care about // the underlying data, not the outer shell). template struct return_value_policy_override::value>> { static return_value_policy policy(return_value_policy p) { return p; } }; // Base class for casting reference/map/block/etc. objects back to python. template struct eigen_map_caster { private: using props = EigenProps; public: // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has // to stay around), but we'll allow it under the assumption that you know what you're doing (and // have an appropriate keep_alive in place). We return a numpy array pointing directly at the // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note // that this means you need to ensure you don't destroy the object in some other way (e.g. with // an appropriate keep_alive, or with a reference to a statically allocated matrix). static handle cast(const MapType &src, return_value_policy policy, handle parent) { switch (policy) { case return_value_policy::copy: return eigen_array_cast(src); case return_value_policy::reference_internal: return eigen_array_cast(src, parent, is_eigen_mutable_map::value); case return_value_policy::reference: case return_value_policy::automatic: case return_value_policy::automatic_reference: return eigen_array_cast(src, none(), is_eigen_mutable_map::value); default: // move, take_ownership don't make any sense for a ref/map: pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); } } static PYBIND11_DESCR name() { return props::descriptor(); } // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return // types but not bound arguments). We still provide them (with an explicitly delete) so that // you end up here if you try anyway. bool load(handle, bool) = delete; operator MapType() = delete; template using cast_op_type = MapType; }; // We can return any map-like object (but can only load Refs, specialized next): template struct type_caster::value>> : eigen_map_caster {}; // Loader for Ref<...> arguments. See the documentation for info on how to make this work without // copying (it requires some extra effort in many cases). template struct type_caster< Eigen::Ref, enable_if_t>::value> > : public eigen_map_caster> { private: using Type = Eigen::Ref; using props = EigenProps; using Scalar = typename props::Scalar; using MapType = Eigen::Map; using Array = array_t; static constexpr bool need_writeable = is_eigen_mutable_map::value; // Delay construction (these have no default constructor) std::unique_ptr map; std::unique_ptr ref; // Our array. When possible, this is just a numpy array pointing to the source data, but // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible // layout, or is an array of a type that needs to be converted). Using a numpy temporary // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and // storage order conversion. (Note that we refuse to use this temporary copy when loading an // argument for a Ref with M non-const, i.e. a read-write reference). Array copy_or_ref; public: bool load(handle src, bool convert) { // First check whether what we have is already an array of the right type. If not, we can't // avoid a copy (because the copy is also going to do type conversion). bool need_copy = !isinstance(src); EigenConformable fits; if (!need_copy) { // We don't need a converting copy, but we also need to check whether the strides are // compatible with the Ref's stride requirements Array aref = reinterpret_borrow(src); if (aref && (!need_writeable || aref.writeable())) { fits = props::conformable(aref); if (!fits) return false; // Incompatible dimensions if (!fits.template stride_compatible()) need_copy = true; else copy_or_ref = std::move(aref); } else { need_copy = true; } } if (need_copy) { // We need to copy: If we need a mutable reference, or we're not supposed to convert // (either because we're in the no-convert overload pass, or because we're explicitly // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. if (!convert || need_writeable) return false; Array copy = Array::ensure(src); if (!copy) return false; fits = props::conformable(copy); if (!fits || !fits.template stride_compatible()) return false; copy_or_ref = std::move(copy); } ref.reset(); map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); ref.reset(new Type(*map)); return true; } operator Type*() { return ref.get(); } operator Type&() { return *ref; } template using cast_op_type = pybind11::detail::cast_op_type<_T>; private: template ::value, int> = 0> Scalar *data(Array &a) { return a.mutable_data(); } template ::value, int> = 0> const Scalar *data(Array &a) { return a.data(); } // Attempt to figure out a constructor of `Stride` that will work. // If both strides are fixed, use a default constructor: template using stride_ctor_default = bool_constant< S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && std::is_default_constructible::value>; // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like // Eigen::Stride, and use it: template using stride_ctor_dual = bool_constant< !stride_ctor_default::value && std::is_constructible::value>; // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use // it (passing whichever stride is dynamic). template using stride_ctor_outer = bool_constant< !any_of, stride_ctor_dual>::value && S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && std::is_constructible::value>; template using stride_ctor_inner = bool_constant< !any_of, stride_ctor_dual>::value && S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && std::is_constructible::value>; template ::value, int> = 0> static S make_stride(EigenIndex, EigenIndex) { return S(); } template ::value, int> = 0> static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } template ::value, int> = 0> static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } template ::value, int> = 0> static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } }; // type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not // EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). // load() is not supported, but we can cast them into the python domain by first copying to a // regular Eigen::Matrix, then casting that. template struct type_caster::value>> { protected: using Matrix = Eigen::Matrix; using props = EigenProps; public: static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { handle h = eigen_encapsulate(new Matrix(src)); return h; } static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } static PYBIND11_DESCR name() { return props::descriptor(); } // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return // types but not bound arguments). We still provide them (with an explicitly delete) so that // you end up here if you try anyway. bool load(handle, bool) = delete; operator Type() = delete; template using cast_op_type = Type; }; template struct type_caster::value>> { typedef typename Type::Scalar Scalar; typedef typename std::remove_reference().outerIndexPtr())>::type StorageIndex; typedef typename Type::Index Index; static constexpr bool rowMajor = Type::IsRowMajor; bool load(handle src, bool) { if (!src) return false; auto obj = reinterpret_borrow(src); object sparse_module = module::import("scipy.sparse"); object matrix_type = sparse_module.attr( rowMajor ? "csr_matrix" : "csc_matrix"); if (obj.get_type() != matrix_type.ptr()) { try { obj = matrix_type(obj); } catch (const error_already_set &) { return false; } } auto values = array_t((object) obj.attr("data")); auto innerIndices = array_t((object) obj.attr("indices")); auto outerIndices = array_t((object) obj.attr("indptr")); auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); auto nnz = obj.attr("nnz").cast(); if (!values || !innerIndices || !outerIndices) return false; value = Eigen::MappedSparseMatrix( shape[0].cast(), shape[1].cast(), nnz, outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); return true; } static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { const_cast(src).makeCompressed(); object matrix_type = module::import("scipy.sparse").attr( rowMajor ? "csr_matrix" : "csc_matrix"); array data((size_t) src.nonZeros(), src.valuePtr()); array outerIndices((size_t) (rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); array innerIndices((size_t) src.nonZeros(), src.innerIndexPtr()); return matrix_type( std::make_tuple(data, innerIndices, outerIndices), std::make_pair(src.rows(), src.cols()) ).release(); } PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") + npy_format_descriptor::name() + _("]")); }; NAMESPACE_END(detail) NAMESPACE_END(pybind11) #if defined(__GNUG__) || defined(__clang__) # pragma GCC diagnostic pop #elif defined(_MSC_VER) # pragma warning(pop) #endif DarkRadiant-2.5.0/libs/pybind/pybind11/eval.h000066400000000000000000000066221321750546400206640ustar00rootroot00000000000000/* pybind11/exec.h: Support for evaluating Python expressions and statements from strings and files Copyright (c) 2016 Klemens Morgenstern and Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #pragma once #include "pybind11.h" NAMESPACE_BEGIN(pybind11) enum eval_mode { /// Evaluate a string containing an isolated expression eval_expr, /// Evaluate a string containing a single statement. Returns \c none eval_single_statement, /// Evaluate a string containing a sequence of statement. Returns \c none eval_statements }; template object eval(str expr, object global = object(), object local = object()) { if (!global) { global = reinterpret_borrow(PyEval_GetGlobals()); if (!global) global = dict(); } if (!local) local = global; /* PyRun_String does not accept a PyObject / encoding specifier, this seems to be the only alternative */ std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; int start; switch (mode) { case eval_expr: start = Py_eval_input; break; case eval_single_statement: start = Py_single_input; break; case eval_statements: start = Py_file_input; break; default: pybind11_fail("invalid evaluation mode"); } PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); if (!result) throw error_already_set(); return reinterpret_steal(result); } template object eval_file(str fname, object global = object(), object local = object()) { if (!global) { global = reinterpret_borrow(PyEval_GetGlobals()); if (!global) global = dict(); } if (!local) local = global; int start; switch (mode) { case eval_expr: start = Py_eval_input; break; case eval_single_statement: start = Py_single_input; break; case eval_statements: start = Py_file_input; break; default: pybind11_fail("invalid evaluation mode"); } int closeFile = 1; std::string fname_str = (std::string) fname; #if PY_VERSION_HEX >= 0x03040000 FILE *f = _Py_fopen_obj(fname.ptr(), "r"); #elif PY_VERSION_HEX >= 0x03000000 FILE *f = _Py_fopen(fname.ptr(), "r"); #else /* No unicode support in open() :( */ auto fobj = reinterpret_steal(PyFile_FromString( const_cast(fname_str.c_str()), const_cast("r"))); FILE *f = nullptr; if (fobj) f = PyFile_AsFile(fobj.ptr()); closeFile = 0; #endif if (!f) { PyErr_Clear(); pybind11_fail("File \"" + fname_str + "\" could not be opened!"); } #if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), local.ptr()); (void) closeFile; #else PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile); #endif if (!result) throw error_already_set(); return reinterpret_steal(result); } NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/pybind/pybind11/functional.h000066400000000000000000000053141321750546400220740ustar00rootroot00000000000000/* pybind11/functional.h: std::function<> support Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "pybind11.h" #include NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) template struct type_caster> { using type = std::function; using retval_type = conditional_t::value, void_type, Return>; using function_type = Return (*) (Args...); public: bool load(handle src_, bool) { if (src_.is_none()) return true; src_ = detail::get_function(src_); if (!src_ || !PyCallable_Check(src_.ptr())) return false; /* When passing a C++ function as an argument to another C++ function via Python, every function call would normally involve a full C++ -> Python -> C++ roundtrip, which can be prohibitive. Here, we try to at least detect the case where the function is stateless (i.e. function pointer or lambda function without captured variables), in which case the roundtrip can be avoided. */ if (PyCFunction_Check(src_.ptr())) { auto c = reinterpret_borrow(PyCFunction_GET_SELF(src_.ptr())); auto rec = (function_record *) c; if (rec && rec->is_stateless && rec->data[1] == &typeid(function_type)) { struct capture { function_type f; }; value = ((capture *) &rec->data)->f; return true; } } auto src = reinterpret_borrow(src_); value = [src](Args... args) -> Return { gil_scoped_acquire acq; object retval(src(std::forward(args)...)); /* Visual studio 2015 parser issue: need parentheses around this expression */ return (retval.template cast()); }; return true; } template static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { if (!f_) return none().inc_ref(); auto result = f_.template target(); if (result) return cpp_function(*result, policy).release(); else return cpp_function(std::forward(f_), policy).release(); } PYBIND11_TYPE_CASTER(type, _("Callable[[") + argument_loader::arg_names() + _("], ") + make_caster::name() + _("]")); }; NAMESPACE_END(detail) NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/pybind/pybind11/numpy.h000066400000000000000000001536351321750546400211140ustar00rootroot00000000000000/* pybind11/numpy.h: Basic NumPy support, vectorize() wrapper Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "pybind11.h" #include "complex.h" #include #include #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant #endif /* This will be true on all flat address space platforms and allows us to reduce the whole npy_intp / size_t / Py_intptr_t business down to just size_t for all size and dimension types (e.g. shape, strides, indexing), instead of inflicting this upon the library user. */ static_assert(sizeof(size_t) == sizeof(Py_intptr_t), "size_t != Py_intptr_t"); NAMESPACE_BEGIN(pybind11) class array; // Forward declaration NAMESPACE_BEGIN(detail) template struct npy_format_descriptor; struct PyArrayDescr_Proxy { PyObject_HEAD PyObject *typeobj; char kind; char type; char byteorder; char flags; int type_num; int elsize; int alignment; char *subarray; PyObject *fields; PyObject *names; }; struct PyArray_Proxy { PyObject_HEAD char *data; int nd; ssize_t *dimensions; ssize_t *strides; PyObject *base; PyObject *descr; int flags; }; struct PyVoidScalarObject_Proxy { PyObject_VAR_HEAD char *obval; PyArrayDescr_Proxy *descr; int flags; PyObject *base; }; struct numpy_type_info { PyObject* dtype_ptr; std::string format_str; }; struct numpy_internals { std::unordered_map registered_dtypes; numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { auto it = registered_dtypes.find(std::type_index(tinfo)); if (it != registered_dtypes.end()) return &(it->second); if (throw_if_missing) pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); return nullptr; } template numpy_type_info *get_type_info(bool throw_if_missing = true) { return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); } }; inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { ptr = &get_or_create_shared_data("_numpy_internals"); } inline numpy_internals& get_numpy_internals() { static numpy_internals* ptr = nullptr; if (!ptr) load_numpy_internals(ptr); return *ptr; } struct npy_api { enum constants { NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, NPY_ARRAY_OWNDATA_ = 0x0004, NPY_ARRAY_FORCECAST_ = 0x0010, NPY_ARRAY_ENSUREARRAY_ = 0x0040, NPY_ARRAY_ALIGNED_ = 0x0100, NPY_ARRAY_WRITEABLE_ = 0x0400, NPY_BOOL_ = 0, NPY_BYTE_, NPY_UBYTE_, NPY_SHORT_, NPY_USHORT_, NPY_INT_, NPY_UINT_, NPY_LONG_, NPY_ULONG_, NPY_LONGLONG_, NPY_ULONGLONG_, NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, NPY_OBJECT_ = 17, NPY_STRING_, NPY_UNICODE_, NPY_VOID_ }; static npy_api& get() { static npy_api api = lookup(); return api; } bool PyArray_Check_(PyObject *obj) const { return (bool) PyObject_TypeCheck(obj, PyArray_Type_); } bool PyArrayDescr_Check_(PyObject *obj) const { return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); } PyObject *(*PyArray_DescrFromType_)(int); PyObject *(*PyArray_NewFromDescr_) (PyTypeObject *, PyObject *, int, Py_intptr_t *, Py_intptr_t *, void *, int, PyObject *); PyObject *(*PyArray_DescrNewFromType_)(int); PyObject *(*PyArray_NewCopy_)(PyObject *, int); PyTypeObject *PyArray_Type_; PyTypeObject *PyVoidArrType_Type_; PyTypeObject *PyArrayDescr_Type_; PyObject *(*PyArray_DescrFromScalar_)(PyObject *); PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); int (*PyArray_DescrConverter_) (PyObject *, PyObject **); bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, Py_ssize_t *, PyObject **, PyObject *); PyObject *(*PyArray_Squeeze_)(PyObject *); int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); private: enum functions { API_PyArray_Type = 2, API_PyArrayDescr_Type = 3, API_PyVoidArrType_Type = 39, API_PyArray_DescrFromType = 45, API_PyArray_DescrFromScalar = 57, API_PyArray_FromAny = 69, API_PyArray_NewCopy = 85, API_PyArray_NewFromDescr = 94, API_PyArray_DescrNewFromType = 9, API_PyArray_DescrConverter = 174, API_PyArray_EquivTypes = 182, API_PyArray_GetArrayParamsFromObject = 278, API_PyArray_Squeeze = 136, API_PyArray_SetBaseObject = 282 }; static npy_api lookup() { module m = module::import("numpy.core.multiarray"); auto c = m.attr("_ARRAY_API"); #if PY_MAJOR_VERSION >= 3 void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); #else void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); #endif npy_api api; #define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; DECL_NPY_API(PyArray_Type); DECL_NPY_API(PyVoidArrType_Type); DECL_NPY_API(PyArrayDescr_Type); DECL_NPY_API(PyArray_DescrFromType); DECL_NPY_API(PyArray_DescrFromScalar); DECL_NPY_API(PyArray_FromAny); DECL_NPY_API(PyArray_NewCopy); DECL_NPY_API(PyArray_NewFromDescr); DECL_NPY_API(PyArray_DescrNewFromType); DECL_NPY_API(PyArray_DescrConverter); DECL_NPY_API(PyArray_EquivTypes); DECL_NPY_API(PyArray_GetArrayParamsFromObject); DECL_NPY_API(PyArray_Squeeze); DECL_NPY_API(PyArray_SetBaseObject); #undef DECL_NPY_API return api; } }; inline PyArray_Proxy* array_proxy(void* ptr) { return reinterpret_cast(ptr); } inline const PyArray_Proxy* array_proxy(const void* ptr) { return reinterpret_cast(ptr); } inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { return reinterpret_cast(ptr); } inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { return reinterpret_cast(ptr); } inline bool check_flags(const void* ptr, int flag) { return (flag == (array_proxy(ptr)->flags & flag)); } template struct is_std_array : std::false_type { }; template struct is_std_array> : std::true_type { }; template struct is_complex : std::false_type { }; template struct is_complex> : std::true_type { }; template using is_pod_struct = all_of< std::is_pod, // since we're accessing directly in memory we need a POD type satisfies_none_of >; template size_t byte_offset_unsafe(const Strides &) { return 0; } template size_t byte_offset_unsafe(const Strides &strides, size_t i, Ix... index) { return i * strides[Dim] + byte_offset_unsafe(strides, index...); } /** Proxy class providing unsafe, unchecked const access to array data. This is constructed through * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` * will be -1 for dimensions determined at runtime. */ template class unchecked_reference { protected: static constexpr bool Dynamic = Dims < 0; const unsigned char *data_; // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to // make large performance gains on big, nested loops, but requires compile-time dimensions conditional_t> shape_, strides_; const size_t dims_; friend class pybind11::array; // Constructor for compile-time dimensions: template unchecked_reference(const void *data, const size_t *shape, const size_t *strides, enable_if_t) : data_{reinterpret_cast(data)}, dims_{Dims} { for (size_t i = 0; i < dims_; i++) { shape_[i] = shape[i]; strides_[i] = strides[i]; } } // Constructor for runtime dimensions: template unchecked_reference(const void *data, const size_t *shape, const size_t *strides, enable_if_t dims) : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} public: /** Unchecked const reference access to data at the given indices. For a compile-time known * number of dimensions, this requires the correct number of arguments; for run-time * dimensionality, this is not checked (and so is up to the caller to use safely). */ template const T &operator()(Ix... index) const { static_assert(sizeof...(Ix) == Dims || Dynamic, "Invalid number of indices for unchecked array reference"); return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, size_t(index)...)); } /** Unchecked const reference access to data; this operator only participates if the reference * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. */ template > const T &operator[](size_t index) const { return operator()(index); } /// Pointer access to the data at the given indices. template const T *data(Ix... ix) const { return &operator()(size_t(ix)...); } /// Returns the item size, i.e. sizeof(T) constexpr static size_t itemsize() { return sizeof(T); } /// Returns the shape (i.e. size) of dimension `dim` size_t shape(size_t dim) const { return shape_[dim]; } /// Returns the number of dimensions of the array size_t ndim() const { return dims_; } /// Returns the total number of elements in the referenced array, i.e. the product of the shapes template enable_if_t size() const { return std::accumulate(shape_.begin(), shape_.end(), (size_t) 1, std::multiplies()); } template enable_if_t size() const { return std::accumulate(shape_, shape_ + ndim(), (size_t) 1, std::multiplies()); } /// Returns the total number of bytes used by the referenced data. Note that the actual span in /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). size_t nbytes() const { return size() * itemsize(); } }; template class unchecked_mutable_reference : public unchecked_reference { friend class pybind11::array; using ConstBase = unchecked_reference; using ConstBase::ConstBase; using ConstBase::Dynamic; public: /// Mutable, unchecked access to data at the given indices. template T& operator()(Ix... index) { static_assert(sizeof...(Ix) == Dims || Dynamic, "Invalid number of indices for unchecked array reference"); return const_cast(ConstBase::operator()(index...)); } /** Mutable, unchecked access data at the given index; this operator only participates if the * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is * exactly equivalent to `obj(index)`. */ template > T &operator[](size_t index) { return operator()(index); } /// Mutable pointer access to the data at the given indices. template T *mutable_data(Ix... ix) { return &operator()(size_t(ix)...); } }; template struct type_caster> { static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); }; template struct type_caster> : type_caster> {}; NAMESPACE_END(detail) class dtype : public object { public: PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); explicit dtype(const buffer_info &info) { dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); // If info.itemsize == 0, use the value calculated from the format string m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); } explicit dtype(const std::string &format) { m_ptr = from_args(pybind11::str(format)).release().ptr(); } dtype(const char *format) : dtype(std::string(format)) { } dtype(list names, list formats, list offsets, size_t itemsize) { dict args; args["names"] = names; args["formats"] = formats; args["offsets"] = offsets; args["itemsize"] = pybind11::int_(itemsize); m_ptr = from_args(args).release().ptr(); } /// This is essentially the same as calling numpy.dtype(args) in Python. static dtype from_args(object args) { PyObject *ptr = nullptr; if (!detail::npy_api::get().PyArray_DescrConverter_(args.release().ptr(), &ptr) || !ptr) throw error_already_set(); return reinterpret_steal(ptr); } /// Return dtype associated with a C++ type. template static dtype of() { return detail::npy_format_descriptor::type>::dtype(); } /// Size of the data type in bytes. size_t itemsize() const { return (size_t) detail::array_descriptor_proxy(m_ptr)->elsize; } /// Returns true for structured data types. bool has_fields() const { return detail::array_descriptor_proxy(m_ptr)->names != nullptr; } /// Single-character type code. char kind() const { return detail::array_descriptor_proxy(m_ptr)->kind; } private: static object _dtype_from_pep3118() { static PyObject *obj = module::import("numpy.core._internal") .attr("_dtype_from_pep3118").cast().release().ptr(); return reinterpret_borrow(obj); } dtype strip_padding(size_t itemsize) { // Recursively strip all void fields with empty names that are generated for // padding fields (as of NumPy v1.11). if (!has_fields()) return *this; struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; std::vector field_descriptors; for (auto field : attr("fields").attr("items")()) { auto spec = field.cast(); auto name = spec[0].cast(); auto format = spec[1].cast()[0].cast(); auto offset = spec[1].cast()[1].cast(); if (!len(name) && format.kind() == 'V') continue; field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); } std::sort(field_descriptors.begin(), field_descriptors.end(), [](const field_descr& a, const field_descr& b) { return a.offset.cast() < b.offset.cast(); }); list names, formats, offsets; for (auto& descr : field_descriptors) { names.append(descr.name); formats.append(descr.format); offsets.append(descr.offset); } return dtype(names, formats, offsets, itemsize); } }; class array : public buffer { public: PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) enum { c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ }; array() : array(0, static_cast(nullptr)) {} array(const pybind11::dtype &dt, const std::vector &shape, const std::vector &strides, const void *ptr = nullptr, handle base = handle()) { auto& api = detail::npy_api::get(); auto ndim = shape.size(); if (shape.size() != strides.size()) pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); auto descr = dt; int flags = 0; if (base && ptr) { if (isinstance(base)) /* Copy flags from base (except ownership bit) */ flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; else /* Writable by default, easy to downgrade later on if needed */ flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; } auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( api.PyArray_Type_, descr.release().ptr(), (int) ndim, reinterpret_cast(const_cast(shape.data())), reinterpret_cast(const_cast(strides.data())), const_cast(ptr), flags, nullptr)); if (!tmp) pybind11_fail("NumPy: unable to create array!"); if (ptr) { if (base) { api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); } else { tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); } } m_ptr = tmp.release().ptr(); } array(const pybind11::dtype &dt, const std::vector &shape, const void *ptr = nullptr, handle base = handle()) : array(dt, shape, default_strides(shape, dt.itemsize()), ptr, base) { } array(const pybind11::dtype &dt, size_t count, const void *ptr = nullptr, handle base = handle()) : array(dt, std::vector{ count }, ptr, base) { } template array(const std::vector& shape, const std::vector& strides, const T* ptr, handle base = handle()) : array(pybind11::dtype::of(), shape, strides, (const void *) ptr, base) { } template array(const std::vector &shape, const T *ptr, handle base = handle()) : array(shape, default_strides(shape, sizeof(T)), ptr, base) { } template array(size_t count, const T *ptr, handle base = handle()) : array(std::vector{ count }, ptr, base) { } explicit array(const buffer_info &info) : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } /// Array descriptor (dtype) pybind11::dtype dtype() const { return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); } /// Total number of elements size_t size() const { return std::accumulate(shape(), shape() + ndim(), (size_t) 1, std::multiplies()); } /// Byte size of a single element size_t itemsize() const { return (size_t) detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; } /// Total number of bytes size_t nbytes() const { return size() * itemsize(); } /// Number of dimensions size_t ndim() const { return (size_t) detail::array_proxy(m_ptr)->nd; } /// Base object object base() const { return reinterpret_borrow(detail::array_proxy(m_ptr)->base); } /// Dimensions of the array const size_t* shape() const { return reinterpret_cast(detail::array_proxy(m_ptr)->dimensions); } /// Dimension along a given axis size_t shape(size_t dim) const { if (dim >= ndim()) fail_dim_check(dim, "invalid axis"); return shape()[dim]; } /// Strides of the array const size_t* strides() const { return reinterpret_cast(detail::array_proxy(m_ptr)->strides); } /// Stride along a given axis size_t strides(size_t dim) const { if (dim >= ndim()) fail_dim_check(dim, "invalid axis"); return strides()[dim]; } /// Return the NumPy array flags int flags() const { return detail::array_proxy(m_ptr)->flags; } /// If set, the array is writeable (otherwise the buffer is read-only) bool writeable() const { return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); } /// If set, the array owns the data (will be freed when the array is deleted) bool owndata() const { return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); } /// Pointer to the contained data. If index is not provided, points to the /// beginning of the buffer. May throw if the index would lead to out of bounds access. template const void* data(Ix... index) const { return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); } /// Mutable pointer to the contained data. If index is not provided, points to the /// beginning of the buffer. May throw if the index would lead to out of bounds access. /// May throw if the array is not writeable. template void* mutable_data(Ix... index) { check_writeable(); return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); } /// Byte offset from beginning of the array to a given index (full or partial). /// May throw if the index would lead to out of bounds access. template size_t offset_at(Ix... index) const { if (sizeof...(index) > ndim()) fail_dim_check(sizeof...(index), "too many indices for an array"); return byte_offset(size_t(index)...); } size_t offset_at() const { return 0; } /// Item count from beginning of the array to a given index (full or partial). /// May throw if the index would lead to out of bounds access. template size_t index_at(Ix... index) const { return offset_at(index...) / itemsize(); } /** Returns a proxy object that provides access to the array's data without bounds or * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with * care: the array must not be destroyed or reshaped for the duration of the returned object, * and the caller must take care not to access invalid dimensions or dimension indices. */ template detail::unchecked_mutable_reference mutable_unchecked() { if (Dims >= 0 && ndim() != (size_t) Dims) throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + "; expected " + std::to_string(Dims)); return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); } /** Returns a proxy object that provides const access to the array's data without bounds or * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the * underlying array have the `writable` flag. Use with care: the array must not be destroyed or * reshaped for the duration of the returned object, and the caller must take care not to access * invalid dimensions or dimension indices. */ template detail::unchecked_reference unchecked() const { if (Dims >= 0 && ndim() != (size_t) Dims) throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + "; expected " + std::to_string(Dims)); return detail::unchecked_reference(data(), shape(), strides(), ndim()); } /// Return a new view with all of the dimensions of length 1 removed array squeeze() { auto& api = detail::npy_api::get(); return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); } /// Ensure that the argument is a NumPy array /// In case of an error, nullptr is returned and the Python error is cleared. static array ensure(handle h, int ExtraFlags = 0) { auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); if (!result) PyErr_Clear(); return result; } protected: template friend struct detail::npy_format_descriptor; void fail_dim_check(size_t dim, const std::string& msg) const { throw index_error(msg + ": " + std::to_string(dim) + " (ndim = " + std::to_string(ndim()) + ")"); } template size_t byte_offset(Ix... index) const { check_dimensions(index...); return detail::byte_offset_unsafe(strides(), size_t(index)...); } void check_writeable() const { if (!writeable()) throw std::domain_error("array is not writeable"); } static std::vector default_strides(const std::vector& shape, size_t itemsize) { auto ndim = shape.size(); std::vector strides(ndim); if (ndim) { std::fill(strides.begin(), strides.end(), itemsize); for (size_t i = 0; i < ndim - 1; i++) for (size_t j = 0; j < ndim - 1 - i; j++) strides[j] *= shape[ndim - 1 - i]; } return strides; } template void check_dimensions(Ix... index) const { check_dimensions_impl(size_t(0), shape(), size_t(index)...); } void check_dimensions_impl(size_t, const size_t*) const { } template void check_dimensions_impl(size_t axis, const size_t* shape, size_t i, Ix... index) const { if (i >= *shape) { throw index_error(std::string("index ") + std::to_string(i) + " is out of bounds for axis " + std::to_string(axis) + " with size " + std::to_string(*shape)); } check_dimensions_impl(axis + 1, shape + 1, index...); } /// Create array from any object -- always returns a new reference static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { if (ptr == nullptr) return nullptr; return detail::npy_api::get().PyArray_FromAny_( ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); } }; template class array_t : public array { public: using value_type = T; array_t() : array(0, static_cast(nullptr)) {} array_t(handle h, borrowed_t) : array(h, borrowed) { } array_t(handle h, stolen_t) : array(h, stolen) { } PYBIND11_DEPRECATED("Use array_t::ensure() instead") array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen) { if (!m_ptr) PyErr_Clear(); if (!is_borrowed) Py_XDECREF(h.ptr()); } array_t(const object &o) : array(raw_array_t(o.ptr()), stolen) { if (!m_ptr) throw error_already_set(); } explicit array_t(const buffer_info& info) : array(info) { } array_t(const std::vector &shape, const std::vector &strides, const T *ptr = nullptr, handle base = handle()) : array(shape, strides, ptr, base) { } explicit array_t(const std::vector &shape, const T *ptr = nullptr, handle base = handle()) : array(shape, ptr, base) { } explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) : array(count, ptr, base) { } constexpr size_t itemsize() const { return sizeof(T); } template size_t index_at(Ix... index) const { return offset_at(index...) / itemsize(); } template const T* data(Ix... index) const { return static_cast(array::data(index...)); } template T* mutable_data(Ix... index) { return static_cast(array::mutable_data(index...)); } // Reference to element at a given index template const T& at(Ix... index) const { if (sizeof...(index) != ndim()) fail_dim_check(sizeof...(index), "index dimension mismatch"); return *(static_cast(array::data()) + byte_offset(size_t(index)...) / itemsize()); } // Mutable reference to element at a given index template T& mutable_at(Ix... index) { if (sizeof...(index) != ndim()) fail_dim_check(sizeof...(index), "index dimension mismatch"); return *(static_cast(array::mutable_data()) + byte_offset(size_t(index)...) / itemsize()); } /** Returns a proxy object that provides access to the array's data without bounds or * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with * care: the array must not be destroyed or reshaped for the duration of the returned object, * and the caller must take care not to access invalid dimensions or dimension indices. */ template detail::unchecked_mutable_reference mutable_unchecked() { return array::mutable_unchecked(); } /** Returns a proxy object that provides const access to the array's data without bounds or * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped * for the duration of the returned object, and the caller must take care not to access invalid * dimensions or dimension indices. */ template detail::unchecked_reference unchecked() const { return array::unchecked(); } /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert /// it). In case of an error, nullptr is returned and the Python error is cleared. static array_t ensure(handle h) { auto result = reinterpret_steal(raw_array_t(h.ptr())); if (!result) PyErr_Clear(); return result; } static bool check_(handle h) { const auto &api = detail::npy_api::get(); return api.PyArray_Check_(h.ptr()) && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()); } protected: /// Create array from any object -- always returns a new reference static PyObject *raw_array_t(PyObject *ptr) { if (ptr == nullptr) return nullptr; return detail::npy_api::get().PyArray_FromAny_( ptr, dtype::of().release().ptr(), 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); } }; template struct format_descriptor::value>> { static std::string format() { return detail::npy_format_descriptor::type>::format(); } }; template struct format_descriptor { static std::string format() { return std::to_string(N) + "s"; } }; template struct format_descriptor> { static std::string format() { return std::to_string(N) + "s"; } }; template struct format_descriptor::value>> { static std::string format() { return format_descriptor< typename std::remove_cv::type>::type>::format(); } }; NAMESPACE_BEGIN(detail) template struct pyobject_caster> { using type = array_t; bool load(handle src, bool convert) { if (!convert && !type::check_(src)) return false; value = type::ensure(src); return static_cast(value); } static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { return src.inc_ref(); } PYBIND11_TYPE_CASTER(type, handle_type_name::name()); }; template struct compare_buffer_info::value>> { static bool compare(const buffer_info& b) { return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); } }; template struct npy_format_descriptor::value>> { private: // NB: the order here must match the one in common.h constexpr static const int values[15] = { npy_api::NPY_BOOL_, npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_, npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_, npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ }; public: static constexpr int value = values[detail::is_fmt_numeric::index]; static pybind11::dtype dtype() { if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) return reinterpret_borrow(ptr); pybind11_fail("Unsupported buffer format!"); } template ::value, int> = 0> static PYBIND11_DESCR name() { return _::value>(_("bool"), _::value>("int", "uint") + _()); } template ::value, int> = 0> static PYBIND11_DESCR name() { return _::value || std::is_same::value>( _("float") + _(), _("longdouble")); } template ::value, int> = 0> static PYBIND11_DESCR name() { return _::value || std::is_same::value>( _("complex") + _(), _("longcomplex")); } }; #define PYBIND11_DECL_CHAR_FMT \ static PYBIND11_DESCR name() { return _("S") + _(); } \ static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; #undef PYBIND11_DECL_CHAR_FMT template struct npy_format_descriptor::value>> { private: using base_descr = npy_format_descriptor::type>; public: static PYBIND11_DESCR name() { return base_descr::name(); } static pybind11::dtype dtype() { return base_descr::dtype(); } }; struct field_descriptor { const char *name; size_t offset; size_t size; size_t alignment; std::string format; dtype descr; }; inline PYBIND11_NOINLINE void register_structured_dtype( const std::initializer_list& fields, const std::type_info& tinfo, size_t itemsize, bool (*direct_converter)(PyObject *, void *&)) { auto& numpy_internals = get_numpy_internals(); if (numpy_internals.get_type_info(tinfo, false)) pybind11_fail("NumPy: dtype is already registered"); list names, formats, offsets; for (auto field : fields) { if (!field.descr) pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ " + tinfo.name()); names.append(PYBIND11_STR_TYPE(field.name)); formats.append(field.descr); offsets.append(pybind11::int_(field.offset)); } auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); // There is an existing bug in NumPy (as of v1.11): trailing bytes are // not encoded explicitly into the format string. This will supposedly // get fixed in v1.12; for further details, see these: // - https://github.com/numpy/numpy/issues/7797 // - https://github.com/numpy/numpy/pull/7798 // Because of this, we won't use numpy's logic to generate buffer format // strings and will just do it ourselves. std::vector ordered_fields(fields); std::sort(ordered_fields.begin(), ordered_fields.end(), [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); size_t offset = 0; std::ostringstream oss; oss << "T{"; for (auto& field : ordered_fields) { if (field.offset > offset) oss << (field.offset - offset) << 'x'; // mark unaligned fields with '^' (unaligned native type) if (field.offset % field.alignment) oss << '^'; oss << field.format << ':' << field.name << ':'; offset = field.offset + field.size; } if (itemsize > offset) oss << (itemsize - offset) << 'x'; oss << '}'; auto format_str = oss.str(); // Sanity check: verify that NumPy properly parses our buffer format string auto& api = npy_api::get(); auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) pybind11_fail("NumPy: invalid buffer descriptor!"); auto tindex = std::type_index(tinfo); numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; get_internals().direct_conversions[tindex].push_back(direct_converter); } template struct npy_format_descriptor { static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); static PYBIND11_DESCR name() { return make_caster::name(); } static pybind11::dtype dtype() { return reinterpret_borrow(dtype_ptr()); } static std::string format() { static auto format_str = get_numpy_internals().get_type_info(true)->format_str; return format_str; } static void register_dtype(const std::initializer_list& fields) { register_structured_dtype(fields, typeid(typename std::remove_cv::type), sizeof(T), &direct_converter); } private: static PyObject* dtype_ptr() { static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; return ptr; } static bool direct_converter(PyObject *obj, void*& value) { auto& api = npy_api::get(); if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) return false; if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { value = ((PyVoidScalarObject_Proxy *) obj)->obval; return true; } } return false; } }; #define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ ::pybind11::detail::field_descriptor { \ Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ alignof(decltype(std::declval().Field)), \ ::pybind11::format_descriptor().Field)>::format(), \ ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ } // Extract name, offset and format descriptor for a struct field #define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) // The main idea of this macro is borrowed from https://github.com/swansontec/map-macro // (C) William Swanson, Paul Fultz #define PYBIND11_EVAL0(...) __VA_ARGS__ #define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) #define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) #define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) #define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) #define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) #define PYBIND11_MAP_END(...) #define PYBIND11_MAP_OUT #define PYBIND11_MAP_COMMA , #define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END #define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT #define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) #define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) #ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround #define PYBIND11_MAP_LIST_NEXT1(test, next) \ PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) #else #define PYBIND11_MAP_LIST_NEXT1(test, next) \ PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) #endif #define PYBIND11_MAP_LIST_NEXT(test, next) \ PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) #define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) #define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) // PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... #define PYBIND11_MAP_LIST(f, t, ...) \ PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) #define PYBIND11_NUMPY_DTYPE(Type, ...) \ ::pybind11::detail::npy_format_descriptor::register_dtype \ ({PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) #ifdef _MSC_VER #define PYBIND11_MAP2_LIST_NEXT1(test, next) \ PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) #else #define PYBIND11_MAP2_LIST_NEXT1(test, next) \ PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) #endif #define PYBIND11_MAP2_LIST_NEXT(test, next) \ PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) #define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) #define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) // PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... #define PYBIND11_MAP2_LIST(f, t, ...) \ PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) #define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ ::pybind11::detail::npy_format_descriptor::register_dtype \ ({PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) template using array_iterator = typename std::add_pointer::type; template array_iterator array_begin(const buffer_info& buffer) { return array_iterator(reinterpret_cast(buffer.ptr)); } template array_iterator array_end(const buffer_info& buffer) { return array_iterator(reinterpret_cast(buffer.ptr) + buffer.size); } class common_iterator { public: using container_type = std::vector; using value_type = container_type::value_type; using size_type = container_type::size_type; common_iterator() : p_ptr(0), m_strides() {} common_iterator(void* ptr, const container_type& strides, const std::vector& shape) : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { m_strides.back() = static_cast(strides.back()); for (size_type i = m_strides.size() - 1; i != 0; --i) { size_type j = i - 1; value_type s = static_cast(shape[i]); m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; } } void increment(size_type dim) { p_ptr += m_strides[dim]; } void* data() const { return p_ptr; } private: char* p_ptr; container_type m_strides; }; template class multi_array_iterator { public: using container_type = std::vector; multi_array_iterator(const std::array &buffers, const std::vector &shape) : m_shape(shape.size()), m_index(shape.size(), 0), m_common_iterator() { // Manual copy to avoid conversion warning if using std::copy for (size_t i = 0; i < shape.size(); ++i) m_shape[i] = static_cast(shape[i]); container_type strides(shape.size()); for (size_t i = 0; i < N; ++i) init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); } multi_array_iterator& operator++() { for (size_t j = m_index.size(); j != 0; --j) { size_t i = j - 1; if (++m_index[i] != m_shape[i]) { increment_common_iterator(i); break; } else { m_index[i] = 0; } } return *this; } template const T& data() const { return *reinterpret_cast(m_common_iterator[K].data()); } private: using common_iter = common_iterator; void init_common_iterator(const buffer_info &buffer, const std::vector &shape, common_iter &iterator, container_type &strides) { auto buffer_shape_iter = buffer.shape.rbegin(); auto buffer_strides_iter = buffer.strides.rbegin(); auto shape_iter = shape.rbegin(); auto strides_iter = strides.rbegin(); while (buffer_shape_iter != buffer.shape.rend()) { if (*shape_iter == *buffer_shape_iter) *strides_iter = static_cast(*buffer_strides_iter); else *strides_iter = 0; ++buffer_shape_iter; ++buffer_strides_iter; ++shape_iter; ++strides_iter; } std::fill(strides_iter, strides.rend(), 0); iterator = common_iter(buffer.ptr, strides, shape); } void increment_common_iterator(size_t dim) { for (auto &iter : m_common_iterator) iter.increment(dim); } container_type m_shape; container_type m_index; std::array m_common_iterator; }; enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; // Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial // enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a // singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage // buffer; returns `non_trivial` otherwise. template broadcast_trivial broadcast(const std::array &buffers, size_t &ndim, std::vector &shape) { ndim = std::accumulate(buffers.begin(), buffers.end(), size_t(0), [](size_t res, const buffer_info& buf) { return std::max(res, buf.ndim); }); shape.clear(); shape.resize(ndim, 1); // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or // the full size). for (size_t i = 0; i < N; ++i) { auto res_iter = shape.rbegin(); auto end = buffers[i].shape.rend(); for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { const auto &dim_size_in = *shape_iter; auto &dim_size_out = *res_iter; // Each input dimension can either be 1 or `n`, but `n` values must match across buffers if (dim_size_out == 1) dim_size_out = dim_size_in; else if (dim_size_in != 1 && dim_size_in != dim_size_out) pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); } } bool trivial_broadcast_c = true; bool trivial_broadcast_f = true; for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { if (buffers[i].size == 1) continue; // Require the same number of dimensions: if (buffers[i].ndim != ndim) return broadcast_trivial::non_trivial; // Require all dimensions be full-size: if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) return broadcast_trivial::non_trivial; // Check for C contiguity (but only if previous inputs were also C contiguous) if (trivial_broadcast_c) { size_t expect_stride = buffers[i].itemsize; auto end = buffers[i].shape.crend(); for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { if (expect_stride == *stride_iter) expect_stride *= *shape_iter; else trivial_broadcast_c = false; } } // Check for Fortran contiguity (if previous inputs were also F contiguous) if (trivial_broadcast_f) { size_t expect_stride = buffers[i].itemsize; auto end = buffers[i].shape.cend(); for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { if (expect_stride == *stride_iter) expect_stride *= *shape_iter; else trivial_broadcast_f = false; } } } return trivial_broadcast_c ? broadcast_trivial::c_trivial : trivial_broadcast_f ? broadcast_trivial::f_trivial : broadcast_trivial::non_trivial; } template struct vectorize_helper { typename std::remove_reference::type f; static constexpr size_t N = sizeof...(Args); template explicit vectorize_helper(T&&f) : f(std::forward(f)) { } object operator()(array_t... args) { return run(args..., make_index_sequence()); } template object run(array_t&... args, index_sequence index) { /* Request buffers from all parameters */ std::array buffers {{ args.request()... }}; /* Determine dimensions parameters of output array */ size_t ndim = 0; std::vector shape(0); auto trivial = broadcast(buffers, ndim, shape); size_t size = 1; std::vector strides(ndim); if (ndim > 0) { if (trivial == broadcast_trivial::f_trivial) { strides[0] = sizeof(Return); for (size_t i = 1; i < ndim; ++i) { strides[i] = strides[i - 1] * shape[i - 1]; size *= shape[i - 1]; } size *= shape[ndim - 1]; } else { strides[ndim-1] = sizeof(Return); for (size_t i = ndim - 1; i > 0; --i) { strides[i - 1] = strides[i] * shape[i]; size *= shape[i]; } size *= shape[0]; } } if (size == 1) return cast(f(*reinterpret_cast(buffers[Index].ptr)...)); array_t result(shape, strides); auto buf = result.request(); auto output = (Return *) buf.ptr; /* Call the function */ if (trivial == broadcast_trivial::non_trivial) { apply_broadcast(buffers, buf, index); } else { for (size_t i = 0; i < size; ++i) output[i] = f((reinterpret_cast(buffers[Index].ptr)[buffers[Index].size == 1 ? 0 : i])...); } return result; } template void apply_broadcast(const std::array &buffers, buffer_info &output, index_sequence) { using input_iterator = multi_array_iterator; using output_iterator = array_iterator; input_iterator input_iter(buffers, output.shape); output_iterator output_end = array_end(output); for (output_iterator iter = array_begin(output); iter != output_end; ++iter, ++input_iter) { *iter = f((input_iter.template data())...); } } }; template struct handle_type_name> { static PYBIND11_DESCR name() { return _("numpy.ndarray[") + npy_format_descriptor::name() + _("]"); } }; NAMESPACE_END(detail) template detail::vectorize_helper vectorize(const Func &f, Return (*) (Args ...)) { return detail::vectorize_helper(f); } template detail::vectorize_helper vectorize(Return (*f) (Args ...)) { return vectorize(f, f); } template ::type::operator())>::type> auto vectorize(Func &&f) -> decltype( vectorize(std::forward(f), (FuncType *) nullptr)) { return vectorize(std::forward(f), (FuncType *) nullptr); } NAMESPACE_END(pybind11) #if defined(_MSC_VER) #pragma warning(pop) #endif DarkRadiant-2.5.0/libs/pybind/pybind11/operators.h000066400000000000000000000177171321750546400217620ustar00rootroot00000000000000/* pybind11/operator.h: Metatemplates for operator overloading Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "pybind11.h" #if defined(__clang__) && !defined(__INTEL_COMPILER) # pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) #endif NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) /// Enumeration with all supported operator types enum op_id : int { op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, op_repr, op_truediv }; enum op_type : int { op_l, /* base type on left */ op_r, /* base type on right */ op_u /* unary operator */ }; struct self_t { }; static const self_t self = self_t(); /// Type for an unused type slot struct undefined_t { }; /// Don't warn about an unused variable inline self_t __self() { return self; } /// base template of operator implementations template struct op_impl { }; /// Operator implementation generator template struct op_ { template void execute(Class &cl, const Extra&... extra) const { typedef typename Class::type Base; typedef typename std::conditional::value, Base, L>::type L_type; typedef typename std::conditional::value, Base, R>::type R_type; typedef op_impl op; cl.def(op::name(), &op::execute, is_operator(), extra...); } template void execute_cast(Class &cl, const Extra&... extra) const { typedef typename Class::type Base; typedef typename std::conditional::value, Base, L>::type L_type; typedef typename std::conditional::value, Base, R>::type R_type; typedef op_impl op; cl.def(op::name(), &op::execute_cast, is_operator(), extra...); } }; #define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ template struct op_impl { \ static char const* name() { return "__" #id "__"; } \ static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ static B execute_cast(const L &l, const R &r) { return B(expr); } \ }; \ template struct op_impl { \ static char const* name() { return "__" #rid "__"; } \ static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ static B execute_cast(const R &r, const L &l) { return B(expr); } \ }; \ inline op_ op(const self_t &, const self_t &) { \ return op_(); \ } \ template op_ op(const self_t &, const T &) { \ return op_(); \ } \ template op_ op(const T &, const self_t &) { \ return op_(); \ } #define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ template struct op_impl { \ static char const* name() { return "__" #id "__"; } \ static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ static B execute_cast(L &l, const R &r) { return B(expr); } \ }; \ template op_ op(const self_t &, const T &) { \ return op_(); \ } #define PYBIND11_UNARY_OPERATOR(id, op, expr) \ template struct op_impl { \ static char const* name() { return "__" #id "__"; } \ static auto execute(const L &l) -> decltype(expr) { return expr; } \ static B execute_cast(const L &l) { return B(expr); } \ }; \ inline op_ op(const self_t &) { \ return op_(); \ } PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) #if PY_MAJOR_VERSION >= 3 PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) #else PYBIND11_BINARY_OPERATOR(div, rdiv, operator/, l / r) #endif PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) //PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) PYBIND11_INPLACE_OPERATOR(idiv, operator/=, l /= r) PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) PYBIND11_UNARY_OPERATOR(neg, operator-, -l) PYBIND11_UNARY_OPERATOR(pos, operator+, +l) PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) PYBIND11_UNARY_OPERATOR(int, int_, (int) l) PYBIND11_UNARY_OPERATOR(float, float_, (double) l) #undef PYBIND11_BINARY_OPERATOR #undef PYBIND11_INPLACE_OPERATOR #undef PYBIND11_UNARY_OPERATOR NAMESPACE_END(detail) using detail::self; NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/pybind/pybind11/options.h000066400000000000000000000037241321750546400214300ustar00rootroot00000000000000/* pybind11/options.h: global settings that are configurable at runtime. Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "common.h" NAMESPACE_BEGIN(pybind11) class options { public: // Default RAII constructor, which leaves settings as they currently are. options() : previous_state(global_state()) {} // Class is non-copyable. options(const options&) = delete; options& operator=(const options&) = delete; // Destructor, which restores settings that were in effect before. ~options() { global_state() = previous_state; } // Setter methods (affect the global state): options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } // Getter methods (return the global state): static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } static bool show_function_signatures() { return global_state().show_function_signatures; } // This type is not meant to be allocated on the heap. void* operator new(size_t) = delete; private: struct state { bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. }; static state &global_state() { static state instance; return instance; } state previous_state; }; NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/pybind/pybind11/pybind11.h000066400000000000000000002267021321750546400213670ustar00rootroot00000000000000/* pybind11/pybind11.h: Main header file of the C++11 python binding generator library Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant # pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted # pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) # pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name # pragma warning(disable: 4702) // warning C4702: unreachable code # pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified #elif defined(__INTEL_COMPILER) # pragma warning(push) # pragma warning(disable: 186) // pointless comparison of unsigned integer with zero # pragma warning(disable: 1334) // the "template" keyword used for syntactic disambiguation may only be used within a template # pragma warning(disable: 2196) // warning #2196: routine is both "inline" and "noinline" #elif defined(__GNUG__) && !defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-but-set-parameter" # pragma GCC diagnostic ignored "-Wunused-but-set-variable" # pragma GCC diagnostic ignored "-Wmissing-field-initializers" # pragma GCC diagnostic ignored "-Wstrict-aliasing" # pragma GCC diagnostic ignored "-Wattributes" #endif #include "attr.h" #include "options.h" #include "class_support.h" NAMESPACE_BEGIN(pybind11) /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object class cpp_function : public function { public: cpp_function() { } /// Construct a cpp_function from a vanilla function pointer template cpp_function(Return (*f)(Args...), const Extra&... extra) { initialize(f, f, extra...); } /// Construct a cpp_function from a lambda function (possibly with internal state) template ::type, std::is_function, std::is_pointer, std::is_member_pointer >::value> > cpp_function(Func &&f, const Extra&... extra) { using FuncType = typename detail::remove_class::type::operator())>::type; initialize(std::forward(f), (FuncType *) nullptr, extra...); } /// Construct a cpp_function from a class method (non-const) template cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, (Return (*) (Class *, Arg...)) nullptr, extra...); } /// Construct a cpp_function from a class method (const) template cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, (Return (*)(const Class *, Arg ...)) nullptr, extra...); } /// Return the function name object name() const { return attr("__name__"); } protected: /// Space optimization: don't inline this frequently instantiated fragment PYBIND11_NOINLINE detail::function_record *make_function_record() { return new detail::function_record(); } /// Special internal constructor for functors, lambda functions, etc. template void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { struct capture { typename std::remove_reference::type f; }; /* Store the function including any extra state it might have (e.g. a lambda capture object) */ auto rec = make_function_record(); /* Store the capture object directly in the function record if there is enough space */ if (sizeof(capture) <= sizeof(rec->data)) { /* Without these pragmas, GCC warns that there might not be enough space to use the placement new operator. However, the 'if' statement above ensures that this is the case. */ #if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wplacement-new" #endif new ((capture *) &rec->data) capture { std::forward(f) }; #if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 # pragma GCC diagnostic pop #endif if (!std::is_trivially_destructible::value) rec->free_data = [](detail::function_record *r) { ((capture *) &r->data)->~capture(); }; } else { rec->data[0] = new capture { std::forward(f) }; rec->free_data = [](detail::function_record *r) { delete ((capture *) r->data[0]); }; } /* Type casters for the function arguments and return value */ using cast_in = detail::argument_loader; using cast_out = detail::make_caster< detail::conditional_t::value, detail::void_type, Return> >; static_assert(detail::expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), "The number of argument annotations does not match the number of function arguments"); /* Dispatch code which converts function arguments and performs the actual function call */ rec->impl = [](detail::function_call &call) -> handle { cast_in args_converter; /* Try to cast the function arguments into the C++ domain */ if (!args_converter.load_args(call)) return PYBIND11_TRY_NEXT_OVERLOAD; /* Invoke call policy pre-call hook */ detail::process_attributes::precall(call); /* Get a pointer to the capture object */ auto data = (sizeof(capture) <= sizeof(call.func.data) ? &call.func.data : call.func.data[0]); capture *cap = const_cast(reinterpret_cast(data)); /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ const auto policy = detail::return_value_policy_override::policy(call.func.policy); /* Perform the function call */ handle result = cast_out::cast(args_converter.template call(cap->f), policy, call.parent); /* Invoke call policy post-call hook */ detail::process_attributes::postcall(call, result); return result; }; /* Process any user-provided function attributes */ detail::process_attributes::init(extra..., rec); /* Generate a readable signature describing the function's arguments and return value types */ using detail::descr; using detail::_; PYBIND11_DESCR signature = _("(") + cast_in::arg_names() + _(") -> ") + cast_out::name(); /* Register the function with Python from generic (non-templated) code */ initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args)); if (cast_in::has_args) rec->has_args = true; if (cast_in::has_kwargs) rec->has_kwargs = true; /* Stash some additional information used by an important optimization in 'functional.h' */ using FunctionType = Return (*)(Args...); constexpr bool is_function_ptr = std::is_convertible::value && sizeof(capture) == sizeof(void *); if (is_function_ptr) { rec->is_stateless = true; rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); } } /// Register a function call with Python (generic non-templated code goes here) void initialize_generic(detail::function_record *rec, const char *text, const std::type_info *const *types, size_t args) { /* Create copies of all referenced C-style strings */ rec->name = strdup(rec->name ? rec->name : ""); if (rec->doc) rec->doc = strdup(rec->doc); for (auto &a: rec->args) { if (a.name) a.name = strdup(a.name); if (a.descr) a.descr = strdup(a.descr); else if (a.value) a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); } /* Generate a proper function signature */ std::string signature; size_t type_depth = 0, char_index = 0, type_index = 0, arg_index = 0; while (true) { char c = text[char_index++]; if (c == '\0') break; if (c == '{') { // Write arg name for everything except *args, **kwargs and return type. if (type_depth == 0 && text[char_index] != '*' && arg_index < args) { if (!rec->args.empty() && rec->args[arg_index].name) { signature += rec->args[arg_index].name; } else if (arg_index == 0 && rec->is_method) { signature += "self"; } else { signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); } signature += ": "; } ++type_depth; } else if (c == '}') { --type_depth; if (type_depth == 0) { if (arg_index < rec->args.size() && rec->args[arg_index].descr) { signature += "="; signature += rec->args[arg_index].descr; } arg_index++; } } else if (c == '%') { const std::type_info *t = types[type_index++]; if (!t) pybind11_fail("Internal error while parsing type signature (1)"); if (auto tinfo = detail::get_type_info(*t)) { #if defined(PYPY_VERSION) signature += handle((PyObject *) tinfo->type) .attr("__module__") .cast() + "."; #endif signature += tinfo->type->tp_name; } else { std::string tname(t->name()); detail::clean_type_id(tname); signature += tname; } } else { signature += c; } } if (type_depth != 0 || types[type_index] != nullptr) pybind11_fail("Internal error while parsing type signature (2)"); #if !defined(PYBIND11_CPP14) delete[] types; delete[] text; #endif #if PY_MAJOR_VERSION < 3 if (strcmp(rec->name, "__next__") == 0) { std::free(rec->name); rec->name = strdup("next"); } else if (strcmp(rec->name, "__bool__") == 0) { std::free(rec->name); rec->name = strdup("__nonzero__"); } #endif rec->signature = strdup(signature.c_str()); rec->args.shrink_to_fit(); rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); rec->nargs = (std::uint16_t) args; #if PY_MAJOR_VERSION < 3 if (rec->sibling && PyMethod_Check(rec->sibling.ptr())) rec->sibling = PyMethod_GET_FUNCTION(rec->sibling.ptr()); #endif detail::function_record *chain = nullptr, *chain_start = rec; if (rec->sibling) { if (PyCFunction_Check(rec->sibling.ptr())) { auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); chain = (detail::function_record *) rec_capsule; /* Never append a method to an overload chain of a parent class; instead, hide the parent's overloads in this case */ if (chain->scope != rec->scope) chain = nullptr; } // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing else if (!rec->sibling.is_none() && rec->name[0] != '_') pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + "\" with a function of the same name"); } if (!chain) { /* No existing overload was found, create a new function object */ rec->def = new PyMethodDef(); memset(rec->def, 0, sizeof(PyMethodDef)); rec->def->ml_name = rec->name; rec->def->ml_meth = reinterpret_cast(*dispatcher); rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; capsule rec_capsule(rec, [](void *ptr) { destruct((detail::function_record *) ptr); }); object scope_module; if (rec->scope) { if (hasattr(rec->scope, "__module__")) { scope_module = rec->scope.attr("__module__"); } else if (hasattr(rec->scope, "__name__")) { scope_module = rec->scope.attr("__name__"); } } m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); if (!m_ptr) pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); } else { /* Append at the end of the overload chain */ m_ptr = rec->sibling.ptr(); inc_ref(); chain_start = chain; while (chain->next) chain = chain->next; chain->next = rec; } std::string signatures; int index = 0; /* Create a nice pydoc rec including all signatures and docstrings of the functions in the overload chain */ if (chain && options::show_function_signatures()) { // First a generic signature signatures += rec->name; signatures += "(*args, **kwargs)\n"; signatures += "Overloaded function.\n\n"; } // Then specific overload signatures bool first_user_def = true; for (auto it = chain_start; it != nullptr; it = it->next) { if (options::show_function_signatures()) { if (index > 0) signatures += "\n"; if (chain) signatures += std::to_string(++index) + ". "; signatures += rec->name; signatures += it->signature; signatures += "\n"; } if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { // If we're appending another docstring, and aren't printing function signatures, we // need to append a newline first: if (!options::show_function_signatures()) { if (first_user_def) first_user_def = false; else signatures += "\n"; } if (options::show_function_signatures()) signatures += "\n"; signatures += it->doc; if (options::show_function_signatures()) signatures += "\n"; } } /* Install docstring */ PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; if (func->m_ml->ml_doc) std::free(const_cast(func->m_ml->ml_doc)); func->m_ml->ml_doc = strdup(signatures.c_str()); if (rec->is_method) { m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); if (!m_ptr) pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); Py_DECREF(func); } } /// When a cpp_function is GCed, release any memory allocated by pybind11 static void destruct(detail::function_record *rec) { while (rec) { detail::function_record *next = rec->next; if (rec->free_data) rec->free_data(rec); std::free((char *) rec->name); std::free((char *) rec->doc); std::free((char *) rec->signature); for (auto &arg: rec->args) { std::free(const_cast(arg.name)); std::free(const_cast(arg.descr)); arg.value.dec_ref(); } if (rec->def) { std::free(const_cast(rec->def->ml_doc)); delete rec->def; } delete rec; rec = next; } } /// Main dispatch logic for calls to functions bound using pybind11 static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { using namespace detail; /* Iterator over the list of potentially admissible overloads */ function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), *it = overloads; /* Need to know how many arguments + keyword arguments there are to pick the right overload */ const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, result = PYBIND11_TRY_NEXT_OVERLOAD; try { // We do this in two passes: in the first pass, we load arguments with `convert=false`; // in the second, we allow conversion (except for arguments with an explicit // py::arg().noconvert()). This lets us prefer calls without conversion, with // conversion as a fallback. std::vector second_pass; // However, if there are no overloads, we can just skip the no-convert pass entirely const bool overloaded = it != nullptr && it->next != nullptr; for (; it != nullptr; it = it->next) { /* For each overload: 1. Copy all positional arguments we were given, also checking to make sure that named positional arguments weren't *also* specified via kwarg. 2. If we weren't given enough, try to make up the omitted ones by checking whether they were provided by a kwarg matching the `py::arg("name")` name. If so, use it (and remove it from kwargs; if not, see if the function binding provided a default that we can use. 3. Ensure that either all keyword arguments were "consumed", or that the function takes a kwargs argument to accept unconsumed kwargs. 4. Any positional arguments still left get put into a tuple (for args), and any leftover kwargs get put into a dict. 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an extra tuple or dict at the end of the positional arguments. 6. Call the function call dispatcher (function_record::impl) If one of these fail, move on to the next overload and keep trying until we get a result other than PYBIND11_TRY_NEXT_OVERLOAD. */ function_record &func = *it; size_t pos_args = func.nargs; // Number of positional arguments that we need if (func.has_args) --pos_args; // (but don't count py::args if (func.has_kwargs) --pos_args; // or py::kwargs) if (!func.has_args && n_args_in > pos_args) continue; // Too many arguments for this overload if (n_args_in < pos_args && func.args.size() < pos_args) continue; // Not enough arguments given, and not enough defaults to fill in the blanks function_call call(func, parent); size_t args_to_copy = std::min(pos_args, n_args_in); size_t args_copied = 0; // 1. Copy any position arguments given. bool bad_kwarg = false; for (; args_copied < args_to_copy; ++args_copied) { if (kwargs_in && args_copied < func.args.size() && func.args[args_copied].name && PyDict_GetItemString(kwargs_in, func.args[args_copied].name)) { bad_kwarg = true; break; } call.args.push_back(PyTuple_GET_ITEM(args_in, args_copied)); call.args_convert.push_back(args_copied < func.args.size() ? func.args[args_copied].convert : true); } if (bad_kwarg) continue; // Maybe it was meant for another overload (issue #688) // We'll need to copy this if we steal some kwargs for defaults dict kwargs = reinterpret_borrow(kwargs_in); // 2. Check kwargs and, failing that, defaults that may help complete the list if (args_copied < pos_args) { bool copied_kwargs = false; for (; args_copied < pos_args; ++args_copied) { const auto &arg = func.args[args_copied]; handle value; if (kwargs_in && arg.name) value = PyDict_GetItemString(kwargs.ptr(), arg.name); if (value) { // Consume a kwargs value if (!copied_kwargs) { kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); copied_kwargs = true; } PyDict_DelItemString(kwargs.ptr(), arg.name); } else if (arg.value) { value = arg.value; } if (value) { call.args.push_back(value); call.args_convert.push_back(arg.convert); } else break; } if (args_copied < pos_args) continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments } // 3. Check everything was consumed (unless we have a kwargs arg) if (kwargs && kwargs.size() > 0 && !func.has_kwargs) continue; // Unconsumed kwargs, but no py::kwargs argument to accept them // 4a. If we have a py::args argument, create a new tuple with leftovers tuple extra_args; if (func.has_args) { if (args_to_copy == 0) { // We didn't copy out any position arguments from the args_in tuple, so we // can reuse it directly without copying: extra_args = reinterpret_borrow(args_in); } else if (args_copied >= n_args_in) { extra_args = tuple(0); } else { size_t args_size = n_args_in - args_copied; extra_args = tuple(args_size); for (size_t i = 0; i < args_size; ++i) { handle item = PyTuple_GET_ITEM(args_in, args_copied + i); extra_args[i] = item.inc_ref().ptr(); } } call.args.push_back(extra_args); call.args_convert.push_back(false); } // 4b. If we have a py::kwargs, pass on any remaining kwargs if (func.has_kwargs) { if (!kwargs.ptr()) kwargs = dict(); // If we didn't get one, send an empty one call.args.push_back(kwargs); call.args_convert.push_back(false); } // 5. Put everything in a vector. Not technically step 5, we've been building it // in `call.args` all along. #if !defined(NDEBUG) if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); #endif std::vector second_pass_convert; if (overloaded) { // We're in the first no-convert pass, so swap out the conversion flags for a // set of all-false flags. If the call fails, we'll swap the flags back in for // the conversion-allowed call below. second_pass_convert.resize(func.nargs, false); call.args_convert.swap(second_pass_convert); } // 6. Call the function. try { result = func.impl(call); } catch (reference_cast_error &) { result = PYBIND11_TRY_NEXT_OVERLOAD; } if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) break; if (overloaded) { // The (overloaded) call failed; if the call has at least one argument that // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) // then add this call to the list of second pass overloads to try. for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { if (second_pass_convert[i]) { // Found one: swap the converting flags back in and store the call for // the second pass. call.args_convert.swap(second_pass_convert); second_pass.push_back(std::move(call)); break; } } } } if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { // The no-conversion pass finished without success, try again with conversion allowed for (auto &call : second_pass) { try { result = call.func.impl(call); } catch (reference_cast_error &) { result = PYBIND11_TRY_NEXT_OVERLOAD; } if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) break; } } } catch (error_already_set &e) { e.restore(); return nullptr; } catch (...) { /* When an exception is caught, give each registered exception translator a chance to translate it to a Python exception in reverse order of registration. A translator may choose to do one of the following: - catch the exception and call PyErr_SetString or PyErr_SetObject to set a standard (or custom) Python exception, or - do nothing and let the exception fall through to the next translator, or - delegate translation to the next translator by throwing a new type of exception. */ auto last_exception = std::current_exception(); auto ®istered_exception_translators = get_internals().registered_exception_translators; for (auto& translator : registered_exception_translators) { try { translator(last_exception); } catch (...) { last_exception = std::current_exception(); continue; } return nullptr; } PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); return nullptr; } if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { if (overloads->is_operator) return handle(Py_NotImplemented).inc_ref().ptr(); std::string msg = std::string(overloads->name) + "(): incompatible " + std::string(overloads->is_constructor ? "constructor" : "function") + " arguments. The following argument types are supported:\n"; int ctr = 0; for (function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { msg += " "+ std::to_string(++ctr) + ". "; bool wrote_sig = false; if (overloads->is_constructor) { // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` std::string sig = it2->signature; size_t start = sig.find('(') + 7; // skip "(self: " if (start < sig.size()) { // End at the , for the next argument size_t end = sig.find(", "), next = end + 2; size_t ret = sig.rfind(" -> "); // Or the ), if there is no comma: if (end >= sig.size()) next = end = sig.find(')'); if (start < end && next < sig.size()) { msg.append(sig, start, end - start); msg += '('; msg.append(sig, next, ret - next); wrote_sig = true; } } } if (!wrote_sig) msg += it2->signature; msg += "\n"; } msg += "\nInvoked with: "; auto args_ = reinterpret_borrow(args_in); bool some_args = false; for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { if (!some_args) some_args = true; else msg += ", "; msg += pybind11::repr(args_[ti]); } if (kwargs_in) { auto kwargs = reinterpret_borrow(kwargs_in); if (kwargs.size() > 0) { if (some_args) msg += "; "; msg += "kwargs: "; bool first = true; for (auto kwarg : kwargs) { if (first) first = false; else msg += ", "; msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); } } } PyErr_SetString(PyExc_TypeError, msg.c_str()); return nullptr; } else if (!result) { std::string msg = "Unable to convert function return value to a " "Python type! The signature was\n\t"; msg += it->signature; PyErr_SetString(PyExc_TypeError, msg.c_str()); return nullptr; } else { if (overloads->is_constructor) { /* When a constructor ran successfully, the corresponding holder type (e.g. std::unique_ptr) must still be initialized. */ auto tinfo = get_type_info(Py_TYPE(parent.ptr())); tinfo->init_holder(parent.ptr(), nullptr); } return result.ptr(); } } }; /// Wrapper for Python extension modules class module : public object { public: PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) /// Create a new top-level Python module with the given name and docstring explicit module(const char *name, const char *doc = nullptr) { if (!options::show_user_defined_docstrings()) doc = nullptr; #if PY_MAJOR_VERSION >= 3 PyModuleDef *def = new PyModuleDef(); memset(def, 0, sizeof(PyModuleDef)); def->m_name = name; def->m_doc = doc; def->m_size = -1; Py_INCREF(def); m_ptr = PyModule_Create(def); #else m_ptr = Py_InitModule3(name, nullptr, doc); #endif if (m_ptr == nullptr) pybind11_fail("Internal error in module::module()"); inc_ref(); } /** \rst Create Python binding for a new function within the module scope. ``Func`` can be a plain C++ function, a function pointer, or a lambda function. For details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. \endrst */ template module &def(const char *name_, Func &&f, const Extra& ... extra) { cpp_function func(std::forward(f), name(name_), scope(*this), sibling(getattr(*this, name_, none())), extra...); // NB: allow overwriting here because cpp_function sets up a chain with the intention of // overwriting (and has already checked internally that it isn't overwriting non-functions). add_object(name_, func, true /* overwrite */); return *this; } /** \rst Create and return a new Python submodule with the given name and docstring. This also works recursively, i.e. .. code-block:: cpp py::module m("example", "pybind11 example plugin"); py::module m2 = m.def_submodule("sub", "A submodule of 'example'"); py::module m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); \endrst */ module def_submodule(const char *name, const char *doc = nullptr) { std::string full_name = std::string(PyModule_GetName(m_ptr)) + std::string(".") + std::string(name); auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); if (doc && options::show_user_defined_docstrings()) result.attr("__doc__") = pybind11::str(doc); attr(name) = result; return result; } /// Import and return a module or throws `error_already_set`. static module import(const char *name) { PyObject *obj = PyImport_ImportModule(name); if (!obj) throw error_already_set(); return reinterpret_steal(obj); } // Adds an object to the module using the given name. Throws if an object with the given name // already exists. // // overwrite should almost always be false: attempting to overwrite objects that pybind11 has // established will, in most cases, break things. PYBIND11_NOINLINE void add_object(const char *name, object &obj, bool overwrite = false) { if (!overwrite && hasattr(*this, name)) pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + std::string(name) + "\""); obj.inc_ref(); // PyModule_AddObject() steals a reference PyModule_AddObject(ptr(), name, obj.ptr()); } }; NAMESPACE_BEGIN(detail) /// Generic support for creating new Python heap types class generic_type : public object { template friend class class_; public: PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) protected: void initialize(const type_record &rec) { if (rec.scope && hasattr(rec.scope, rec.name)) pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + "\": an object with that name is already defined"); if (get_type_info(*rec.type)) pybind11_fail("generic_type: type \"" + std::string(rec.name) + "\" is already registered!"); m_ptr = make_new_python_type(rec); /* Register supplemental type information in C++ dict */ auto *tinfo = new detail::type_info(); tinfo->type = (PyTypeObject *) m_ptr; tinfo->type_size = rec.type_size; tinfo->operator_new = rec.operator_new; tinfo->init_holder = rec.init_holder; tinfo->dealloc = rec.dealloc; auto &internals = get_internals(); auto tindex = std::type_index(*rec.type); tinfo->direct_conversions = &internals.direct_conversions[tindex]; tinfo->default_holder = rec.default_holder; internals.registered_types_cpp[tindex] = tinfo; internals.registered_types_py[m_ptr] = tinfo; if (rec.bases.size() > 1 || rec.multiple_inheritance) mark_parents_nonsimple(tinfo->type); } /// Helper function which tags all parents of a type using mult. inheritance void mark_parents_nonsimple(PyTypeObject *value) { auto t = reinterpret_borrow(value->tp_bases); for (handle h : t) { auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); if (tinfo2) tinfo2->simple_type = false; mark_parents_nonsimple((PyTypeObject *) h.ptr()); } } void install_buffer_funcs( buffer_info *(*get_buffer)(PyObject *, void *), void *get_buffer_data) { PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; auto tinfo = detail::get_type_info(&type->ht_type); if (!type->ht_type.tp_as_buffer) pybind11_fail( "To be able to register buffer protocol support for the type '" + std::string(tinfo->type->tp_name) + "' the associated class<>(..) invocation must " "include the pybind11::buffer_protocol() annotation!"); tinfo->get_buffer = get_buffer; tinfo->get_buffer_data = get_buffer_data; } void def_property_static_impl(const char *name, handle fget, handle fset, detail::function_record *rec_fget) { const auto is_static = !(rec_fget->is_method && rec_fget->scope); const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings(); auto property = handle((PyObject *) (is_static ? get_internals().static_property_type : &PyProperty_Type)); attr(name) = property(fget.ptr() ? fget : none(), fset.ptr() ? fset : none(), /*deleter*/none(), pybind11::str(has_doc ? rec_fget->doc : "")); } }; /// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. template (T::operator new))>> void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } template void set_operator_new(...) { } /// Call class-specific delete if it exists or global otherwise. Can also be an overload set. template (T::operator delete))>> void call_operator_delete(T *p) { T::operator delete(p); } inline void call_operator_delete(void *p) { ::operator delete(p); } NAMESPACE_END(detail) template class class_ : public detail::generic_type { template using is_holder = detail::is_holder_type; template using is_subtype = detail::bool_constant::value && !std::is_same::value>; template using is_base = detail::bool_constant::value && !std::is_same::value>; // struct instead of using here to help MSVC: template struct is_valid_class_option : detail::any_of, is_subtype, is_base> {}; public: using type = type_; using type_alias = detail::first_of_t; constexpr static bool has_alias = !std::is_void::value; using holder_type = detail::first_of_t, options...>; using instance_type = detail::instance; static_assert(detail::all_of...>::value, "Unknown/invalid class_ template parameters provided"); PYBIND11_OBJECT(class_, generic_type, PyType_Check) template class_(handle scope, const char *name, const Extra &... extra) { using namespace detail; // MI can only be specified via class_ template options, not constructor parameters static_assert( none_of...>::value || // no base class arguments, or: ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base constexpr_sum(is_base::value...) == 0 && // no template option bases none_of...>::value), // no multiple_inheritance attr "Error: multiple inheritance bases must be specified via class_ template options"); type_record record; record.scope = scope; record.name = name; record.type = &typeid(type); record.type_size = sizeof(conditional_t); record.instance_size = sizeof(instance_type); record.init_holder = init_holder; record.dealloc = dealloc; record.default_holder = std::is_same>::value; set_operator_new(&record); /* Register base classes specified via template arguments to class_, if any */ bool unused[] = { (add_base(record), false)..., false }; (void) unused; /* Process optional arguments, if any */ process_attributes::init(extra..., &record); generic_type::initialize(record); if (has_alias) { auto &instances = get_internals().registered_types_cpp; instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; } } template ::value, int> = 0> static void add_base(detail::type_record &rec) { rec.add_base(&typeid(Base), [](void *src) -> void * { return static_cast(reinterpret_cast(src)); }); } template ::value, int> = 0> static void add_base(detail::type_record &) { } template class_ &def(const char *name_, Func&& f, const Extra&... extra) { cpp_function cf(std::forward(f), name(name_), is_method(*this), sibling(getattr(*this, name_, none())), extra...); attr(cf.name()) = cf; return *this; } template class_ & def_static(const char *name_, Func &&f, const Extra&... extra) { static_assert(!std::is_member_function_pointer::value, "def_static(...) called with a non-static member function pointer"); cpp_function cf(std::forward(f), name(name_), scope(*this), sibling(getattr(*this, name_, none())), extra...); attr(cf.name()) = cf; return *this; } template class_ &def(const detail::op_ &op, const Extra&... extra) { op.execute(*this, extra...); return *this; } template class_ & def_cast(const detail::op_ &op, const Extra&... extra) { op.execute_cast(*this, extra...); return *this; } template class_ &def(const detail::init &init, const Extra&... extra) { init.execute(*this, extra...); return *this; } template class_ &def(const detail::init_alias &init, const Extra&... extra) { init.execute(*this, extra...); return *this; } template class_& def_buffer(Func &&func) { struct capture { Func func; }; capture *ptr = new capture { std::forward(func) }; install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { detail::make_caster caster; if (!caster.load(obj, false)) return nullptr; return new buffer_info(((capture *) ptr)->func(caster)); }, ptr); return *this; } template class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; }, is_method(*this)), fset([pm](C &c, const D &value) { c.*pm = value; }, is_method(*this)); def_property(name, fget, fset, return_value_policy::reference_internal, extra...); return *this; } template class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; }, is_method(*this)); def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); return *this; } template class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), fset([pm](object, const D &value) { *pm = value; }, scope(*this)); def_property_static(name, fget, fset, return_value_policy::reference, extra...); return *this; } template class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); def_property_readonly_static(name, fget, return_value_policy::reference, extra...); return *this; } /// Uses return_value_policy::reference_internal by default template class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { return def_property_readonly(name, cpp_function(fget), return_value_policy::reference_internal, extra...); } /// Uses cpp_function's return_value_policy by default template class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { return def_property(name, fget, cpp_function(), extra...); } /// Uses return_value_policy::reference by default template class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); } /// Uses cpp_function's return_value_policy by default template class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { return def_property_static(name, fget, cpp_function(), extra...); } /// Uses return_value_policy::reference_internal by default template class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { return def_property(name, cpp_function(fget), fset, return_value_policy::reference_internal, extra...); } /// Uses cpp_function's return_value_policy by default template class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { return def_property_static(name, fget, fset, is_method(*this), extra...); } /// Uses return_value_policy::reference by default template class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); } /// Uses cpp_function's return_value_policy by default template class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ detail::process_attributes::init(extra..., rec_fget); if (rec_fget->doc && rec_fget->doc != doc_prev) { free(doc_prev); rec_fget->doc = strdup(rec_fget->doc); } if (rec_fset) { doc_prev = rec_fset->doc; detail::process_attributes::init(extra..., rec_fset); if (rec_fset->doc && rec_fset->doc != doc_prev) { free(doc_prev); rec_fset->doc = strdup(rec_fset->doc); } } def_property_static_impl(name, fget, fset, rec_fget); return *this; } private: /// Initialize holder object, variant 1: object derives from enable_shared_from_this template static void init_holder_helper(instance_type *inst, const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { try { new (&inst->holder) holder_type(std::static_pointer_cast(inst->value->shared_from_this())); inst->holder_constructed = true; } catch (const std::bad_weak_ptr &) { if (inst->owned) { new (&inst->holder) holder_type(inst->value); inst->holder_constructed = true; } } } static void init_holder_from_existing(instance_type *inst, const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { new (&inst->holder) holder_type(*holder_ptr); } static void init_holder_from_existing(instance_type *inst, const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { new (&inst->holder) holder_type(std::move(*const_cast(holder_ptr))); } /// Initialize holder object, variant 2: try to construct from existing holder object, if possible static void init_holder_helper(instance_type *inst, const holder_type *holder_ptr, const void * /* dummy */) { if (holder_ptr) { init_holder_from_existing(inst, holder_ptr, std::is_copy_constructible()); inst->holder_constructed = true; } else if (inst->owned || detail::always_construct_holder::value) { new (&inst->holder) holder_type(inst->value); inst->holder_constructed = true; } } /// Initialize holder object of an instance, possibly given a pointer to an existing holder static void init_holder(PyObject *inst_, const void *holder_ptr) { auto inst = (instance_type *) inst_; init_holder_helper(inst, (const holder_type *) holder_ptr, inst->value); } static void dealloc(PyObject *inst_) { instance_type *inst = (instance_type *) inst_; if (inst->holder_constructed) inst->holder.~holder_type(); else if (inst->owned) detail::call_operator_delete(inst->value); } static detail::function_record *get_function_record(handle h) { h = detail::get_function(h); return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) : nullptr; } }; /// Binds C++ enumerations and enumeration classes to Python template class enum_ : public class_ { public: using class_::def; using class_::def_property_readonly_static; using Scalar = typename std::underlying_type::type; template using arithmetic_tag = std::is_same; template enum_(const handle &scope, const char *name, const Extra&... extra) : class_(scope, name, extra...), m_entries(), m_parent(scope) { constexpr bool is_arithmetic = !std::is_same, void>::value; auto m_entries_ptr = m_entries.inc_ref().ptr(); def("__repr__", [name, m_entries_ptr](Type value) -> pybind11::str { for (const auto &kv : reinterpret_borrow(m_entries_ptr)) { if (pybind11::cast(kv.second) == value) return pybind11::str("{}.{}").format(name, kv.first); } return pybind11::str("{}.???").format(name); }); def_property_readonly_static("__members__", [m_entries_ptr](object /* self */) { dict m; for (const auto &kv : reinterpret_borrow(m_entries_ptr)) m[kv.first] = kv.second; return m; }, return_value_policy::copy); def("__init__", [](Type& value, Scalar i) { value = (Type)i; }); def("__int__", [](Type value) { return (Scalar) value; }); def("__eq__", [](const Type &value, Type *value2) { return value2 && value == *value2; }); def("__ne__", [](const Type &value, Type *value2) { return !value2 || value != *value2; }); if (is_arithmetic) { def("__lt__", [](const Type &value, Type *value2) { return value2 && value < *value2; }); def("__gt__", [](const Type &value, Type *value2) { return value2 && value > *value2; }); def("__le__", [](const Type &value, Type *value2) { return value2 && value <= *value2; }); def("__ge__", [](const Type &value, Type *value2) { return value2 && value >= *value2; }); } if (std::is_convertible::value) { // Don't provide comparison with the underlying type if the enum isn't convertible, // i.e. if Type is a scoped enum, mirroring the C++ behaviour. (NB: we explicitly // convert Type to Scalar below anyway because this needs to compile). def("__eq__", [](const Type &value, Scalar value2) { return (Scalar) value == value2; }); def("__ne__", [](const Type &value, Scalar value2) { return (Scalar) value != value2; }); if (is_arithmetic) { def("__lt__", [](const Type &value, Scalar value2) { return (Scalar) value < value2; }); def("__gt__", [](const Type &value, Scalar value2) { return (Scalar) value > value2; }); def("__le__", [](const Type &value, Scalar value2) { return (Scalar) value <= value2; }); def("__ge__", [](const Type &value, Scalar value2) { return (Scalar) value >= value2; }); def("__invert__", [](const Type &value) { return ~((Scalar) value); }); def("__and__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); def("__or__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); def("__xor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); def("__rand__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); def("__ror__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); def("__rxor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); def("__and__", [](const Type &value, const Type &value2) { return (Scalar) value & (Scalar) value2; }); def("__or__", [](const Type &value, const Type &value2) { return (Scalar) value | (Scalar) value2; }); def("__xor__", [](const Type &value, const Type &value2) { return (Scalar) value ^ (Scalar) value2; }); } } def("__hash__", [](const Type &value) { return (Scalar) value; }); // Pickling and unpickling -- needed for use with the 'multiprocessing' module def("__getstate__", [](const Type &value) { return pybind11::make_tuple((Scalar) value); }); def("__setstate__", [](Type &p, tuple t) { new (&p) Type((Type) t[0].cast()); }); } /// Export enumeration entries into the parent scope enum_& export_values() { for (const auto &kv : m_entries) m_parent.attr(kv.first) = kv.second; return *this; } /// Add an enumeration entry enum_& value(char const* name, Type value) { auto v = pybind11::cast(value, return_value_policy::copy); this->attr(name) = v; m_entries[pybind11::str(name)] = v; return *this; } private: dict m_entries; handle m_parent; }; NAMESPACE_BEGIN(detail) template struct init { template = 0> static void execute(Class &cl, const Extra&... extra) { using Base = typename Class::type; /// Function which calls a specific C++ in-place constructor cl.def("__init__", [](Base *self_, Args... args) { new (self_) Base(args...); }, extra...); } template ::value, int> = 0> static void execute(Class &cl, const Extra&... extra) { using Base = typename Class::type; using Alias = typename Class::type_alias; handle cl_type = cl; cl.def("__init__", [cl_type](handle self_, Args... args) { if (self_.get_type() == cl_type) new (self_.cast()) Base(args...); else new (self_.cast()) Alias(args...); }, extra...); } template ::value, int> = 0> static void execute(Class &cl, const Extra&... extra) { init_alias::execute(cl, extra...); } }; template struct init_alias { template ::value, int> = 0> static void execute(Class &cl, const Extra&... extra) { using Alias = typename Class::type_alias; cl.def("__init__", [](Alias *self_, Args... args) { new (self_) Alias(args...); }, extra...); } }; inline void keep_alive_impl(handle nurse, handle patient) { /* Clever approach based on weak references taken from Boost.Python */ if (!nurse || !patient) pybind11_fail("Could not activate keep_alive!"); if (patient.is_none() || nurse.is_none()) return; /* Nothing to keep alive or nothing to be kept alive by */ cpp_function disable_lifesupport( [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); weakref wr(nurse, disable_lifesupport); patient.inc_ref(); /* reference patient and leak the weak reference */ (void) wr.release(); } PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { keep_alive_impl( Nurse == 0 ? ret : Nurse <= call.args.size() ? call.args[Nurse - 1] : handle(), Patient == 0 ? ret : Patient <= call.args.size() ? call.args[Patient - 1] : handle() ); } template struct iterator_state { Iterator it; Sentinel end; bool first; }; NAMESPACE_END(detail) template detail::init init() { return detail::init(); } template detail::init_alias init_alias() { return detail::init_alias(); } /// Makes a python iterator from a first and past-the-end C++ InputIterator. template ()), typename... Extra> iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { typedef detail::iterator_state state; if (!detail::get_type_info(typeid(state), false)) { class_(handle(), "iterator") .def("__iter__", [](state &s) -> state& { return s; }) .def("__next__", [](state &s) -> ValueType { if (!s.first) ++s.it; else s.first = false; if (s.it == s.end) throw stop_iteration(); return *s.it; }, std::forward(extra)..., Policy); } return (iterator) cast(state { first, last, true }); } /// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a /// first and past-the-end InputIterator. template ()).first), typename... Extra> iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { typedef detail::iterator_state state; if (!detail::get_type_info(typeid(state), false)) { class_(handle(), "iterator") .def("__iter__", [](state &s) -> state& { return s; }) .def("__next__", [](state &s) -> KeyType { if (!s.first) ++s.it; else s.first = false; if (s.it == s.end) throw stop_iteration(); return (*s.it).first; }, std::forward(extra)..., Policy); } return (iterator) cast(state { first, last, true }); } /// Makes an iterator over values of an stl container or other container supporting /// `std::begin()`/`std::end()` template iterator make_iterator(Type &value, Extra&&... extra) { return make_iterator(std::begin(value), std::end(value), extra...); } /// Makes an iterator over the keys (`.first`) of a stl map-like container supporting /// `std::begin()`/`std::end()` template iterator make_key_iterator(Type &value, Extra&&... extra) { return make_key_iterator(std::begin(value), std::end(value), extra...); } template void implicitly_convertible() { auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { if (!detail::make_caster().load(obj, false)) return nullptr; tuple args(1); args[0] = obj; PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); if (result == nullptr) PyErr_Clear(); return result; }; if (auto tinfo = detail::get_type_info(typeid(OutputType))) tinfo->implicit_conversions.push_back(implicit_caster); else pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); } template void register_exception_translator(ExceptionTranslator&& translator) { detail::get_internals().registered_exception_translators.push_front( std::forward(translator)); } /* Wrapper to generate a new Python exception type. * * This should only be used with PyErr_SetString for now. * It is not (yet) possible to use as a py::base. * Template type argument is reserved for future use. */ template class exception : public object { public: exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { std::string full_name = scope.attr("__name__").cast() + std::string(".") + name; m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); if (hasattr(scope, name)) pybind11_fail("Error during initialization: multiple incompatible " "definitions with name \"" + std::string(name) + "\""); scope.attr(name) = *this; } // Sets the current python exception to this exception object with the given message void operator()(const char *message) { PyErr_SetString(m_ptr, message); } }; /** Registers a Python exception in `m` of the given `name` and installs an exception translator to * translate the C++ exception to the created Python exception using the exceptions what() method. * This is intended for simple exception translations; for more complex translation, register the * exception object and translator directly. */ template exception ®ister_exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { static exception ex(scope, name, base); register_exception_translator([](std::exception_ptr p) { if (!p) return; try { std::rethrow_exception(p); } catch (const CppException &e) { ex(e.what()); } }); return ex; } NAMESPACE_BEGIN(detail) PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { auto strings = tuple(args.size()); for (size_t i = 0; i < args.size(); ++i) { strings[i] = str(args[i]); } auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); auto line = sep.attr("join")(strings); object file; if (kwargs.contains("file")) { file = kwargs["file"].cast(); } else { try { file = module::import("sys").attr("stdout"); } catch (const error_already_set &) { /* If print() is called from code that is executed as part of garbage collection during interpreter shutdown, importing 'sys' can fail. Give up rather than crashing the interpreter in this case. */ return; } } auto write = file.attr("write"); write(line); write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); if (kwargs.contains("flush") && kwargs["flush"].cast()) file.attr("flush")(); } NAMESPACE_END(detail) template void print(Args &&...args) { auto c = detail::collect_arguments(std::forward(args)...); detail::print(c.args(), c.kwargs()); } #if defined(WITH_THREAD) && !defined(PYPY_VERSION) /* The functions below essentially reproduce the PyGILState_* API using a RAII * pattern, but there are a few important differences: * * 1. When acquiring the GIL from an non-main thread during the finalization * phase, the GILState API blindly terminates the calling thread, which * is often not what is wanted. This API does not do this. * * 2. The gil_scoped_release function can optionally cut the relationship * of a PyThreadState and its associated thread, which allows moving it to * another thread (this is a fairly rare/advanced use case). * * 3. The reference count of an acquired thread state can be controlled. This * can be handy to prevent cases where callbacks issued from an external * thread would otherwise constantly construct and destroy thread state data * structures. * * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an * example which uses features 2 and 3 to migrate the Python thread of * execution to another thread (to run the event loop on the original thread, * in this case). */ class gil_scoped_acquire { public: PYBIND11_NOINLINE gil_scoped_acquire() { auto const &internals = detail::get_internals(); tstate = (PyThreadState *) PyThread_get_key_value(internals.tstate); if (!tstate) { tstate = PyThreadState_New(internals.istate); #if !defined(NDEBUG) if (!tstate) pybind11_fail("scoped_acquire: could not create thread state!"); #endif tstate->gilstate_counter = 0; #if PY_MAJOR_VERSION < 3 PyThread_delete_key_value(internals.tstate); #endif PyThread_set_key_value(internals.tstate, tstate); } else { release = detail::get_thread_state_unchecked() != tstate; } if (release) { /* Work around an annoying assertion in PyThreadState_Swap */ #if defined(Py_DEBUG) PyInterpreterState *interp = tstate->interp; tstate->interp = nullptr; #endif PyEval_AcquireThread(tstate); #if defined(Py_DEBUG) tstate->interp = interp; #endif } inc_ref(); } void inc_ref() { ++tstate->gilstate_counter; } PYBIND11_NOINLINE void dec_ref() { --tstate->gilstate_counter; #if !defined(NDEBUG) if (detail::get_thread_state_unchecked() != tstate) pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); if (tstate->gilstate_counter < 0) pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); #endif if (tstate->gilstate_counter == 0) { #if !defined(NDEBUG) if (!release) pybind11_fail("scoped_acquire::dec_ref(): internal error!"); #endif PyThreadState_Clear(tstate); PyThreadState_DeleteCurrent(); PyThread_delete_key_value(detail::get_internals().tstate); release = false; } } PYBIND11_NOINLINE ~gil_scoped_acquire() { dec_ref(); if (release) PyEval_SaveThread(); } private: PyThreadState *tstate = nullptr; bool release = true; }; class gil_scoped_release { public: explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { tstate = PyEval_SaveThread(); if (disassoc) { auto key = detail::get_internals().tstate; #if PY_MAJOR_VERSION < 3 PyThread_delete_key_value(key); #else PyThread_set_key_value(key, nullptr); #endif } } ~gil_scoped_release() { if (!tstate) return; PyEval_RestoreThread(tstate); if (disassoc) { auto key = detail::get_internals().tstate; #if PY_MAJOR_VERSION < 3 PyThread_delete_key_value(key); #endif PyThread_set_key_value(key, tstate); } } private: PyThreadState *tstate; bool disassoc; }; #elif defined(PYPY_VERSION) class gil_scoped_acquire { PyGILState_STATE state; public: gil_scoped_acquire() { state = PyGILState_Ensure(); } ~gil_scoped_acquire() { PyGILState_Release(state); } }; class gil_scoped_release { PyThreadState *state; public: gil_scoped_release() { state = PyEval_SaveThread(); } ~gil_scoped_release() { PyEval_RestoreThread(state); } }; #else class gil_scoped_acquire { }; class gil_scoped_release { }; #endif error_already_set::~error_already_set() { if (value) { gil_scoped_acquire gil; clear(); } } inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { handle self = detail::get_object_handle(this_ptr, this_type); if (!self) return function(); handle type = self.get_type(); auto key = std::make_pair(type.ptr(), name); /* Cache functions that aren't overloaded in Python to avoid many costly Python dictionary lookups below */ auto &cache = detail::get_internals().inactive_overload_cache; if (cache.find(key) != cache.end()) return function(); function overload = getattr(self, name, function()); if (overload.is_cpp_function()) { cache.insert(key); return function(); } /* Don't call dispatch code if invoked from overridden function. Unfortunately this doesn't work on PyPy. */ #if !defined(PYPY_VERSION) PyFrameObject *frame = PyThreadState_Get()->frame; if (frame && (std::string) str(frame->f_code->co_name) == name && frame->f_code->co_argcount > 0) { PyFrame_FastToLocals(frame); PyObject *self_caller = PyDict_GetItem( frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); if (self_caller == self.ptr()) return function(); } #else /* PyPy currently doesn't provide a detailed cpyext emulation of frame objects, so we have to emulate this using Python. This is going to be slow..*/ dict d; d["self"] = self; d["name"] = pybind11::str(name); PyObject *result = PyRun_String( "import inspect\n" "frame = inspect.currentframe()\n" "if frame is not None:\n" " frame = frame.f_back\n" " if frame is not None and str(frame.f_code.co_name) == name and " "frame.f_code.co_argcount > 0:\n" " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" " if self_caller == self:\n" " self = None\n", Py_file_input, d.ptr(), d.ptr()); if (result == nullptr) throw error_already_set(); if ((handle) d["self"] == Py_None) return function(); Py_DECREF(result); #endif return overload; } template function get_overload(const T *this_ptr, const char *name) { auto tinfo = detail::get_type_info(typeid(T)); return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); } #define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ pybind11::gil_scoped_acquire gil; \ pybind11::function overload = pybind11::get_overload(static_cast(this), name); \ if (overload) { \ auto o = overload(__VA_ARGS__); \ if (pybind11::detail::cast_is_temporary_value_reference::value) { \ static pybind11::detail::overload_caster_t caster; \ return pybind11::detail::cast_ref(std::move(o), caster); \ } \ else return pybind11::detail::cast_safe(std::move(o)); \ } \ } #define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ return cname::fn(__VA_ARGS__) #define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ pybind11::pybind11_fail("Tried to call pure virtual function \"" #cname "::" name "\""); #define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ PYBIND11_OVERLOAD_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) #define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) NAMESPACE_END(pybind11) #if defined(_MSC_VER) # pragma warning(pop) #elif defined(__INTEL_COMPILER) /* Leave ignored warnings on */ #elif defined(__GNUG__) && !defined(__clang__) # pragma GCC diagnostic pop #endif DarkRadiant-2.5.0/libs/pybind/pybind11/pytypes.h000066400000000000000000001347361321750546400214620ustar00rootroot00000000000000/* pybind11/typeid.h: Convenience wrapper classes for basic Python types Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "common.h" #include #include NAMESPACE_BEGIN(pybind11) /* A few forward declarations */ class handle; class object; class str; class iterator; struct arg; struct arg_v; NAMESPACE_BEGIN(detail) class args_proxy; inline bool isinstance_generic(handle obj, const std::type_info &tp); // Accessor forward declarations template class accessor; namespace accessor_policies { struct obj_attr; struct str_attr; struct generic_item; struct sequence_item; struct list_item; struct tuple_item; } using obj_attr_accessor = accessor; using str_attr_accessor = accessor; using item_accessor = accessor; using sequence_accessor = accessor; using list_accessor = accessor; using tuple_accessor = accessor; /// Tag and check to identify a class which implements the Python object API class pyobject_tag { }; template using is_pyobject = std::is_base_of::type>; /** \rst A mixin class which adds common functions to `handle`, `object` and various accessors. The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. \endrst */ template class object_api : public pyobject_tag { const Derived &derived() const { return static_cast(*this); } public: /** \rst Return an iterator equivalent to calling ``iter()`` in Python. The object must be a collection which supports the iteration protocol. \endrst */ iterator begin() const; /// Return a sentinel which ends iteration. iterator end() const; /** \rst Return an internal functor to invoke the object's sequence protocol. Casting the returned ``detail::item_accessor`` instance to a `handle` or `object` subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` or `object` subclass causes a call to ``__setitem__``. \endrst */ item_accessor operator[](handle key) const; /// See above (the only difference is that they key is provided as a string literal) item_accessor operator[](const char *key) const; /** \rst Return an internal functor to access the object's attributes. Casting the returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` subclass causes a corresponding call to ``getattr``. Assigning a `handle` or `object` subclass causes a call to ``setattr``. \endrst */ obj_attr_accessor attr(handle key) const; /// See above (the only difference is that they key is provided as a string literal) str_attr_accessor attr(const char *key) const; /** \rst Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` or ``list`` for a function call. Applying another * to the result yields ** unpacking, e.g. to unpack a dict as function keyword arguments. See :ref:`calling_python_functions`. \endrst */ args_proxy operator*() const; /// Check if the given item is contained within this object, i.e. ``item in obj``. template bool contains(T &&item) const; /** \rst Assuming the Python object is a function or implements the ``__call__`` protocol, ``operator()`` invokes the underlying function, passing an arbitrary set of parameters. The result is returned as a `object` and may need to be converted back into a Python object using `handle::cast()`. When some of the arguments cannot be converted to Python objects, the function will throw a `cast_error` exception. When the Python function call fails, a `error_already_set` exception is thrown. \endrst */ template object operator()(Args &&...args) const; template PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") object call(Args&&... args) const; /// Equivalent to ``obj is None`` in Python. bool is_none() const { return derived().ptr() == Py_None; } PYBIND11_DEPRECATED("Use py::str(obj) instead") pybind11::str str() const; /// Return the object's current reference count int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } /// Return a handle to the Python type object underlying the instance handle get_type() const; }; NAMESPACE_END(detail) /** \rst Holds a reference to a Python object (no reference counting) The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a ``PyObject *`` in Python's C API). It does not perform any automatic reference counting and merely provides a basic C++ interface to various Python API functions. .. seealso:: The `object` class inherits from `handle` and adds automatic reference counting features. \endrst */ class handle : public detail::object_api { public: /// The default constructor creates a handle with a ``nullptr``-valued pointer handle() = default; /// Creates a ``handle`` from the given raw Python object pointer handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* /// Return the underlying ``PyObject *`` pointer PyObject *ptr() const { return m_ptr; } PyObject *&ptr() { return m_ptr; } /** \rst Manually increase the reference count of the Python object. Usually, it is preferable to use the `object` class which derives from `handle` and calls this function automatically. Returns a reference to itself. \endrst */ const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } /** \rst Manually decrease the reference count of the Python object. Usually, it is preferable to use the `object` class which derives from `handle` and calls this function automatically. Returns a reference to itself. \endrst */ const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } /** \rst Attempt to cast the Python object into the given C++ type. A `cast_error` will be throw upon failure. \endrst */ template T cast() const; /// Return ``true`` when the `handle` wraps a valid Python object explicit operator bool() const { return m_ptr != nullptr; } /** \rst Check that the underlying pointers are the same. Equivalent to ``obj1 is obj2`` in Python. \endrst */ bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } PYBIND11_DEPRECATED("Use handle::operator bool() instead") bool check() const { return m_ptr != nullptr; } protected: PyObject *m_ptr = nullptr; }; /** \rst Holds a reference to a Python object (with reference counting) Like `handle`, the `object` class is a thin wrapper around an arbitrary Python object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it optionally increases the object's reference count upon construction, and it *always* decreases the reference count when the `object` instance goes out of scope and is destructed. When using `object` instances consistently, it is much easier to get reference counting right at the first attempt. \endrst */ class object : public handle { public: object() = default; PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } /// Copy constructor; always increases the reference count object(const object &o) : handle(o) { inc_ref(); } /// Move constructor; steals the object from ``other`` and preserves its reference count object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } /// Destructor; automatically calls `handle::dec_ref()` ~object() { dec_ref(); } /** \rst Resets the internal pointer to ``nullptr`` without without decreasing the object's reference count. The function returns a raw handle to the original Python object. \endrst */ handle release() { PyObject *tmp = m_ptr; m_ptr = nullptr; return handle(tmp); } object& operator=(const object &other) { other.inc_ref(); dec_ref(); m_ptr = other.m_ptr; return *this; } object& operator=(object &&other) noexcept { if (this != &other) { handle temp(m_ptr); m_ptr = other.m_ptr; other.m_ptr = nullptr; temp.dec_ref(); } return *this; } // Calling cast() on an object lvalue just copies (via handle::cast) template T cast() const &; // Calling on an object rvalue does a move, if needed and/or possible template T cast() &&; protected: // Tags for choosing constructors from raw PyObject * struct borrowed_t { }; static constexpr borrowed_t borrowed{}; struct stolen_t { }; static constexpr stolen_t stolen{}; template friend T reinterpret_borrow(handle); template friend T reinterpret_steal(handle); public: // Only accessible from derived classes and the reinterpret_* functions object(handle h, borrowed_t) : handle(h) { inc_ref(); } object(handle h, stolen_t) : handle(h) { } }; /** \rst Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. The target type ``T`` must be `object` or one of its derived classes. The function doesn't do any conversions or checks. It's up to the user to make sure that the target type is correct. .. code-block:: cpp PyObject *p = PyList_GetItem(obj, index); py::object o = reinterpret_borrow(p); // or py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` \endrst */ template T reinterpret_borrow(handle h) { return {h, object::borrowed}; } /** \rst Like `reinterpret_borrow`, but steals the reference. .. code-block:: cpp PyObject *p = PyObject_Str(obj); py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` \endrst */ template T reinterpret_steal(handle h) { return {h, object::stolen}; } /** \defgroup python_builtins _ Unless stated otherwise, the following C++ functions behave the same as their Python counterparts. */ /** \ingroup python_builtins \rst Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of `object` or a class which was exposed to Python as ``py::class_``. \endrst */ template ::value, int> = 0> bool isinstance(handle obj) { return T::check_(obj); } template ::value, int> = 0> bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } template <> inline bool isinstance(handle obj) = delete; template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } /// \ingroup python_builtins /// Return true if ``obj`` is an instance of the ``type``. inline bool isinstance(handle obj, handle type) { const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); if (result == -1) throw error_already_set(); return result != 0; } /// \addtogroup python_builtins /// @{ inline bool hasattr(handle obj, handle name) { return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; } inline bool hasattr(handle obj, const char *name) { return PyObject_HasAttrString(obj.ptr(), name) == 1; } inline object getattr(handle obj, handle name) { PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); if (!result) { throw error_already_set(); } return reinterpret_steal(result); } inline object getattr(handle obj, const char *name) { PyObject *result = PyObject_GetAttrString(obj.ptr(), name); if (!result) { throw error_already_set(); } return reinterpret_steal(result); } inline object getattr(handle obj, handle name, handle default_) { if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { return reinterpret_steal(result); } else { PyErr_Clear(); return reinterpret_borrow(default_); } } inline object getattr(handle obj, const char *name, handle default_) { if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { return reinterpret_steal(result); } else { PyErr_Clear(); return reinterpret_borrow(default_); } } inline void setattr(handle obj, handle name, handle value) { if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } } inline void setattr(handle obj, const char *name, handle value) { if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } } /// @} python_builtins NAMESPACE_BEGIN(detail) inline handle get_function(handle value) { if (value) { #if PY_MAJOR_VERSION >= 3 if (PyInstanceMethod_Check(value.ptr())) value = PyInstanceMethod_GET_FUNCTION(value.ptr()); #endif if (PyMethod_Check(value.ptr())) value = PyMethod_GET_FUNCTION(value.ptr()); } return value; } // Helper aliases/functions to support implicit casting of values given to python accessors/methods. // When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes // through pybind11::cast(obj) to convert it to an `object`. template ::value, int> = 0> auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } // The following casting version is implemented in cast.h: template ::value, int> = 0> object object_or_cast(T &&o); // Match a PyObject*, which we want to convert directly to handle via its converting constructor inline handle object_or_cast(PyObject *ptr) { return ptr; } template class accessor : public object_api> { using key_type = typename Policy::key_type; public: accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } // accessor overload required to override default assignment operator (templates are not allowed // to replace default compiler-generated assignments). void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } void operator=(const accessor &a) & { operator=(handle(a)); } template void operator=(T &&value) && { Policy::set(obj, key, object_or_cast(std::forward(value))); } template void operator=(T &&value) & { get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); } template PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") explicit operator enable_if_t::value || std::is_same::value, bool>() const { return hasattr(obj, key); } template PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") explicit operator enable_if_t::value, bool>() const { return obj.contains(key); } operator object() const { return get_cache(); } PyObject *ptr() const { return get_cache().ptr(); } template T cast() const { return get_cache().template cast(); } private: object &get_cache() const { if (!cache) { cache = Policy::get(obj, key); } return cache; } private: handle obj; key_type key; mutable object cache; }; NAMESPACE_BEGIN(accessor_policies) struct obj_attr { using key_type = object; static object get(handle obj, handle key) { return getattr(obj, key); } static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } }; struct str_attr { using key_type = const char *; static object get(handle obj, const char *key) { return getattr(obj, key); } static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } }; struct generic_item { using key_type = object; static object get(handle obj, handle key) { PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); if (!result) { throw error_already_set(); } return reinterpret_steal(result); } static void set(handle obj, handle key, handle val) { if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } } }; struct sequence_item { using key_type = size_t; static object get(handle obj, size_t index) { PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); if (!result) { throw error_already_set(); } return reinterpret_steal(result); } static void set(handle obj, size_t index, handle val) { // PySequence_SetItem does not steal a reference to 'val' if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { throw error_already_set(); } } }; struct list_item { using key_type = size_t; static object get(handle obj, size_t index) { PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); if (!result) { throw error_already_set(); } return reinterpret_borrow(result); } static void set(handle obj, size_t index, handle val) { // PyList_SetItem steals a reference to 'val' if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { throw error_already_set(); } } }; struct tuple_item { using key_type = size_t; static object get(handle obj, size_t index) { PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); if (!result) { throw error_already_set(); } return reinterpret_borrow(result); } static void set(handle obj, size_t index, handle val) { // PyTuple_SetItem steals a reference to 'val' if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { throw error_already_set(); } } }; NAMESPACE_END(accessor_policies) /// STL iterator template used for tuple, list, sequence and dict template class generic_iterator : public Policy { using It = generic_iterator; public: using difference_type = ssize_t; using iterator_category = typename Policy::iterator_category; using value_type = typename Policy::value_type; using reference = typename Policy::reference; using pointer = typename Policy::pointer; generic_iterator() = default; generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } reference operator*() const { return Policy::dereference(); } reference operator[](difference_type n) const { return *(*this + n); } pointer operator->() const { return **this; } It &operator++() { Policy::increment(); return *this; } It operator++(int) { auto copy = *this; Policy::increment(); return copy; } It &operator--() { Policy::decrement(); return *this; } It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } It &operator+=(difference_type n) { Policy::advance(n); return *this; } It &operator-=(difference_type n) { Policy::advance(-n); return *this; } friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } friend It operator+(difference_type n, const It &b) { return b + n; } friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } friend bool operator==(const It &a, const It &b) { return a.equal(b); } friend bool operator!=(const It &a, const It &b) { return !(a == b); } friend bool operator< (const It &a, const It &b) { return b - a > 0; } friend bool operator> (const It &a, const It &b) { return b < a; } friend bool operator>=(const It &a, const It &b) { return !(a < b); } friend bool operator<=(const It &a, const It &b) { return !(a > b); } }; NAMESPACE_BEGIN(iterator_policies) /// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers template struct arrow_proxy { T value; arrow_proxy(T &&value) : value(std::move(value)) { } T *operator->() const { return &value; } }; /// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` class sequence_fast_readonly { protected: using iterator_category = std::random_access_iterator_tag; using value_type = handle; using reference = const handle; using pointer = arrow_proxy; sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } reference dereference() const { return *ptr; } void increment() { ++ptr; } void decrement() { --ptr; } void advance(ssize_t n) { ptr += n; } bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } private: PyObject **ptr; }; /// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` class sequence_slow_readwrite { protected: using iterator_category = std::random_access_iterator_tag; using value_type = object; using reference = sequence_accessor; using pointer = arrow_proxy; sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } reference dereference() const { return {obj, static_cast(index)}; } void increment() { ++index; } void decrement() { --index; } void advance(ssize_t n) { index += n; } bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } private: handle obj; ssize_t index; }; /// Python's dictionary protocol permits this to be a forward iterator class dict_readonly { protected: using iterator_category = std::forward_iterator_tag; using value_type = std::pair; using reference = const value_type; using pointer = arrow_proxy; dict_readonly() = default; dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } reference dereference() const { return {key, value}; } void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } bool equal(const dict_readonly &b) const { return pos == b.pos; } private: handle obj; PyObject *key, *value; ssize_t pos = -1; }; NAMESPACE_END(iterator_policies) #if !defined(PYPY_VERSION) using tuple_iterator = generic_iterator; using list_iterator = generic_iterator; #else using tuple_iterator = generic_iterator; using list_iterator = generic_iterator; #endif using sequence_iterator = generic_iterator; using dict_iterator = generic_iterator; inline bool PyIterable_Check(PyObject *obj) { PyObject *iter = PyObject_GetIter(obj); if (iter) { Py_DECREF(iter); return true; } else { PyErr_Clear(); return false; } } inline bool PyNone_Check(PyObject *o) { return o == Py_None; } inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } class kwargs_proxy : public handle { public: explicit kwargs_proxy(handle h) : handle(h) { } }; class args_proxy : public handle { public: explicit args_proxy(handle h) : handle(h) { } kwargs_proxy operator*() const { return kwargs_proxy(*this); } }; /// Python argument categories (using PEP 448 terms) template using is_keyword = std::is_base_of; template using is_s_unpacking = std::is_same; // * unpacking template using is_ds_unpacking = std::is_same; // ** unpacking template using is_positional = satisfies_none_of; template using is_keyword_or_ds = satisfies_any_of; // Call argument collector forward declarations template class simple_collector; template class unpacking_collector; NAMESPACE_END(detail) // TODO: After the deprecated constructors are removed, this macro can be simplified by // inheriting ctors: `using Parent::Parent`. It's not an option right now because // the `using` statement triggers the parent deprecation warning even if the ctor // isn't even used. #define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ public: \ PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed) : Parent(h, stolen)) { } \ Name(handle h, borrowed_t) : Parent(h, borrowed) { } \ Name(handle h, stolen_t) : Parent(h, stolen) { } \ PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } #define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ Name(const object &o) : Parent(ConvertFun(o.ptr()), stolen) { if (!m_ptr) throw error_already_set(); } #define PYBIND11_OBJECT(Name, Parent, CheckFun) \ PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ Name(const object &o) : Parent(o) { } \ Name(object &&o) : Parent(std::move(o)) { } #define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ PYBIND11_OBJECT(Name, Parent, CheckFun) \ Name() : Parent() { } /// \addtogroup pytypes /// @{ /** \rst Wraps a Python iterator so that it can also be used as a C++ input iterator Caveat: copying an iterator does not (and cannot) clone the internal state of the Python iterable. This also applies to the post-increment operator. This iterator should only be used to retrieve the current value using ``operator*()``. \endrst */ class iterator : public object { public: using iterator_category = std::input_iterator_tag; using difference_type = ssize_t; using value_type = handle; using reference = const handle; using pointer = const handle *; PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) iterator& operator++() { advance(); return *this; } iterator operator++(int) { auto rv = *this; advance(); return rv; } reference operator*() const { if (m_ptr && !value.ptr()) { auto& self = const_cast(*this); self.advance(); } return value; } pointer operator->() const { operator*(); return &value; } /** \rst The value which marks the end of the iteration. ``it == iterator::sentinel()`` is equivalent to catching ``StopIteration`` in Python. .. code-block:: cpp void foo(py::iterator it) { while (it != py::iterator::sentinel()) { // use `*it` ++it; } } \endrst */ static iterator sentinel() { return {}; } friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } private: void advance() { value = reinterpret_steal(PyIter_Next(m_ptr)); if (PyErr_Occurred()) { throw error_already_set(); } } private: object value = {}; }; class iterable : public object { public: PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) }; class bytes; class str : public object { public: PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) str(const char *c, size_t n) : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen) { if (!m_ptr) pybind11_fail("Could not allocate string object!"); } // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects str(const char *c = "") : object(PyUnicode_FromString(c), stolen) { if (!m_ptr) pybind11_fail("Could not allocate string object!"); } str(const std::string &s) : str(s.data(), s.size()) { } explicit str(const bytes &b); /** \rst Return a string representation of the object. This is analogous to the ``str()`` function in Python. \endrst */ explicit str(handle h) : object(raw_str(h.ptr()), stolen) { } operator std::string() const { object temp = *this; if (PyUnicode_Check(m_ptr)) { temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); if (!temp) pybind11_fail("Unable to extract string contents! (encoding issue)"); } char *buffer; ssize_t length; if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) pybind11_fail("Unable to extract string contents! (invalid type)"); return std::string(buffer, (size_t) length); } template str format(Args &&...args) const { return attr("format")(std::forward(args)...); } private: /// Return string representation -- always returns a new reference, even if already a str static PyObject *raw_str(PyObject *op) { PyObject *str_value = PyObject_Str(op); #if PY_MAJOR_VERSION < 3 if (!str_value) throw error_already_set(); PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); Py_XDECREF(str_value); str_value = unicode; #endif return str_value; } }; /// @} pytypes inline namespace literals { /** \rst String literal version of `str` \endrst */ inline str operator"" _s(const char *s, size_t size) { return {s, size}; } } /// \addtogroup pytypes /// @{ class bytes : public object { public: PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) // Allow implicit conversion: bytes(const char *c = "") : object(PYBIND11_BYTES_FROM_STRING(c), stolen) { if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); } bytes(const char *c, size_t n) : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen) { if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); } // Allow implicit conversion: bytes(const std::string &s) : bytes(s.data(), s.size()) { } explicit bytes(const pybind11::str &s); operator std::string() const { char *buffer; ssize_t length; if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) pybind11_fail("Unable to extract bytes contents!"); return std::string(buffer, (size_t) length); } }; inline bytes::bytes(const pybind11::str &s) { object temp = s; if (PyUnicode_Check(s.ptr())) { temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); if (!temp) pybind11_fail("Unable to extract string contents! (encoding issue)"); } char *buffer; ssize_t length; if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) pybind11_fail("Unable to extract string contents! (invalid type)"); auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); if (!obj) pybind11_fail("Could not allocate bytes object!"); m_ptr = obj.release().ptr(); } inline str::str(const bytes& b) { char *buffer; ssize_t length; if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) pybind11_fail("Unable to extract bytes contents!"); auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); if (!obj) pybind11_fail("Could not allocate string object!"); m_ptr = obj.release().ptr(); } class none : public object { public: PYBIND11_OBJECT(none, object, detail::PyNone_Check) none() : object(Py_None, borrowed) { } }; class bool_ : public object { public: PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) bool_() : object(Py_False, borrowed) { } // Allow implicit conversion from and to `bool`: bool_(bool value) : object(value ? Py_True : Py_False, borrowed) { } operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } private: /// Return the truth value of an object -- always returns a new reference static PyObject *raw_bool(PyObject *op) { const auto value = PyObject_IsTrue(op); if (value == -1) return nullptr; return handle(value ? Py_True : Py_False).inc_ref().ptr(); } }; class int_ : public object { public: PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) int_() : object(PyLong_FromLong(0), stolen) { } // Allow implicit conversion from C++ integral types: template ::value, int> = 0> int_(T value) { if (sizeof(T) <= sizeof(long)) { if (std::is_signed::value) m_ptr = PyLong_FromLong((long) value); else m_ptr = PyLong_FromUnsignedLong((unsigned long) value); } else { if (std::is_signed::value) m_ptr = PyLong_FromLongLong((long long) value); else m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); } if (!m_ptr) pybind11_fail("Could not allocate int object!"); } template ::value, int> = 0> operator T() const { if (sizeof(T) <= sizeof(long)) { if (std::is_signed::value) return (T) PyLong_AsLong(m_ptr); else return (T) PyLong_AsUnsignedLong(m_ptr); } else { if (std::is_signed::value) return (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); else return (T) PYBIND11_LONG_AS_UNSIGNED_LONGLONG(m_ptr); } } }; class float_ : public object { public: PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) // Allow implicit conversion from float/double: float_(float value) : object(PyFloat_FromDouble((double) value), stolen) { if (!m_ptr) pybind11_fail("Could not allocate float object!"); } float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen) { if (!m_ptr) pybind11_fail("Could not allocate float object!"); } operator float() const { return (float) PyFloat_AsDouble(m_ptr); } operator double() const { return (double) PyFloat_AsDouble(m_ptr); } }; class weakref : public object { public: PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) explicit weakref(handle obj, handle callback = {}) : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen) { if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); } }; class slice : public object { public: PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) slice(ssize_t start_, ssize_t stop_, ssize_t step_) { int_ start(start_), stop(stop_), step(step_); m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); if (!m_ptr) pybind11_fail("Could not allocate slice object!"); } bool compute(size_t length, size_t *start, size_t *stop, size_t *step, size_t *slicelength) const { return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, (ssize_t) length, (ssize_t *) start, (ssize_t *) stop, (ssize_t *) step, (ssize_t *) slicelength) == 0; } }; class capsule : public object { public: PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed) : object(ptr, stolen)) { } explicit capsule(const void *value) : object(PyCapsule_New(const_cast(value), nullptr, nullptr), stolen) { if (!m_ptr) pybind11_fail("Could not allocate capsule object!"); } PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") capsule(const void *value, void (*destruct)(PyObject *)) : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen) { if (!m_ptr) pybind11_fail("Could not allocate capsule object!"); } capsule(const void *value, void (*destructor)(void *)) { m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); void *ptr = PyCapsule_GetPointer(o, nullptr); destructor(ptr); }); if (!m_ptr) pybind11_fail("Could not allocate capsule object!"); if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) pybind11_fail("Could not set capsule context!"); } capsule(void (*destructor)()) { m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); destructor(); }); if (!m_ptr) pybind11_fail("Could not allocate capsule object!"); } template operator T *() const { T * result = static_cast(PyCapsule_GetPointer(m_ptr, nullptr)); if (!result) pybind11_fail("Unable to extract capsule contents!"); return result; } }; class tuple : public object { public: PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen) { if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); } size_t size() const { return (size_t) PyTuple_Size(m_ptr); } detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } detail::tuple_iterator begin() const { return {*this, 0}; } detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } }; class dict : public object { public: PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) dict() : object(PyDict_New(), stolen) { if (!m_ptr) pybind11_fail("Could not allocate dict object!"); } template ...>::value>, // MSVC workaround: it can't compile an out-of-line definition, so defer the collector typename collector = detail::deferred_t, Args...>> explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } size_t size() const { return (size_t) PyDict_Size(m_ptr); } detail::dict_iterator begin() const { return {*this, 0}; } detail::dict_iterator end() const { return {}; } void clear() const { PyDict_Clear(ptr()); } bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } private: /// Call the `dict` Python type -- always returns a new reference static PyObject *raw_dict(PyObject *op) { if (PyDict_Check(op)) return handle(op).inc_ref().ptr(); return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); } }; class sequence : public object { public: PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) size_t size() const { return (size_t) PySequence_Size(m_ptr); } detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } detail::sequence_iterator begin() const { return {*this, 0}; } detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } }; class list : public object { public: PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen) { if (!m_ptr) pybind11_fail("Could not allocate list object!"); } size_t size() const { return (size_t) PyList_Size(m_ptr); } detail::list_accessor operator[](size_t index) const { return {*this, index}; } detail::list_iterator begin() const { return {*this, 0}; } detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } template void append(T &&val) const { PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); } }; class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; class set : public object { public: PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) set() : object(PySet_New(nullptr), stolen) { if (!m_ptr) pybind11_fail("Could not allocate set object!"); } size_t size() const { return (size_t) PySet_Size(m_ptr); } template bool add(T &&val) const { return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; } void clear() const { PySet_Clear(m_ptr); } }; class function : public object { public: PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) bool is_cpp_function() const { handle fun = detail::get_function(m_ptr); return fun && PyCFunction_Check(fun.ptr()); } }; class buffer : public object { public: PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) buffer_info request(bool writable = false) { int flags = PyBUF_STRIDES | PyBUF_FORMAT; if (writable) flags |= PyBUF_WRITABLE; Py_buffer *view = new Py_buffer(); if (PyObject_GetBuffer(m_ptr, view, flags) != 0) throw error_already_set(); return buffer_info(view); } }; class memoryview : public object { public: explicit memoryview(const buffer_info& info) { static Py_buffer buf { }; // Py_buffer uses signed sizes, strides and shape!.. static std::vector py_strides { }; static std::vector py_shape { }; buf.buf = info.ptr; buf.itemsize = (Py_ssize_t) info.itemsize; buf.format = const_cast(info.format.c_str()); buf.ndim = (int) info.ndim; buf.len = (Py_ssize_t) info.size; py_strides.clear(); py_shape.clear(); for (size_t i = 0; i < info.ndim; ++i) { py_strides.push_back((Py_ssize_t) info.strides[i]); py_shape.push_back((Py_ssize_t) info.shape[i]); } buf.strides = py_strides.data(); buf.shape = py_shape.data(); buf.suboffsets = nullptr; buf.readonly = false; buf.internal = nullptr; m_ptr = PyMemoryView_FromBuffer(&buf); if (!m_ptr) pybind11_fail("Unable to create memoryview from buffer descriptor"); } PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) }; /// @} pytypes /// \addtogroup python_builtins /// @{ inline size_t len(handle h) { ssize_t result = PyObject_Length(h.ptr()); if (result < 0) pybind11_fail("Unable to compute length of object"); return (size_t) result; } inline str repr(handle h) { PyObject *str_value = PyObject_Repr(h.ptr()); if (!str_value) throw error_already_set(); #if PY_MAJOR_VERSION < 3 PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); Py_XDECREF(str_value); str_value = unicode; if (!str_value) throw error_already_set(); #endif return reinterpret_steal(str_value); } inline iterator iter(handle obj) { PyObject *result = PyObject_GetIter(obj.ptr()); if (!result) { throw error_already_set(); } return reinterpret_steal(result); } /// @} python_builtins NAMESPACE_BEGIN(detail) template iterator object_api::begin() const { return iter(derived()); } template iterator object_api::end() const { return iterator::sentinel(); } template item_accessor object_api::operator[](handle key) const { return {derived(), reinterpret_borrow(key)}; } template item_accessor object_api::operator[](const char *key) const { return {derived(), pybind11::str(key)}; } template obj_attr_accessor object_api::attr(handle key) const { return {derived(), reinterpret_borrow(key)}; } template str_attr_accessor object_api::attr(const char *key) const { return {derived(), key}; } template args_proxy object_api::operator*() const { return args_proxy(derived().ptr()); } template template bool object_api::contains(T &&item) const { return attr("__contains__")(std::forward(item)).template cast(); } template pybind11::str object_api::str() const { return pybind11::str(derived()); } template handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } NAMESPACE_END(detail) NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/pybind/pybind11/stl.h000066400000000000000000000217671321750546400205460ustar00rootroot00000000000000/* pybind11/stl.h: Transparent conversion for STL data types Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "pybind11.h" #include #include #include #include #include #include #include #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4127) // warning C4127: Conditional expression is constant #endif #ifdef __has_include // std::optional (but including it in c++14 mode isn't allowed) # if defined(PYBIND11_CPP17) && __has_include() # include # define PYBIND11_HAS_OPTIONAL 1 # endif // std::experimental::optional (but not allowed in c++11 mode) # if defined(PYBIND11_CPP14) && __has_include() # include # if __cpp_lib_experimental_optional // just in case # define PYBIND11_HAS_EXP_OPTIONAL 1 # endif # endif #endif NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) template struct set_caster { using type = Type; using key_conv = make_caster; bool load(handle src, bool convert) { if (!isinstance(src)) return false; auto s = reinterpret_borrow(src); value.clear(); key_conv conv; for (auto entry : s) { if (!conv.load(entry, convert)) return false; value.insert(cast_op(conv)); } return true; } static handle cast(const type &src, return_value_policy policy, handle parent) { pybind11::set s; for (auto const &value: src) { auto value_ = reinterpret_steal(key_conv::cast(value, policy, parent)); if (!value_ || !s.add(value_)) return handle(); } return s.release(); } PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name() + _("]")); }; template struct map_caster { using key_conv = make_caster; using value_conv = make_caster; bool load(handle src, bool convert) { if (!isinstance(src)) return false; auto d = reinterpret_borrow(src); key_conv kconv; value_conv vconv; value.clear(); for (auto it : d) { if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) return false; value.emplace(cast_op(kconv), cast_op(vconv)); } return true; } static handle cast(const Type &src, return_value_policy policy, handle parent) { dict d; for (auto const &kv: src) { auto key = reinterpret_steal(key_conv::cast(kv.first, policy, parent)); auto value = reinterpret_steal(value_conv::cast(kv.second, policy, parent)); if (!key || !value) return handle(); d[key] = value; } return d.release(); } PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name() + _(", ") + value_conv::name() + _("]")); }; template struct list_caster { using value_conv = make_caster; bool load(handle src, bool convert) { if (!isinstance(src)) return false; auto s = reinterpret_borrow(src); value_conv conv; value.clear(); reserve_maybe(s, &value); for (auto it : s) { if (!conv.load(it, convert)) return false; value.push_back(cast_op(conv)); } return true; } private: template ().reserve(0)), void>::value, int> = 0> void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } void reserve_maybe(sequence, void *) { } public: static handle cast(const Type &src, return_value_policy policy, handle parent) { list l(src.size()); size_t index = 0; for (auto const &value: src) { auto value_ = reinterpret_steal(value_conv::cast(value, policy, parent)); if (!value_) return handle(); PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference } return l.release(); } PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name() + _("]")); }; template struct type_caster> : list_caster, Type> { }; template struct type_caster> : list_caster, Type> { }; template struct array_caster { using value_conv = make_caster; private: template bool require_size(enable_if_t size) { if (value.size() != size) value.resize(size); return true; } template bool require_size(enable_if_t size) { return size == Size; } public: bool load(handle src, bool convert) { if (!isinstance(src)) return false; auto l = reinterpret_borrow(src); if (!require_size(l.size())) return false; value_conv conv; size_t ctr = 0; for (auto it : l) { if (!conv.load(it, convert)) return false; value[ctr++] = cast_op(conv); } return true; } static handle cast(const ArrayType &src, return_value_policy policy, handle parent) { list l(src.size()); size_t index = 0; for (auto const &value: src) { auto value_ = reinterpret_steal(value_conv::cast(value, policy, parent)); if (!value_) return handle(); PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference } return l.release(); } PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name() + _(_(""), _("[") + _() + _("]")) + _("]")); }; template struct type_caster> : array_caster, Type, false, Size> { }; template struct type_caster> : array_caster, Type, true> { }; template struct type_caster> : set_caster, Key> { }; template struct type_caster> : set_caster, Key> { }; template struct type_caster> : map_caster, Key, Value> { }; template struct type_caster> : map_caster, Key, Value> { }; // This type caster is intended to be used for std::optional and std::experimental::optional template struct optional_caster { using value_conv = make_caster; static handle cast(const T& src, return_value_policy policy, handle parent) { if (!src) return none().inc_ref(); return value_conv::cast(*src, policy, parent); } bool load(handle src, bool convert) { if (!src) { return false; } else if (src.is_none()) { value = {}; // nullopt return true; } value_conv inner_caster; if (!inner_caster.load(src, convert)) return false; value.emplace(cast_op(inner_caster)); return true; } PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name() + _("]")); }; #if PYBIND11_HAS_OPTIONAL template struct type_caster> : public optional_caster> {}; template<> struct type_caster : public void_caster {}; #endif #if PYBIND11_HAS_EXP_OPTIONAL template struct type_caster> : public optional_caster> {}; template<> struct type_caster : public void_caster {}; #endif NAMESPACE_END(detail) inline std::ostream &operator<<(std::ostream &os, const handle &obj) { os << (std::string) str(obj); return os; } NAMESPACE_END(pybind11) #if defined(_MSC_VER) #pragma warning(pop) #endif DarkRadiant-2.5.0/libs/pybind/pybind11/stl_bind.h000066400000000000000000000504321321750546400215310ustar00rootroot00000000000000/* pybind11/std_bind.h: Binding generators for STL data types Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include "common.h" #include "operators.h" #include #include NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) /* SFINAE helper class used by 'is_comparable */ template struct container_traits { template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); template static std::false_type test_comparable(...); template static std::true_type test_value(typename T2::value_type *); template static std::false_type test_value(...); template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); template static std::false_type test_pair(...); static constexpr const bool is_comparable = std::is_same(nullptr))>::value; static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; static constexpr const bool is_vector = std::is_same(nullptr))>::value; static constexpr const bool is_element = !is_pair && !is_vector; }; /* Default: is_comparable -> std::false_type */ template struct is_comparable : std::false_type { }; /* For non-map data structures, check whether operator== can be instantiated */ template struct is_comparable< T, enable_if_t::is_element && container_traits::is_comparable>> : std::true_type { }; /* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ template struct is_comparable::is_vector>> { static constexpr const bool value = is_comparable::value; }; /* For pairs, recursively check the two data types */ template struct is_comparable::is_pair>> { static constexpr const bool value = is_comparable::value && is_comparable::value; }; /* Fallback functions */ template void vector_if_copy_constructible(const Args &...) { } template void vector_if_equal_operator(const Args &...) { } template void vector_if_insertion_operator(const Args &...) { } template void vector_modifiers(const Args &...) { } template void vector_if_copy_constructible(enable_if_t< std::is_copy_constructible::value && std::is_copy_constructible::value, Class_> &cl) { cl.def(init(), "Copy constructor"); } template void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { using T = typename Vector::value_type; cl.def(self == self); cl.def(self != self); cl.def("count", [](const Vector &v, const T &x) { return std::count(v.begin(), v.end(), x); }, arg("x"), "Return the number of times ``x`` appears in the list" ); cl.def("remove", [](Vector &v, const T &x) { auto p = std::find(v.begin(), v.end(), x); if (p != v.end()) v.erase(p); else throw value_error(); }, arg("x"), "Remove the first item from the list whose value is x. " "It is an error if there is no such item." ); cl.def("__contains__", [](const Vector &v, const T &x) { return std::find(v.begin(), v.end(), x) != v.end(); }, arg("x"), "Return true the container contains ``x``" ); } // Vector modifiers -- requires a copyable vector_type: // (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems // silly to allow deletion but not insertion, so include them here too.) template void vector_modifiers(enable_if_t::value, Class_> &cl) { using T = typename Vector::value_type; using SizeType = typename Vector::size_type; using DiffType = typename Vector::difference_type; cl.def("append", [](Vector &v, const T &value) { v.push_back(value); }, arg("x"), "Add an item to the end of the list"); cl.def("__init__", [](Vector &v, iterable it) { new (&v) Vector(); try { v.reserve(len(it)); for (handle h : it) v.push_back(h.cast()); } catch (...) { v.~Vector(); throw; } }); cl.def("extend", [](Vector &v, const Vector &src) { v.reserve(v.size() + src.size()); v.insert(v.end(), src.begin(), src.end()); }, arg("L"), "Extend the list by appending all the items in the given list" ); cl.def("insert", [](Vector &v, SizeType i, const T &x) { v.insert(v.begin() + (DiffType) i, x); }, arg("i") , arg("x"), "Insert an item at a given position." ); cl.def("pop", [](Vector &v) { if (v.empty()) throw index_error(); T t = v.back(); v.pop_back(); return t; }, "Remove and return the last item" ); cl.def("pop", [](Vector &v, SizeType i) { if (i >= v.size()) throw index_error(); T t = v[i]; v.erase(v.begin() + (DiffType) i); return t; }, arg("i"), "Remove and return the item at index ``i``" ); cl.def("__setitem__", [](Vector &v, SizeType i, const T &t) { if (i >= v.size()) throw index_error(); v[i] = t; } ); /// Slicing protocol cl.def("__getitem__", [](const Vector &v, slice slice) -> Vector * { size_t start, stop, step, slicelength; if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) throw error_already_set(); Vector *seq = new Vector(); seq->reserve((size_t) slicelength); for (size_t i=0; ipush_back(v[start]); start += step; } return seq; }, arg("s"), "Retrieve list elements using a slice object" ); cl.def("__setitem__", [](Vector &v, slice slice, const Vector &value) { size_t start, stop, step, slicelength; if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) throw error_already_set(); if (slicelength != value.size()) throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); for (size_t i=0; i= v.size()) throw index_error(); v.erase(v.begin() + DiffType(i)); }, "Delete the list elements at index ``i``" ); cl.def("__delitem__", [](Vector &v, slice slice) { size_t start, stop, step, slicelength; if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) throw error_already_set(); if (step == 1 && false) { v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); } else { for (size_t i = 0; i < slicelength; ++i) { v.erase(v.begin() + DiffType(start)); start += step - 1; } } }, "Delete list elements using a slice object" ); } // If the type has an operator[] that doesn't return a reference (most notably std::vector), // we have to access by copying; otherwise we return by reference. template using vector_needs_copy = negation< std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; // The usual case: access and iterate by reference template void vector_accessor(enable_if_t::value, Class_> &cl) { using T = typename Vector::value_type; using SizeType = typename Vector::size_type; using ItType = typename Vector::iterator; cl.def("__getitem__", [](Vector &v, SizeType i) -> T & { if (i >= v.size()) throw index_error(); return v[i]; }, return_value_policy::reference_internal // ref + keepalive ); cl.def("__iter__", [](Vector &v) { return make_iterator< return_value_policy::reference_internal, ItType, ItType, T&>( v.begin(), v.end()); }, keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ ); } // The case for special objects, like std::vector, that have to be returned-by-copy: template void vector_accessor(enable_if_t::value, Class_> &cl) { using T = typename Vector::value_type; using SizeType = typename Vector::size_type; using ItType = typename Vector::iterator; cl.def("__getitem__", [](const Vector &v, SizeType i) -> T { if (i >= v.size()) throw index_error(); return v[i]; } ); cl.def("__iter__", [](Vector &v) { return make_iterator< return_value_policy::copy, ItType, ItType, T>( v.begin(), v.end()); }, keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ ); } template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) -> decltype(std::declval() << std::declval(), void()) { using size_type = typename Vector::size_type; cl.def("__repr__", [name](Vector &v) { std::ostringstream s; s << name << '['; for (size_type i=0; i < v.size(); ++i) { s << v[i]; if (i != v.size() - 1) s << ", "; } s << ']'; return s.str(); }, "Return the canonical string representation of this list." ); } // Provide the buffer interface for vectors if we have data() and we have a format for it // GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer template struct vector_has_data_and_format : std::false_type {}; template struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; // Add the buffer interface to a vector template enable_if_t...>::value> vector_buffer(Class_& cl) { using T = typename Vector::value_type; static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here format_descriptor::format(); cl.def_buffer([](Vector& v) -> buffer_info { return buffer_info(v.data(), sizeof(T), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); }); cl.def("__init__", [](Vector& vec, buffer buf) { auto info = buf.request(); if (info.ndim != 1 || info.strides[0] <= 0 || info.strides[0] % sizeof(T)) throw type_error("Only valid 1D buffers can be copied to a vector"); if (!detail::compare_buffer_info::compare(info) || sizeof(T) != info.itemsize) throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); new (&vec) Vector(); vec.reserve(info.shape[0]); T *p = static_cast(info.ptr); auto step = info.strides[0] / sizeof(T); T *end = p + info.shape[0] * step; for (; p < end; p += step) vec.push_back(*p); }); return; } template enable_if_t...>::value> vector_buffer(Class_&) {} NAMESPACE_END(detail) // // std::vector // template , typename... Args> class_ bind_vector(module &m, std::string const &name, Args&&... args) { using Class_ = class_; Class_ cl(m, name.c_str(), std::forward(args)...); // Declare the buffer interface if a buffer_protocol() is passed in detail::vector_buffer(cl); cl.def(init<>()); // Register copy constructor (if possible) detail::vector_if_copy_constructible(cl); // Register comparison-related operators and functions (if possible) detail::vector_if_equal_operator(cl); // Register stream insertion operator (if possible) detail::vector_if_insertion_operator(cl, name); // Modifiers require copyable vector value type detail::vector_modifiers(cl); // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive detail::vector_accessor(cl); cl.def("__bool__", [](const Vector &v) -> bool { return !v.empty(); }, "Check whether the list is nonempty" ); cl.def("__len__", &Vector::size); #if 0 // C++ style functions deprecated, leaving it here as an example cl.def(init()); cl.def("resize", (void (Vector::*) (size_type count)) & Vector::resize, "changes the number of elements stored"); cl.def("erase", [](Vector &v, SizeType i) { if (i >= v.size()) throw index_error(); v.erase(v.begin() + i); }, "erases element at index ``i``"); cl.def("empty", &Vector::empty, "checks whether the container is empty"); cl.def("size", &Vector::size, "returns the number of elements"); cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); cl.def("pop_back", &Vector::pop_back, "removes the last element"); cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); cl.def("reserve", &Vector::reserve, "reserves storage"); cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); cl.def("clear", &Vector::clear, "clears the contents"); cl.def("swap", &Vector::swap, "swaps the contents"); cl.def("front", [](Vector &v) { if (v.size()) return v.front(); else throw index_error(); }, "access the first element"); cl.def("back", [](Vector &v) { if (v.size()) return v.back(); else throw index_error(); }, "access the last element "); #endif return cl; } // // std::map, std::unordered_map // NAMESPACE_BEGIN(detail) /* Fallback functions */ template void map_if_insertion_operator(const Args &...) { } template void map_assignment(const Args &...) { } // Map assignment when copy-assignable: just copy the value template void map_assignment(enable_if_t::value, Class_> &cl) { using KeyType = typename Map::key_type; using MappedType = typename Map::mapped_type; cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) { auto it = m.find(k); if (it != m.end()) it->second = v; else m.emplace(k, v); } ); } // Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting template void map_assignment(enable_if_t< !std::is_copy_assignable::value && std::is_copy_constructible::value, Class_> &cl) { using KeyType = typename Map::key_type; using MappedType = typename Map::mapped_type; cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) { // We can't use m[k] = v; because value type might not be default constructable auto r = m.emplace(k, v); if (!r.second) { // value type is not copy assignable so the only way to insert it is to erase it first... m.erase(r.first); m.emplace(k, v); } } ); } template auto map_if_insertion_operator(Class_ &cl, std::string const &name) -> decltype(std::declval() << std::declval() << std::declval(), void()) { cl.def("__repr__", [name](Map &m) { std::ostringstream s; s << name << '{'; bool f = false; for (auto const &kv : m) { if (f) s << ", "; s << kv.first << ": " << kv.second; f = true; } s << '}'; return s.str(); }, "Return the canonical string representation of this map." ); } NAMESPACE_END(detail) template , typename... Args> class_ bind_map(module &m, const std::string &name, Args&&... args) { using KeyType = typename Map::key_type; using MappedType = typename Map::mapped_type; using Class_ = class_; Class_ cl(m, name.c_str(), std::forward(args)...); cl.def(init<>()); // Register stream insertion operator (if possible) detail::map_if_insertion_operator(cl, name); cl.def("__bool__", [](const Map &m) -> bool { return !m.empty(); }, "Check whether the map is nonempty" ); cl.def("__iter__", [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ ); cl.def("items", [](Map &m) { return make_iterator(m.begin(), m.end()); }, keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ ); cl.def("__getitem__", [](Map &m, const KeyType &k) -> MappedType & { auto it = m.find(k); if (it == m.end()) throw key_error(); return it->second; }, return_value_policy::reference_internal // ref + keepalive ); // Assignment provided only if the type is copyable detail::map_assignment(cl); cl.def("__delitem__", [](Map &m, const KeyType &k) { auto it = m.find(k); if (it == m.end()) throw key_error(); return m.erase(it); } ); cl.def("__len__", &Map::size); return cl; } NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/pybind/pybind11/typeid.h000066400000000000000000000025721321750546400212330ustar00rootroot00000000000000/* pybind11/typeid.h: Compiler-independent access to type identifiers Copyright (c) 2016 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include #include #if defined(__GNUG__) #include #endif NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) /// Erase all occurrences of a substring inline void erase_all(std::string &string, const std::string &search) { for (size_t pos = 0;;) { pos = string.find(search, pos); if (pos == std::string::npos) break; string.erase(pos, search.length()); } } PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { #if defined(__GNUG__) int status = 0; std::unique_ptr res { abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; if (status == 0) name = res.get(); #else detail::erase_all(name, "class "); detail::erase_all(name, "struct "); detail::erase_all(name, "enum "); #endif detail::erase_all(name, "pybind11::"); } NAMESPACE_END(detail) /// Return a string representation of a C++ type template static std::string type_id() { std::string name(typeid(T).name()); detail::clean_type_id(name); return name; } NAMESPACE_END(pybind11) DarkRadiant-2.5.0/libs/registry/000077500000000000000000000000001321750546400165125ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/registry/CachedKey.h000066400000000000000000000017151321750546400205070ustar00rootroot00000000000000#pragma once #include "registry.h" namespace registry { /** * \brief * Simple value caching class for a registry key. * * This can be used when a registry key needs to be read frequently but only * written rarely. It stores a copy of the value converted to its given type, * and uses the registry signal to update its copy when the value changes. */ template class CachedKey: public sigc::trackable { const std::string _key; T _cachedValue; private: void updateCachedValue() { _cachedValue = registry::getValue(_key); } public: /// Construct a CachedKey to observe the given registry key CachedKey(const std::string& key) : _key(key) { updateCachedValue(); GlobalRegistry().signalForKey(key).connect( sigc::mem_fun(this, &CachedKey::updateCachedValue) ); } /// Return the current value T get() const { return _cachedValue; } }; } DarkRadiant-2.5.0/libs/registry/Widgets.h000066400000000000000000000137131321750546400202760ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "registry.h" #include "buffer.h" namespace registry { /** * Various bind() overloads to let widgets export their values to the registry * as soon as they fire their changes signal. The value * will be initialised with the one currently present in the registry. * * Note: due to the use of lambdas it's not possible to disconnect * the widget's after calling bind(). The widget will keep writing * its value to the registry, unless it's destroyed. */ inline void bindWidget(wxSpinCtrlDouble* spinCtrl, const std::string& key) { // Set initial value then connect to changed signal if (GlobalRegistry().keyExists(key)) { spinCtrl->SetValue(registry::getValue(key)); } spinCtrl->Bind(wxEVT_SPINCTRLDOUBLE, [=] (wxSpinDoubleEvent& ev) { registry::setValue(key, spinCtrl->GetValue()); ev.Skip(); }); } inline void bindWidget(wxTextCtrl* text, const std::string& key) { // Set initial value then connect to changed signal if (GlobalRegistry().keyExists(key)) { text->SetValue(registry::getValue(key)); } text->Bind(wxEVT_TEXT, [=] (wxCommandEvent& ev) { registry::setValue(key, text->GetValue().ToStdString()); ev.Skip(); }); } inline void bindWidget(wxCheckBox* checkbox, const std::string& key) { // Set initial value then connect to changed signal if (GlobalRegistry().keyExists(key)) { checkbox->SetValue(registry::getValue(key)); } checkbox->Bind(wxEVT_CHECKBOX, [=] (wxCommandEvent& ev) { registry::setValue(key, checkbox->GetValue() ? "1" : "0"); ev.Skip(); }); } inline void bindWidget(wxToggleButton* toggleButton, const std::string& key) { // Set initial value then connect to changed signal if (GlobalRegistry().keyExists(key)) { toggleButton->SetValue(registry::getValue(key)); } toggleButton->Bind(wxEVT_TOGGLEBUTTON, [=](wxCommandEvent& ev) { registry::setValue(key, toggleButton->GetValue() ? "1" : "0"); ev.Skip(); }); } // ------------- Variants supporting registry::Buffer --------------------- inline void bindWidgetToBufferedKey(wxCheckBox* checkbox, const std::string& key, Buffer& buffer, sigc::signal& resetSignal) { // Set initial value then connect to changed signal checkbox->SetValue(registry::getValue(key) == "1"); checkbox->Bind(wxEVT_CHECKBOX, [=, &buffer] (wxCommandEvent& ev) { buffer.set(key, checkbox->GetValue() ? "1" : "0"); ev.Skip(); }); resetSignal.connect([=, &buffer] { if (buffer.keyExists(key)) { checkbox->SetValue(registry::getValue(key) == "1"); } }); } inline void bindWidgetToBufferedKey(wxSlider* slider, const std::string& key, Buffer& buffer, sigc::signal& resetSignal, int factor) { // Set initial value then connect to changed signal slider->SetValue(registry::getValue(key) * factor); slider->Bind(wxEVT_SCROLL_CHANGED, [=, &buffer] (wxScrollEvent& ev) { buffer.set(key, string::to_string(static_cast(slider->GetValue()) / factor)); ev.Skip(); }); slider->Bind(wxEVT_SCROLL_THUMBTRACK, [=, &buffer] (wxScrollEvent& ev) { buffer.set(key, string::to_string(static_cast(slider->GetValue()) / factor)); ev.Skip(); }); resetSignal.connect([=, &buffer] { if (buffer.keyExists(key)) { slider->SetValue(registry::getValue(key) * factor); } }); } inline void bindWidgetToBufferedKey(wxChoice* choice, const std::string& key, Buffer& buffer, sigc::signal& resetSignal, bool storeValueNotIndex) { // Set initial value then connect to changed signal choice->Select(storeValueNotIndex ? choice->FindString(registry::getValue(key)): registry::getValue(key)); choice->Bind(wxEVT_CHOICE, [=, &buffer] (wxCommandEvent& ev) { if (storeValueNotIndex) { buffer.set(key, choice->GetStringSelection().ToStdString()); } else { buffer.set(key, string::to_string(choice->GetSelection())); } ev.Skip(); }); resetSignal.connect([=, &buffer] { if (buffer.keyExists(key)) { choice->Select(storeValueNotIndex ? choice->FindString(registry::getValue(key)): registry::getValue(key)); } }); } inline void bindWidgetToBufferedKey(wxTextCtrl* entry, const std::string& key, Buffer& buffer, sigc::signal& resetSignal) { // Set initial value then connect to changed signal if (GlobalRegistry().keyExists(key)) { entry->SetValue(registry::getValue(key)); } entry->Bind(wxEVT_TEXT, [=, &buffer] (wxCommandEvent& ev) { buffer.set(key, entry->GetValue().ToStdString()); ev.Skip(); }); resetSignal.connect([=, &buffer] { if (buffer.keyExists(key)) { entry->SetValue(registry::getValue(key)); } }); } inline void bindWidgetToBufferedKey(wxSpinCtrl* spinCtrl, const std::string& key, Buffer& buffer, sigc::signal& resetSignal) { // Set initial value then connect to changed signal if (GlobalRegistry().keyExists(key)) { spinCtrl->SetValue(registry::getValue(key)); } spinCtrl->Bind(wxEVT_SPINCTRL, [=, &buffer] (wxSpinEvent& ev) { buffer.set(key, string::to_string(spinCtrl->GetValue())); ev.Skip(); }); resetSignal.connect([=, &buffer] { if (buffer.keyExists(key)) { spinCtrl->SetValue(registry::getValue(key)); } }); } inline void bindWidgetToBufferedKey(wxSpinCtrlDouble* spinCtrl, const std::string& key, Buffer& buffer, sigc::signal& resetSignal) { // Set initial value then connect to changed signal if (GlobalRegistry().keyExists(key)) { spinCtrl->SetValue(registry::getValue(key)); } spinCtrl->Bind(wxEVT_SPINCTRLDOUBLE, [=, &buffer] (wxSpinDoubleEvent& ev) { buffer.set(key, string::to_string(spinCtrl->GetValue())); ev.Skip(); }); resetSignal.connect([=, &buffer] { if (buffer.keyExists(key)) { spinCtrl->SetValue(registry::getValue(key)); } }); } } // namespace DarkRadiant-2.5.0/libs/registry/adaptors.h000066400000000000000000000017531321750546400205060ustar00rootroot00000000000000#pragma once #include "registry.h" #include namespace registry { namespace detail { inline void invokeFromBoolean(const std::string& key, sigc::slot trueCallback, sigc::slot falseCallback) { if (getValue(key)) trueCallback(); else falseCallback(); } } /** * \brief * Adaptor function to connect two slots to a boolean registry key, with one * slot invoked when the value changes to true and the other invoked when the * value changes to false. */ inline void observeBooleanKey(const std::string& key, sigc::slot trueCallback, sigc::slot falseCallback) { GlobalRegistry().signalForKey(key).connect( sigc::bind(sigc::ptr_fun(&detail::invokeFromBoolean), key, trueCallback, falseCallback) ); } } DarkRadiant-2.5.0/libs/registry/buffer.h000066400000000000000000000035441321750546400201420ustar00rootroot00000000000000#pragma once #include #include #include "iregistry.h" #include "string/convert.h" /// Convenience methods and types for interacting with the XML registry namespace registry { /** * A proxy class that is buffering all write operations. * Nothing is written to the back-end XMLRegistry until * a call to commitChanges() is performed. * Only the most common operations are supported by this class, * to get and set certain registry key values. */ class Buffer { private: Registry& _backend; // A key => value map to store buffered values typedef std::map KeyValueBuffer; KeyValueBuffer _buffer; public: // Pass the Registry instance to wrap around, defaults to GlobalRegistry() Buffer(Registry& backend = GlobalRegistry()) : _backend(backend) {} // Get the value of the given registry key - returns cached values if present std::string get(const std::string& key) { KeyValueBuffer::const_iterator found = _buffer.find(key); // Check if the key value is cached if (found != _buffer.end()) { return found->second; // return the cached value } // Not cached, pass the call to the backend return _backend.get(key); } // Set the value of the given registry key void set(const std::string& key, const std::string& value) { _buffer[key] = value; } // Discard all pending write operations void clear() { _buffer.clear(); } bool keyExists(const std::string& key) { KeyValueBuffer::const_iterator found = _buffer.find(key); // Check if the key value is cached if (found != _buffer.end()) { return true; } return _backend.keyExists(key); } // Performs any pending write operations void commitChanges() { std::for_each(_buffer.begin(), _buffer.end(), [&] (KeyValueBuffer::value_type& kv) { _backend.set(kv.first, kv.second); }); _buffer.clear(); } }; } // namespace DarkRadiant-2.5.0/libs/registry/registry.h000066400000000000000000000030121321750546400205270ustar00rootroot00000000000000#pragma once #include "iregistry.h" #include "string/convert.h" #include "util/Noncopyable.h" /// Convenience methods and types for interacting with the XML registry namespace registry { /** * \brief * Set a value in the registry of any type that can be converted to a string * with string::to_string. */ template void setValue(const std::string& key, const T& value) { GlobalRegistry().set(key, string::to_string(value)); } /** * \brief * Get the value of the given registry and convert it to type T. If the key * cannot be found or is not convertible to the required type, a * default-constructed T will be returned. * * T must be default-constructible, copy-constructible and convertible from * an std::string using string::convert. */ template T getValue(const std::string& key, T defaultVal = T()) { if (GlobalRegistry().keyExists(key)) { return string::convert(GlobalRegistry().get(key)); } else { return defaultVal; } } /** * \brief * Scoped sentry object which sets a registry key to a temporary value for its * lifetime, and restores the original value on destruction. */ template class ScopedKeyChanger : util::Noncopyable { std::string _key; T _origVal; public: ScopedKeyChanger(const std::string& key, T tempVal) : _key(key), _origVal(registry::getValue(key)) { registry::setValue(_key, tempVal); } ~ScopedKeyChanger() { registry::setValue(_key, _origVal); } }; } DarkRadiant-2.5.0/libs/render.h000066400000000000000000000240221321750546400162720ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once /// \file /// \brief High-level constructs for efficient OpenGL rendering. #include "render/ArbitraryMeshVertex.h" #include "render/Vertex3f.h" #include "render/TexCoord2f.h" #include "render/VertexCb.h" #include "irender.h" #include "igl.h" #include "math/FloatTools.h" #include "math/Vector2.h" #include "math/pi.h" #include typedef unsigned int RenderIndex; const GLenum RenderIndexTypeID = GL_UNSIGNED_INT; /// Vector of indices for use with glDrawElements typedef std::vector IndexBuffer; /// \brief A wrapper around a std::vector which inserts only vertices which have not already been inserted. /// \param Vertex The vertex data type. Must support operator<, operator== and operator!=. /// For best performance, quantise vertices before inserting them. template class UniqueVertexBuffer { typedef std::vector Vertices; Vertices& m_data; struct bnode { bnode() : m_left(0), m_right(0) {} RenderIndex m_left; RenderIndex m_right; }; std::vector m_btree; RenderIndex m_prev0; RenderIndex m_prev1; RenderIndex m_prev2; const RenderIndex find_or_insert(const Vertex& vertex) { RenderIndex index = 0; while(1) { if(vertex < m_data[index]) { bnode& node = m_btree[index]; if(node.m_left != 0) { index = node.m_left; continue; } else { node.m_left = RenderIndex(m_btree.size()); m_btree.push_back(bnode()); m_data.push_back(vertex); return RenderIndex(m_btree.size()-1); } } if(m_data[index] < vertex) { bnode& node = m_btree[index]; if(node.m_right != 0) { index = node.m_right; continue; } else { node.m_right = RenderIndex(m_btree.size()); m_btree.push_back(bnode()); m_data.push_back(vertex); return RenderIndex(m_btree.size()-1); } } return index; } } public: UniqueVertexBuffer(Vertices& data) : m_data(data), m_prev0(0), m_prev1(0), m_prev2(0) {} typedef typename Vertices::const_iterator iterator; iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } std::size_t size() const { return m_data.size(); } const Vertex* data() const { return &(*m_data.begin()); } Vertex& operator[](std::size_t index) { return m_data[index]; } const Vertex& operator[](std::size_t index) const { return m_data[index]; } void clear() { m_prev0 = 0; m_prev1 = 0; m_prev2 = 0; m_data.clear(); m_btree.clear(); } void reserve(std::size_t max_vertices) { m_data.reserve(max_vertices); m_btree.reserve(max_vertices); } /// \brief Returns the index of the element equal to \p vertex. RenderIndex insert(const Vertex& vertex) { if(m_data.empty()) { m_data.push_back(vertex); m_btree.push_back(bnode()); return 0; } if(m_data[m_prev0] == vertex) return m_prev0; if(m_prev1 != m_prev0 && m_data[m_prev1] == vertex) return m_prev1; if(m_prev2 != m_prev0 && m_prev2 != m_prev1 && m_data[m_prev2] == vertex) return m_prev2; m_prev2 = m_prev1; m_prev1 = m_prev0; m_prev0 = find_or_insert(vertex); return m_prev0; } }; // A Normal3f is just another Vertex3f (Vector3) typedef Vertex3f Normal3f; /// \brief Returns a double-precision \p component quantised to \p precision. inline double double_quantise(double component, double precision) { return float_snapped(component, precision); } /// \brief Returns a \p vertex quantised to \p precision. inline Vertex3f vertex3f_quantised(const Vertex3f& vertex, double precision) { return Vertex3f(double_quantise(vertex.x(), precision), double_quantise(vertex.y(), precision), double_quantise(vertex.z(), precision)); } const float c_quantise_vertex = 1.f / static_cast(1 << 3); /// \brief Returns \p v with vertex quantised to a fixed precision. inline VertexCb pointvertex_quantised(const VertexCb& v) { return VertexCb(vertex3f_quantised(v.vertex, c_quantise_vertex), v.colour); } /// \brief Sets up the OpenGL colour and vertex arrays for \p array. inline void pointvertex_gl_array(const VertexCb* array) { glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexCb), &array->colour); glVertexPointer(3, GL_DOUBLE, sizeof(VertexCb), &array->vertex); } /// A renderable collection of coloured vertices class RenderablePointVector : public OpenGLRenderable { protected: typedef std::vector PointVertexVector; PointVertexVector _vector; const GLenum _mode; public: RenderablePointVector(GLenum mode) : _mode(mode) {} RenderablePointVector(GLenum mode, std::size_t initialSize) : _vector(initialSize), _mode(mode) {} void render(const RenderInfo& info) const { if (_vector.empty()) return; // Enable point colours if required bool enablePointColours = info.checkFlag(RENDER_VERTEX_COLOUR) || (info.checkFlag(RENDER_POINT_COLOUR) && _mode == GL_POINTS); if (enablePointColours) { glEnableClientState(GL_COLOR_ARRAY); } pointvertex_gl_array(&_vector.front()); glDrawArrays(_mode, 0, static_cast(_vector.size())); if (enablePointColours) { glDisableClientState(GL_COLOR_ARRAY); } } // Convenience method to set the colour of the whole array void setColour(const Colour4b& colour) { for (PointVertexVector::iterator i = _vector.begin(); i != _vector.end(); ++i) { i->colour = colour; } } const VertexCb& operator[](std::size_t i) const { return _vector[i]; } VertexCb& operator[](std::size_t i) { return _vector[i]; } VertexCb& front() { return _vector.front(); } const VertexCb& front() const { return _vector.front(); } std::size_t size() const { return _vector.size(); } bool empty() const { return _vector.empty(); } void clear() { _vector.clear(); } void resize(std::size_t size) { _vector.resize(size); } void reserve(std::size_t size) { _vector.reserve(size); } void push_back(const VertexCb& point) { _vector.push_back(point); } }; /// A renderable wrapper for a collection of vertices stored elsewhere class RenderableVertexBuffer : public OpenGLRenderable { const GLenum _mode; const std::vector& m_vertices; public: RenderableVertexBuffer(GLenum mode, const std::vector& vertices) : _mode(mode), m_vertices(vertices) {} void render(const RenderInfo& info) const { bool enableColours = info.checkFlag(RENDER_VERTEX_COLOUR) || (info.checkFlag(RENDER_POINT_COLOUR) && _mode == GL_POINTS); if (enableColours) { glEnableClientState(GL_COLOR_ARRAY); } pointvertex_gl_array(m_vertices.data()); glDrawArrays(_mode, 0, static_cast(m_vertices.size())); if (enableColours) { glDisableClientState(GL_COLOR_ARRAY); } } }; /// Renderable wrapper for a set of vertices and indices stored in other arrays class RenderableIndexBuffer : public OpenGLRenderable { const GLenum _mode; const IndexBuffer& m_indices; const std::vector& m_vertices; public: RenderableIndexBuffer(GLenum mode, const IndexBuffer& indices, const std::vector& vertices) : _mode(mode), m_indices(indices), m_vertices(vertices) {} void render(const RenderInfo& info) const { bool enableColours = info.checkFlag(RENDER_VERTEX_COLOUR) || (info.checkFlag(RENDER_POINT_COLOUR) && _mode == GL_POINTS); if (enableColours) { glEnableClientState(GL_COLOR_ARRAY); } pointvertex_gl_array(m_vertices.data()); glDrawElements(_mode, GLsizei(m_indices.size()), RenderIndexTypeID, m_indices.data()); if (enableColours) { glDisableClientState(GL_COLOR_ARRAY); } } }; class RemapXYZ { public: static void set(Vertex3f& vertex, float x, float y, float z) { vertex.x() = x; vertex.y() = y; vertex.z() = z; } }; class RemapYZX { public: static void set(Vertex3f& vertex, float x, float y, float z) { vertex.x() = z; vertex.y() = x; vertex.z() = y; } }; class RemapZXY { public: static void set(Vertex3f& vertex, float x, float y, float z) { vertex.x() = y; vertex.y() = z; vertex.z() = x; } }; template inline void draw_circle(const std::size_t segments, const float radius, VertexCb* vertices, remap_policy remap) { const double increment = c_pi / double(segments << 2); std::size_t count = 0; float x = radius; float y = 0; while(count < segments) { VertexCb* i = vertices + count; VertexCb* j = vertices + ((segments << 1) - (count + 1)); VertexCb* k = i + (segments << 1); VertexCb* l = j + (segments << 1); VertexCb* m = i + (segments << 2); VertexCb* n = j + (segments << 2); VertexCb* o = k + (segments << 2); VertexCb* p = l + (segments << 2); remap_policy::set(i->vertex, x,-y, 0); remap_policy::set(k->vertex,-y,-x, 0); remap_policy::set(m->vertex,-x, y, 0); remap_policy::set(o->vertex, y, x, 0); ++count; { const double theta = increment * count; x = static_cast(radius * cos(theta)); y = static_cast(radius * sin(theta)); } remap_policy::set(j->vertex, y,-x, 0); remap_policy::set(l->vertex,-x,-y, 0); remap_policy::set(n->vertex,-y, x, 0); remap_policy::set(p->vertex, x, y, 0); } } inline void draw_quad(const float radius, VertexCb* quad) { (*quad++).vertex = Vertex3f(-radius, radius, 0); (*quad++).vertex = Vertex3f(radius, radius, 0); (*quad++).vertex = Vertex3f(radius, -radius, 0); (*quad++).vertex = Vertex3f(-radius, -radius, 0); } DarkRadiant-2.5.0/libs/render/000077500000000000000000000000001321750546400161215ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/render/ArbitraryMeshVertex.h000066400000000000000000000116501321750546400222470ustar00rootroot00000000000000#pragma once #include #include "Vertex3f.h" #include "TexCoord2f.h" #include "VertexTraits.h" /** * Data structure representing a mesh vertex. */ class ArbitraryMeshVertex { public: TexCoord2f texcoord; Normal3f normal; Vertex3f vertex; Normal3f tangent; Normal3f bitangent; // Vertex colour Vector3 colour; /// Default constructor. ArbitraryMeshVertex() : tangent(0, 0, 0), bitangent(0, 0, 0), colour(1.0, 1.0, 1.0) {} /// Initialising constructor. ArbitraryMeshVertex(const Vertex3f& v, const Normal3f& n, const TexCoord2f& t) : texcoord(t), normal(n), vertex(v), tangent(0, 0, 0), bitangent(0, 0, 0), colour(1.0, 1.0, 1.0) {} /// Cast to simple Vertex3f, throwing away other components operator Vertex3f() const { return vertex; } }; /// Less-than comparison for ArbitraryMeshVertex inline bool operator<(const ArbitraryMeshVertex& first, const ArbitraryMeshVertex& other) { if (first.texcoord != other.texcoord) { return first.texcoord < other.texcoord; } if (first.normal != other.normal) { return first.normal < other.normal; } if (first.vertex != other.vertex) { return first.vertex < other.vertex; } return false; } /// Equality comparison for ArbitraryMeshVertex inline bool operator==(const ArbitraryMeshVertex& first, const ArbitraryMeshVertex& other) { return first.texcoord == other.texcoord && first.normal == other.normal && first.vertex == other.vertex; } /// Inequality comparison for ArbitraryMeshVertex inline bool operator!=(const ArbitraryMeshVertex& first, const ArbitraryMeshVertex& other) { return !(first == other); } namespace render { /// VertexTraits specialisation for ArbitraryMeshVertex template<> class VertexTraits { public: static const void* VERTEX_OFFSET() { return reinterpret_cast( offsetof(ArbitraryMeshVertex, vertex) ); } static bool hasNormal() { return true; } static const void* NORMAL_OFFSET() { return reinterpret_cast( offsetof(ArbitraryMeshVertex, normal) ); } static bool hasTexCoord() { return true; } static const void* TEXCOORD_OFFSET() { return reinterpret_cast( offsetof(ArbitraryMeshVertex, texcoord) ); } static bool hasTangents() { return true; } static const void* TANGENT_OFFSET() { return reinterpret_cast( offsetof(ArbitraryMeshVertex, tangent) ); } static const void* BITANGENT_OFFSET() { return reinterpret_cast( offsetof(ArbitraryMeshVertex, bitangent) ); } }; } /** * String output for ArbitraryMeshVertex. */ inline std::ostream& operator<< (std::ostream& os, const ArbitraryMeshVertex& v) { os << "ArbitraryMeshVertex { " << " vertex = " << v.vertex << ", normal = " << v.normal << ", texcoord = " << v.texcoord << " }"; return os; } /// \brief Calculates the tangent vectors for a triangle \p a, \p b, \p c and stores the tangent in \p s and the bitangent in \p t. inline void ArbitraryMeshTriangle_calcTangents(const ArbitraryMeshVertex& a, const ArbitraryMeshVertex& b, const ArbitraryMeshVertex& c, Vector3& s, Vector3& t) { s = Vector3(0, 0, 0); t = Vector3(0, 0, 0); Vector3 aVec, bVec, cVec; { aVec.set(a.vertex.x(), a.texcoord.s(), a.texcoord.t()); bVec.set(b.vertex.x(), b.texcoord.s(), b.texcoord.t()); cVec.set(c.vertex.x(), c.texcoord.s(), c.texcoord.t()); Vector3 cross( (bVec-aVec).crossProduct(cVec-aVec) ); if(fabs(cross.x()) > 0.000001f) { s.x() = -cross.y() / cross.x(); } if(fabs(cross.x()) > 0.000001f) { t.x() = -cross.z() / cross.x(); } } { aVec.set(a.vertex.y(), a.texcoord.s(), a.texcoord.t()); bVec.set(b.vertex.y(), b.texcoord.s(), b.texcoord.t()); cVec.set(c.vertex.y(), c.texcoord.s(), c.texcoord.t()); Vector3 cross( (bVec-aVec).crossProduct(cVec-aVec)); if(fabs(cross.x()) > 0.000001f) { s.y() = -cross.y() / cross.x(); } if(fabs(cross.x()) > 0.000001f) { t.y() = -cross.z() / cross.x(); } } { aVec.set(a.vertex.z(), a.texcoord.s(), a.texcoord.t()); bVec.set(b.vertex.z(), b.texcoord.s(), b.texcoord.t()); cVec.set(c.vertex.z(), c.texcoord.s(), c.texcoord.t()); Vector3 cross( (bVec-aVec).crossProduct(cVec-aVec)); if(fabs(cross.x()) > 0.000001f) { s.z() = -cross.y() / cross.x(); } if(fabs(cross.x()) > 0.000001f) { t.z() = -cross.z() / cross.x(); } } } inline void ArbitraryMeshTriangle_sumTangents(ArbitraryMeshVertex& a, ArbitraryMeshVertex& b, ArbitraryMeshVertex& c) { Vector3 s, t; ArbitraryMeshTriangle_calcTangents(a, b, c, s, t); a.tangent += s; b.tangent += s; c.tangent += s; a.bitangent += t; b.bitangent += t; c.bitangent += t; } DarkRadiant-2.5.0/libs/render/Colour4.h000066400000000000000000000024711321750546400176250ustar00rootroot00000000000000#pragma once #include "math/Vector4.h" #include /** * \brief * A Vector4 storing RGBA colour * * This class provides certain convenience methods for treating a 4-element * vector as an RGBA colour, for example, ensuring that the colour values lie * within the [0.0, 1.0] range. */ class Colour4: public Vector4 { bool channelValid(double c) const { return c >= 0.0 && c <= 1.0; } public: /// Default-construct an invalid colour Colour4() : Vector4(-1, -1, -1, -1) { assert(!isValid()); } /// Initialise a colour with individual components Colour4(float r, float g, float b, float a) : Vector4(r, g, b, a) { } /// Construct a Colour4 from a Vector3 and optional alpha Colour4(const Vector3& vec, float alpha = 1.0f) : Vector4(vec, alpha) { } /// Return true if this colour contains valid component values bool isValid() const { return channelValid(x()) && channelValid(y()) && channelValid(z()) && channelValid(w()); } public: /// Useful colour constants static const Colour4& BLACK() { static Colour4 black(0.0f, 0.0f, 0.0f, 1.0f); return black; } static const Colour4& WHITE() { static Colour4 white(1.0f, 1.0f, 1.0f, 1.0f); return white; } }; DarkRadiant-2.5.0/libs/render/Colour4b.h000066400000000000000000000013231321750546400177620ustar00rootroot00000000000000#pragma once /// 4-element colour in single-byte precision (0 - 255) struct Colour4b { unsigned char r, g, b, a; Colour4b() {} Colour4b(unsigned char _r, unsigned char _g, unsigned char _b, unsigned char _a) : r(_r), g(_g), b(_b), a(_a) {} bool operator<(const Colour4b& other) const { if (r != other.r) { return r < other.r; } if (g != other.g) { return g < other.g; } if (b != other.b) { return b < other.b; } if (a != other.a) { return a < other.a; } return false; } bool operator==(const Colour4b& other) const { return r == other.r && g == other.g && b == other.b && a == other.a; } bool operator!=(const Colour4b& other) const { return !operator==(other); } }; DarkRadiant-2.5.0/libs/render/IndexedVertexBuffer.h000066400000000000000000000126671321750546400222160ustar00rootroot00000000000000#pragma once #include #include "GLProgramAttributes.h" namespace render { /** * \brief * Receiver of indexed vertex geometry for rendering * * An IndexedVertexBuffer stores a pool of vertex data, and receives batches of * indices into the vertex buffer which are rendered with glDrawElements. It * functions much like \a VertexBuffer except for indexed geometry rather than * raw vertex geometry. */ template class IndexedVertexBuffer { public: typedef std::vector Vertices; typedef std::vector Indices; private: typedef VertexTraits Traits; // OpenGL VBO mutable GLuint _vertexVBO; mutable GLuint _indexVBO; // Vertex and index storage Vertices _vertices; Indices _indices; // Batches of indices (start index and count) struct Batch { std::size_t start; std::size_t size; }; // Batches std::vector _batches; private: void initialiseVBOs() const { _vertexVBO = makeVBOFromArray(GL_ARRAY_BUFFER, _vertices); _indexVBO = makeVBOFromArray(GL_ELEMENT_ARRAY_BUFFER, _indices); } public: /// Construct an empty IndexedVertexBuffer IndexedVertexBuffer() : _vertexVBO(0), _indexVBO(0) { } /// Destroy resources ~IndexedVertexBuffer() { deleteVBO(_vertexVBO); deleteVBO(_indexVBO); } /** * \brief * Add vertices to the IndexedVertexBuffer * * Unlike with VertexBuffer, adding vertices to an IndexedVertexBuffer does * not create a batch. It just adds vertices to the common vertex pool. */ template void addVertices(Iter_T begin, Iter_T end) { std::copy(begin, end, std::back_inserter(_vertices)); } /// Add a batch of indices template void addIndexBatch(Iter_T begin, std::size_t count) { if (count < 1) { throw std::logic_error("Batch must contain at least one index"); } // Create the new batch Batch newBatch = { _indices.size(), count }; _batches.push_back(newBatch); // Copy the indices _indices.reserve(_indices.size() + count); for (Iter_T i = begin; i < begin + count; ++i) { _indices.push_back(*i); } } /** * \brief * Replace all data with that from another IndexedVertexBuffer * * \see VertexBuffer::replaceData */ void replaceData(const IndexedVertexBuffer& other) { replaceVBODataIfPossible(GL_ARRAY_BUFFER, _vertexVBO, _vertices, other._vertices); replaceVBODataIfPossible(GL_ELEMENT_ARRAY_BUFFER, _indexVBO, _indices, other._indices); _vertices = other._vertices; _indices = other._indices; _batches = other._batches; } /** * \brief * Render all batches with given primitive type * * \param renderBump * True if tangent and bitangent vectors should be submitted for bump map * rendering. If false, only regular normal vectors will be submitted. In * all cases the pointers will only be set if carried by the particular * vertex type. */ void renderAllBatches(GLenum primitiveType, bool renderBump = false) const { if (_vertexVBO == 0 || _indexVBO == 0) { initialiseVBOs(); } glBindBuffer(GL_ARRAY_BUFFER, _vertexVBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexVBO); // Vertex pointer includes whole vertex buffer const GLsizei STRIDE = sizeof(Vertex_T); glVertexPointer(3, GL_DOUBLE, STRIDE, Traits::VERTEX_OFFSET()); // Set other pointers as necessary if (Traits::hasTexCoord()) { if (renderBump) { glVertexAttribPointer( ATTR_TEXCOORD, 2, GL_DOUBLE, GL_FALSE, STRIDE, Traits::TEXCOORD_OFFSET() ); } else { glTexCoordPointer(2, GL_DOUBLE, STRIDE, Traits::TEXCOORD_OFFSET()); } } if (Traits::hasNormal()) { if (renderBump) { glVertexAttribPointer( ATTR_NORMAL, 3, GL_DOUBLE, GL_FALSE, STRIDE, Traits::NORMAL_OFFSET() ); } else { glNormalPointer(GL_DOUBLE, STRIDE, Traits::NORMAL_OFFSET()); } } if (Traits::hasTangents() && renderBump) { glVertexAttribPointer(ATTR_TANGENT, 3, GL_DOUBLE, GL_FALSE, STRIDE, Traits::TANGENT_OFFSET()); glVertexAttribPointer(ATTR_BITANGENT, 3, GL_DOUBLE, GL_FALSE, STRIDE, Traits::BITANGENT_OFFSET()); } // Render each batch of indices for (typename std::vector::const_iterator i = _batches.begin(); i != _batches.end(); ++i) { glDrawElements( primitiveType, GLint(i->size), RenderIndexTypeID, reinterpret_cast( i->start * sizeof(Indices::value_type) ) ); } glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } }; } DarkRadiant-2.5.0/libs/render/NopVolumeTest.h000066400000000000000000000030041321750546400210530ustar00rootroot00000000000000#pragma once #include "ivolumetest.h" #include "math/Matrix4.h" namespace render { /** * greebo: This is a minimal VolumeTest implementation which * returns true in all test cases. All matrices are identity * unless they're set to something different. */ class NopVolumeTest : public VolumeTest { private: Matrix4 _viewPort; Matrix4 _projection; Matrix4 _modelView; public: NopVolumeTest() : _viewPort(Matrix4::getIdentity()), _projection(Matrix4::getIdentity()), _modelView(Matrix4::getIdentity()) {} bool TestPoint(const Vector3& point) const { return true; } bool TestLine(const Segment& segment) const { return true; } bool TestPlane(const Plane3& plane) const { return true; } bool TestPlane(const Plane3& plane, const Matrix4& localToWorld) const { return true; } VolumeIntersectionValue TestAABB(const AABB& aabb) const { return VOLUME_INSIDE; } VolumeIntersectionValue TestAABB(const AABB& aabb, const Matrix4& localToWorld) const { return VOLUME_INSIDE; } virtual bool fill() const { return true; } virtual const Matrix4& GetViewport() const { return _viewPort; } virtual const Matrix4& GetProjection() const { return _projection; } virtual const Matrix4& GetModelview() const { return _modelView; } void setViewPort(const Matrix4& matrix) { _viewPort = matrix; } void setProjection(const Matrix4& matrix) { _projection = matrix; } void setModelView(const Matrix4& matrix) { _modelView = matrix; } }; } // namespace render DarkRadiant-2.5.0/libs/render/RenderablePivot.h000066400000000000000000000053521321750546400213640ustar00rootroot00000000000000#pragma once #include "irender.h" #include "irenderable.h" #include "igl.h" #include #include "math/Vector3.h" #include "math/Matrix4.h" #include "Colour4b.h" #include "VertexCb.h" namespace render { class RenderablePivot : public OpenGLRenderable { private: const Colour4b _colourX; const Colour4b _colourY; const Colour4b _colourZ; std::vector _vertices; const Vector3& _pivot; ShaderPtr _shader; public: mutable Matrix4 m_localToWorld; const ShaderPtr& getShader() const { return _shader; } RenderablePivot(const Vector3& pivot) : _colourX(255, 0, 0, 255), _colourY(0, 255, 0, 255), _colourZ(0, 0, 255, 255), _pivot(pivot) { _vertices.reserve(6); _vertices.push_back(VertexCb(_pivot, _colourX)); _vertices.push_back(VertexCb(_pivot + Vector3(16, 0, 0), _colourX)); _vertices.push_back(VertexCb(_pivot, _colourY)); _vertices.push_back(VertexCb(_pivot + Vector3(0, 16, 0), _colourY)); _vertices.push_back(VertexCb(_pivot, _colourZ)); _vertices.push_back(VertexCb(_pivot + Vector3(0, 0, 16), _colourZ)); } /** greebo: Updates the renderable vertex array to the given pivot point */ void updatePivot() { _vertices.clear(); _vertices.push_back(VertexCb(_pivot, _colourX)); _vertices.push_back(VertexCb(_pivot + Vector3(16, 0, 0), _colourX)); _vertices.push_back(VertexCb(_pivot, _colourY)); _vertices.push_back(VertexCb(_pivot + Vector3(0, 16, 0), _colourY)); _vertices.push_back(VertexCb(_pivot, _colourZ)); _vertices.push_back(VertexCb(_pivot + Vector3(0, 0, 16), _colourZ)); } void setRenderSystem(const RenderSystemPtr& renderSystem) { if (renderSystem) { _shader = renderSystem->capture("$PIVOT"); } else { _shader.reset(); } } void render(const RenderInfo& info) const { if (_vertices.empty()) return; glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(3, GL_DOUBLE, sizeof(VertexCb), &_vertices.data()->vertex); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexCb), &_vertices.data()->colour); glDrawArrays(GL_LINES, 0, static_cast(_vertices.size())); glDisableClientState(GL_COLOR_ARRAY); } void render(RenderableCollector& collector, const VolumeTest& volume, const Matrix4& localToWorld) const { collector.PushState(); // greebo: Commented this out to avoid the point from being moved along with the view. //Pivot2World_worldSpace(m_localToWorld, localToWorld, volume.GetModelview(), volume.GetProjection(), volume.GetViewport()); collector.setHighlightFlag(RenderableCollector::Highlight::Primitives, false); collector.SetState(_shader, RenderableCollector::eWireframeOnly); collector.SetState(_shader, RenderableCollector::eFullMaterials); collector.addRenderable(*this, localToWorld); collector.PopState(); } }; } DarkRadiant-2.5.0/libs/render/RenderableSpacePartition.h000066400000000000000000000123071321750546400232060ustar00rootroot00000000000000#ifndef _RENDERABLE_SPACE_PARTITION_H_ #define _RENDERABLE_SPACE_PARTITION_H_ #include "ispacepartition.h" #include "irender.h" #include "irenderable.h" #include "math/Matrix4.h" #include "math/AABB.h" namespace render { /** * greebo: This is a renderable helper object which can be used * to render any space partition system implementing the ISPacepartitionSystem interface. * * Instantiate such a class and pass the Shader and the SpacePartition system to the * setShader() and setSpacePartition() methods to enable rendering. * * This object can be directly attached to the GlobalRenderSystem(). */ class RenderableSpacePartition : public Renderable, public OpenGLRenderable { private: // The shader we're using ShaderPtr _shader; // The space partition to render scene::ISpacePartitionSystemPtr _spacePartition; public: void setSpacePartition(const scene::ISpacePartitionSystemPtr& spacePartition) { _spacePartition = spacePartition; } void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override { if (_shader != NULL) { collector.SetState(_shader, RenderableCollector::eFullMaterials); collector.addRenderable(*this, Matrix4::getIdentity()); } } void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override { if (_shader != NULL) { collector.SetState(_shader, RenderableCollector::eWireframeOnly); collector.addRenderable(*this, Matrix4::getIdentity()); } } void setRenderSystem(const RenderSystemPtr& renderSystem) override { if (renderSystem) { _shader = renderSystem->capture("[1 0 0]"); } else { _shader.reset(); } } std::size_t getHighlightFlags() override { return Highlight::NoHighlight; // never highlighted } void renderNode(const scene::ISPNodePtr& node) const { const scene::ISPNode::MemberList& members = node->getMembers(); float numItems = members.size() > 2 ? 1 : (members.size() > 0 ? 0.6f : 0); glColor3f(numItems, numItems, numItems); AABB rb(node->getBounds()); // Extend the renderbounds *slightly* so that the lines don't overlap rb.extents *= 1.02f; // Wireframe cuboid glBegin(GL_LINES); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + -rb.extents.z()); glVertex3d(rb.origin.x() + -rb.extents.x(), rb.origin.y() + -rb.extents.y(), rb.origin.z() + -rb.extents.z()); glEnd(); const scene::ISPNode::NodeList& children = node->getChildNodes(); for (scene::ISPNode::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) { renderNode(*i); } } void render(const RenderInfo& info) const override { if (_spacePartition != NULL) { renderNode(_spacePartition->getRoot()); } } }; } // namespace render #endif /* _RENDERABLE_SPACE_PARTITION_H_ */ DarkRadiant-2.5.0/libs/render/SceneRenderWalker.h000066400000000000000000000020641321750546400216370ustar00rootroot00000000000000#pragma once #include "irender.h" #include "iscenegraph.h" namespace render { /// A scenegraph walker that passes nodes to its contained RenderableCollector class SceneRenderWalker : public scene::Graph::Walker { // The collector which is sorting our renderables RenderableCollector& _collector; // The view we're using for culling const VolumeTest& _volume; private: void render(const Renderable& renderable) const { if (_collector.supportsFullMaterials()) renderable.renderSolid(_collector, _volume); else renderable.renderWireframe(_collector, _volume); } public: /// Initialise with a RenderableCollector to populate and a view volume SceneRenderWalker(RenderableCollector& collector, const VolumeTest& volume) : _collector(collector), _volume(volume) {} // scene::Graph::Walker implementation, tells each node to submit its OpenGLRenderables bool visit(const scene::INodePtr& node) { _collector.PushState(); node->viewChanged(); render(*node); _collector.PopState(); return true; } }; } // namespace render DarkRadiant-2.5.0/libs/render/ShaderStateRenderer.h000066400000000000000000000035451321750546400221770ustar00rootroot00000000000000#pragma once #include "irender.h" #include namespace render { /** * greebo: This is a basic front-end renderer (collecting renderables) * using a state stack to sort the renderables. Each renderable * is assigned to the topmost shader on the stack. No highlighting support. * It is returning FullMaterials as renderer style. */ class ShaderStateRenderer : public RenderableCollector { private: struct State { ShaderPtr shader; const LightList* lights; State() : lights(nullptr) {} }; // The state stack, empty at start typedef std::list StateStack; StateStack _stateStack; public: ShaderStateRenderer() { // Start with an empty shader, which can be assigned in SetState _stateStack.push_back(State()); } void PushState() { if (!_stateStack.empty()) { _stateStack.push_back(_stateStack.back()); } } void PopState() { if (!_stateStack.empty()) { _stateStack.pop_back(); } } void SetState(const ShaderPtr& state, EStyle mode) { assert(!_stateStack.empty()); _stateStack.back().shader = state; } void addRenderable(const OpenGLRenderable& renderable, const Matrix4& world) { assert(!_stateStack.empty()); _stateStack.back().shader->addRenderable(renderable, world, _stateStack.back().lights); } void addRenderable(const OpenGLRenderable& renderable, const Matrix4& world, const IRenderEntity& entity) { assert(!_stateStack.empty()); _stateStack.back().shader->addRenderable(renderable, world, entity, _stateStack.back().lights); } bool supportsFullMaterials() const { return true; } // No support for selection highlighting void setHighlightFlag(Highlight::Flags flags, bool enabled) {} void setLights(const LightList& lights) { _stateStack.back().lights = &lights; } }; } // namespace DarkRadiant-2.5.0/libs/render/TexCoord2f.h000066400000000000000000000014511321750546400202520ustar00rootroot00000000000000#pragma once #include "math/Vector2.h" /// \brief A 2-component texture-coordinate set. class TexCoord2f : public Vector2 { public: // Default constructor TexCoord2f() : Vector2(0,0) {} // constructor TexCoord2f(double s, double t) : Vector2(s, t) {} // Copy constructor TexCoord2f(const TexCoord2f& other) : Vector2(other.s(), other.t()) {} // Copy constructor from Vector2 TexCoord2f(const Vector2& other) : Vector2(other.x(), other.y()) {} double& s() { return x(); } const double& s() const { return x(); } double& t() { return y(); } const double& t() const { return y(); } bool operator< (const TexCoord2f& other) const { if (s() != other.s()) { return s() < other.s(); } if (t() != other.t()) { return t() < other.t(); } return false; } }; DarkRadiant-2.5.0/libs/render/VBO.h000066400000000000000000000041331321750546400167210ustar00rootroot00000000000000/** * \file * Utility functions for manipulating VBOs. */ #pragma once #include namespace render { namespace detail { template GLsizei byteSize(const Array_T& array) { return GLsizei(array.size() * sizeof(typename Array_T::value_type)); } } /** * \brief * Generate a VBO from the given data * * \param target * Binding point for the VBO (e.g. GL_ARRAY_BUFFER). * * \param data * Array of data to copy into the VBO. */ template GLuint makeVBOFromArray(GLenum target, const Array_T& data) { // Create and bind GLuint vboID = 0; glGenBuffers(1, &vboID); glBindBuffer(target, vboID); // Copy data glBufferData(target, detail::byteSize(data), &data.front(), GL_STATIC_DRAW); // Return the VBO identifier return vboID; } /// Replace VBO data store with given array template void replaceVBOData(GLenum target, GLuint vboID, const Array_T& data) { glBindBuffer(target, vboID); glBufferSubData(target, 0, detail::byteSize(data), &data.front()); } /// Delete a VBO and set its identifier to 0 inline void deleteVBO(GLuint& id) { if (id == 0) return; glDeleteBuffers(1, &id); id = 0; } /// Replace VBO data if new data is smaller than or equal to existing data /** * \param target * VBO target (e.g. GL_ARRAY_BUFFER). * * \param vboID * Identifier variable for the VBO. If this is 0 (no VBO allocated), this * function does nothing. If the new data size is larger than the existing * data, this VBO is deleted and its identifier reset to 0. */ template void replaceVBODataIfPossible(GLenum target, GLuint& vboID, const Array_T& existingData, const Array_T& newData) { if (vboID != 0) { if (newData.size() <= existingData.size()) { // Replace VBO data replaceVBOData(GL_ARRAY_BUFFER, vboID, newData); } else { // Size mismatch, cannot replace data so invalidate VBO deleteVBO(vboID); } } } } DarkRadiant-2.5.0/libs/render/VectorLightList.h000066400000000000000000000017041321750546400213620ustar00rootroot00000000000000#pragma once #include "irender.h" namespace render { namespace lib { /** * Implementation of the LightList interface using a std::vector of pointers. */ class VectorLightList : public LightList { private: // Vector of lights typedef std::vector Lights; Lights _lights; friend std::ostream& operator<< (std::ostream&, const VectorLightList&); public: void addLight(const RendererLight& light) { _lights.push_back(&light); } void clear() { _lights.clear(); } void calculateIntersectingLights() const { } void setDirty() { } void forEachLight(const RendererLightCallback& callback) const { for (const RendererLight* light : _lights) { callback(*light); } } }; inline std::ostream& operator<< (std::ostream& os, const VectorLightList& ll) { return os << "VectorLightList { size = " << ll._lights.size() << " }"; } } } DarkRadiant-2.5.0/libs/render/Vertex3f.h000066400000000000000000000026321321750546400200030ustar00rootroot00000000000000#pragma once #include "VertexTraits.h" #include "math/Vector3.h" /// \brief A 3-component vertex. class Vertex3f : public Vector3 { public: /** Default constructor. */ Vertex3f() {} // Construct from a Vector3 Vertex3f(const Vector3& other) : Vector3(other) {} /** Construct a Vertex3f from 3 individual values */ Vertex3f(double _x, double _y, double _z) : Vector3(_x, _y, _z) {} /** Construct a Vertex3f from a 3-element array */ Vertex3f(const double* array) : Vector3(array) {} // static Named constructor static Vertex3f Identity() { return Vertex3f(0,0,0); } bool operator<(const Vertex3f& other) const { if (x() != other.x()) { return x() < other.x(); } if (y() != other.y()) { return y() < other.y(); } if (z() != other.z()) { return z() < other.z(); } return false; } }; /* Normal3f typedef */ typedef Vertex3f Normal3f; namespace render { /// VertexTraits for Vertex3f template<> class VertexTraits { public: static const void* VERTEX_OFFSET() { return 0; } static bool hasNormal() { return false; } static const void* NORMAL_OFFSET() { return 0; } static bool hasTexCoord() { return false; } static const void* TEXCOORD_OFFSET() { return 0; } static bool hasTangents() { return false; } static const void* TANGENT_OFFSET() { return 0; } static const void* BITANGENT_OFFSET() { return 0; } }; } DarkRadiant-2.5.0/libs/render/VertexBuffer.h000066400000000000000000000074161321750546400207110ustar00rootroot00000000000000#pragma once #include #include "VBO.h" #include "VertexTraits.h" namespace render { /** * \brief * Receiver of vertex geometry for rendering * * A VertexBuffer provides methods to accept batches of vertices for rendering. * Vertices can be submitted in several batches, and each batch can be rendered * separately (and more than once) without recreating the vertex buffer. The * VertexBuffer may make use of an OpenGL VBO for improved performance. */ template class VertexBuffer { public: typedef std::vector Vertices; private: typedef VertexTraits Traits; // OpenGL VBO information mutable GLuint _vboID; // Initial non-VBO based vertex storage Vertices _vertices; // Batch start index and size struct Batch { std::size_t start; std::size_t size; }; // All batches std::vector _batches; private: // Create the VBO and copy all vertex data into it void initialiseVBO() const { _vboID = makeVBOFromArray(GL_ARRAY_BUFFER, _vertices); if (_vboID == 0) { std::runtime_error("Could not create vertex buffer"); } } public: /// Default construct with no initial resource allocation VertexBuffer() : _vboID(0) { } /// Destroy all resources ~VertexBuffer() { deleteVBO(_vboID); } /** * \brief * Add a batch of vertices * * \param begin * Iterator pointing to the first Vertex_T in the batch. * * \param count * Number of vertices in the batch. * * \param stride * How much to increment the input iterator between each vertex (defaults * to 1). */ template void addBatch(Iter_T begin, std::size_t count, std::size_t stride = 1) { if (count < 1) { throw std::logic_error("Batch must contain at least one vertex"); } // Store batch information Batch newBatch = { _vertices.size(), count }; _batches.push_back(newBatch); // Append all vertices _vertices.reserve(_vertices.size() + count); Iter_T i = begin; while (count > 0) { _vertices.push_back(*i); if (--count > 0) { i += stride; } else { break; } } } /** * \brief * Replace data with that from another VertexBuffer * * If the other VertexBuffer is the same size or smaller than this one and * has not yet had its own VBO allocated, this may improve performance by * avoiding unnecessary re-allocations of GPU memory. * * This method may call GL functions so requires a valid GL context. */ void replaceData(const VertexBuffer& other) { replaceVBODataIfPossible(GL_ARRAY_BUFFER, _vboID, _vertices, other._vertices); _vertices = other._vertices; _batches = other._batches; } /// Render all batches with the given primitive type void renderAllBatches(GLenum primitiveType) const { if (_vboID == 0) { initialiseVBO(); } glBindBuffer(GL_ARRAY_BUFFER, _vboID); // Vertex pointer is always at the start of the whole buffer (the start // and count parameters to glDrawArrays separate batches). glVertexPointer(3, GL_DOUBLE, sizeof(Vertex_T), Traits::VERTEX_OFFSET()); // For each batch for (typename std::vector::const_iterator i = _batches.begin(); i != _batches.end(); ++i) { glDrawArrays(primitiveType, static_cast(i->start), static_cast(i->size)); } glBindBuffer(GL_ARRAY_BUFFER, 0); } }; } DarkRadiant-2.5.0/libs/render/VertexCb.h000066400000000000000000000021271321750546400200160ustar00rootroot00000000000000#pragma once #include "Vertex3f.h" #include "Colour4b.h" /// Vertex with a 4-byte colour value (0 - 255) class VertexCb { public: Colour4b colour; Vertex3f vertex; /// Default constructor, leaves all values uninitialised VertexCb() { } /// Initialise with position and colour VertexCb(const Vertex3f& _vertex, const Colour4b& _colour) : colour(_colour), vertex(_vertex) {} /// Initialise with position and default white colour VertexCb(const Vector3& vector) : colour(255, 255, 255, 255), vertex(vector) {} // greebo: Same as above, but with a Vector3 as argument VertexCb(const Vector3& point, const Colour4b& _colour) : colour(_colour), vertex(point) {} bool operator< (const VertexCb& other) const { if (vertex != other.vertex) { return vertex < other.vertex; } if (colour != other.colour) { return colour < other.colour; } return false; } bool operator== (const VertexCb& other) const { return colour == other.colour && vertex == other.vertex; } bool operator!= (const VertexCb& other) const { return !(*this == other); } }; DarkRadiant-2.5.0/libs/render/VertexNCb.h000066400000000000000000000003071321750546400201320ustar00rootroot00000000000000#pragma once #include "Vertex3f.h" #include "Colour4b.h" /// 3-element vertex with a 4-byte colour and a normal vector struct VertexNCb { Vertex3f vertex; Colour4b colour; Normal3f normal; }; DarkRadiant-2.5.0/libs/render/VertexNT.h000066400000000000000000000007261321750546400200160ustar00rootroot00000000000000#pragma once #include "math/Vector3.h" /// 3D vertex with normal and UV coordinates struct VertexNT { Vector3 vertex; // 3D position Vector2 texcoord; // UV coordinates Vector3 normal; // Normal vector // Needed for boost::python::vectorindexing_suite bool operator==(const VertexNT& other) const { return (vertex == other.vertex && texcoord == other.texcoord && normal == other.normal); } }; DarkRadiant-2.5.0/libs/render/VertexTraits.h000066400000000000000000000007271321750546400207440ustar00rootroot00000000000000#pragma once namespace render { /** * \brief * Traits class for vertex data * * Specialisations of this class provide the VBO-related code with a mechanism * to determine what vertex properties a particular class provides (e.g. * normal, colour, texcoord etc), and to obtain the byte offsets of each * property within the class. */ template class VertexTraits { public: /// Typedef of the underlying vertex type typedef V VertexType; }; } DarkRadiant-2.5.0/libs/scene/000077500000000000000000000000001321750546400157375ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/scene/BasicRootNode.h000066400000000000000000000027361321750546400206130ustar00rootroot00000000000000#pragma once #include "imap.h" #include "mapfile.h" #include "ientity.h" #include "Node.h" #include "inamespace.h" #include "UndoFileChangeTracker.h" namespace scene { // A very simple implementation of a Map Root Node // for use in the preview widget's scenes. class BasicRootNode : public IMapRootNode, public Node { private: INamespacePtr _namespace; UndoFileChangeTracker _changeTracker; ITargetManagerPtr _targetManager; AABB _emptyAABB; public: BasicRootNode() { _namespace = GlobalNamespaceFactory().createNamespace(); _targetManager = GlobalEntityCreator().createTargetManager(); } virtual ~BasicRootNode() {} const INamespacePtr& getNamespace() override { return _namespace; } IMapFileChangeTracker& getUndoChangeTracker() override { return _changeTracker; } ITargetManager& getTargetManager() override { return *_targetManager; } const AABB& localAABB() const override { return _emptyAABB; } Type getNodeType() const override { return Type::MapRoot; } // Renderable implementation (empty) void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override {} void renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const override {} std::size_t getHighlightFlags() override { return Highlight::NoHighlight; // never highlighted } }; } DarkRadiant-2.5.0/libs/scene/InstanceWalkers.cpp000066400000000000000000000022451321750546400215430ustar00rootroot00000000000000#include "InstanceWalkers.h" #include "iscenegraph.h" namespace scene { InstanceSubgraphWalker::InstanceSubgraphWalker(GraphPtr& sceneGraph) : _sceneGraph(sceneGraph) {} bool InstanceSubgraphWalker::pre(const scene::INodePtr& node) { // greebo: Register this new node with the scenegraph if (!node->inScene()) { _sceneGraph->insert(node); node->setSceneGraph(_sceneGraph); } _nodeStack.push(node); return true; } void InstanceSubgraphWalker::post(const INodePtr& node) { _nodeStack.pop(); if (!_nodeStack.empty()) { if (node->getParent() != _nodeStack.top()) { // Parent-child mismatch, adjust node->setParent(_nodeStack.top()); } } } // ============================================================================================== UninstanceSubgraphWalker::UninstanceSubgraphWalker(Graph& sceneGraph) : _sceneGraph(sceneGraph) {} bool UninstanceSubgraphWalker::pre(const scene::INodePtr& node) { return true; } void UninstanceSubgraphWalker::post(const scene::INodePtr& node) { // Notify the Scenegraph about the upcoming deletion if (node->inScene()) { _sceneGraph.erase(node); node->setSceneGraph(GraphPtr()); } } } // namespace scene DarkRadiant-2.5.0/libs/scene/InstanceWalkers.h000066400000000000000000000021231321750546400212030ustar00rootroot00000000000000#pragma once #include "inode.h" #include namespace scene { class Graph; typedef std::shared_ptr GraphPtr; /** * greebo: This Walker instantiates the visited nodes. * The whole subgraph is traversed and GlobalSceneGraph().insert() is * called on each node. */ class InstanceSubgraphWalker : public scene::NodeVisitor { private: std::stack _nodeStack; GraphPtr& _sceneGraph; public: InstanceSubgraphWalker(GraphPtr& sceneGraph); bool pre(const INodePtr& node); void post(const INodePtr& node); }; /** * greebo: This Walker un-instantiates the visited nodes * The whole subgraph is traversed and erase() is * called on each nodes, AFTER it has been traversed. * * Contrary to InstanceSubgraphWalker this class is taking * a direct reference to Graph, since it is invoked in the * SceneGraph destructor. */ class UninstanceSubgraphWalker : public scene::NodeVisitor { private: Graph& _sceneGraph; public: UninstanceSubgraphWalker(Graph& sceneGraph); bool pre(const scene::INodePtr& node); void post(const scene::INodePtr& node); }; } // namespace scene DarkRadiant-2.5.0/libs/scene/LayerValidityCheckWalker.h000066400000000000000000000013601321750546400227760ustar00rootroot00000000000000#pragma once #include "ilayer.h" namespace scene { class LayerValidityCheckWalker : public NodeVisitor { private: std::size_t _numFixed; public: LayerValidityCheckWalker() : _numFixed(0) {} // scene::NodeVisitor bool pre(const INodePtr& node) { if (ProcessNode(node)) { _numFixed++; } return true; } // Returns true if the node has been "fixed" static bool ProcessNode(const INodePtr& node) { LayerList list = node->getLayers(); bool fixed = false; for (LayerList::iterator i = list.begin(); i != list.end(); ++i) { if (!GlobalLayerSystem().layerExists(*i)) { node->removeFromLayer(*i); fixed = true; } } return fixed; } std::size_t getNumFixed() const { return _numFixed; } }; } DarkRadiant-2.5.0/libs/scene/Makefile.am000066400000000000000000000005721321750546400177770ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs $(LIBSIGC_CFLAGS) pkglib_LTLIBRARIES = libscenegraph.la libscenegraph_la_LDFLAGS = -release @PACKAGE_VERSION@ $(LIBSIGC_LIBS) libscenegraph_la_LIBADD = $(top_builddir)/libs/math/libmath.la libscenegraph_la_SOURCES = InstanceWalkers.cpp \ TraversableNodeSet.cpp \ SelectableNode.cpp \ Node.cpp DarkRadiant-2.5.0/libs/scene/Node.cpp000066400000000000000000000242401321750546400173320ustar00rootroot00000000000000#include "Node.h" #include "itransformnode.h" #include "iscenegraph.h" #include "debugging/debugging.h" #include "InstanceWalkers.h" namespace scene { namespace { /** * greebo: This walker is used to traverse the children of * a given node and accumulate their values of worldAABB(). * * Note: This walker's pre() method always returns false, so only * the first order children are visited. */ class AABBAccumulateWalker : public scene::NodeVisitor { AABB& _aabb; public: AABBAccumulateWalker(AABB& aabb) : _aabb(aabb) {} virtual bool pre(const INodePtr& node) { _aabb.includeAABB(node->worldAABB()); // Don't traverse the children return false; } }; } // namespace Node::Node() : _state(eVisible), _isRoot(false), _id(getNewId()), // Get new auto-incremented ID _children(*this), _boundsChanged(true), _boundsMutex(false), _childBoundsChanged(true), _childBoundsMutex(false), _transformChanged(true), _transformMutex(false), _local2world(Matrix4::getIdentity()), _instantiated(false), _forceVisible(false), _renderEntity(nullptr) { // Each node is part of layer 0 by default _layers.insert(0); } Node::Node(const Node& other) : INode(other), std::enable_shared_from_this(other), _state(other._state), _isRoot(other._isRoot), _id(getNewId()), // ID is incremented on copy _children(*this), _boundsChanged(true), _boundsMutex(false), _childBoundsChanged(true), _childBoundsMutex(false), _local2world(other._local2world), _instantiated(false), _forceVisible(false), _layers(other._layers), _renderEntity(other._renderEntity) {} scene::INodePtr Node::getSelf() { return shared_from_this(); } void Node::resetIds() { _maxNodeId = 0; } unsigned long Node::getNewId() { return ++_maxNodeId; } void Node::setSceneGraph(const GraphPtr& sceneGraph) { _sceneGraph = sceneGraph; } bool Node::isRoot() const { return _isRoot; } void Node::setIsRoot(bool isRoot) { _isRoot = isRoot; } void Node::enable(unsigned int state) { _state |= state; } void Node::disable(unsigned int state) { _state &= ~state; } bool Node::checkStateFlag(unsigned int state) const { return (_state & state) != 0; } bool Node::visible() const { // Only instantiated nodes can be considered visible // The force visible flag is allowed to override the regular status return (_state == eVisible && _instantiated) || _forceVisible; } bool Node::excluded() const { return (_state & eExcluded) != 0; } void Node::addToLayer(int layerId) { _layers.insert(layerId); } void Node::moveToLayer(int layerId) { _layers.clear(); _layers.insert(layerId); } void Node::removeFromLayer(int layerId) { // Look up the layer ID and remove it from the list LayerList::iterator found = _layers.find(layerId); if (found != _layers.end()) { _layers.erase(found); // greebo: Make sure that every node is at least member of layer 0 if (_layers.empty()) { _layers.insert(0); } } } LayerList Node::getLayers() const { return _layers; } void Node::assignToLayers(const LayerList& newLayers) { if (!newLayers.empty()) { _layers = newLayers; } } void Node::addChildNode(const INodePtr& node) { // Add the node to the TraversableNodeSet, this triggers an // Node::onChildAdded() event, where the parent of the new // child is set, among other things _children.append(node); } void Node::addChildNodeToFront(const INodePtr& node) { // Add the node to the TraversableNodeSet at the front // This behaves the same as addChildNode(), triggering a // Node::onChildAdded() event, where the parent of the new // child is set, among other things _children.prepend(node); } void Node::removeChildNode(const INodePtr& node) { // Remove the node from the TraversableNodeSet, this triggers an // Node::onChildRemoved() event _children.erase(node); // Clear out the parent, this is not done in onChildRemoved(). node->setParent(INodePtr()); } bool Node::hasChildNodes() const { return !_children.empty(); } void Node::removeAllChildNodes() { _children.clear(); } void Node::traverse(NodeVisitor& visitor) { // First, visit the node itself INodePtr self = getSelf(); if (visitor.pre(self)) { // The walker requested to descend the children of this node as well traverseChildren(visitor); } visitor.post(self); } void Node::traverseChildren(NodeVisitor& visitor) const { if (!_children.empty()) { _children.traverse(visitor); } } bool Node::foreachNode(const VisitorFunc& functor) const { return _children.foreachNode(functor); } void Node::onChildAdded(const INodePtr& child) { // Double-check the parent of this new child node if (child->getParent().get() != this) { child->setParent(shared_from_this()); } // Pass down the RenderSystem to or children child->setRenderSystem(_renderSystem.lock()); // greebo: The bounds most probably change when child nodes are added boundsChanged(); if (!_instantiated) return; GraphPtr sceneGraph = _sceneGraph.lock(); if (sceneGraph) { InstanceSubgraphWalker visitor(sceneGraph); child->traverse(visitor); } } void Node::onChildRemoved(const INodePtr& child) { // Don't change the parent node of the new child on erase // greebo: The bounds are likely to change when child nodes are removed boundsChanged(); if (!_instantiated) return; GraphPtr sceneGraph = _sceneGraph.lock(); if (sceneGraph) { UninstanceSubgraphWalker visitor(*sceneGraph); child->traverse(visitor); } } void Node::onInsertIntoScene(IMapRootNode& root) { _instantiated = true; } void Node::onRemoveFromScene(IMapRootNode& root) { _instantiated = false; } void Node::connectUndoSystem(IMapFileChangeTracker& changeTracker) { _children.connectUndoSystem(changeTracker); } void Node::disconnectUndoSystem(IMapFileChangeTracker& changeTracker) { _children.disconnectUndoSystem(changeTracker); } TraversableNodeSet& Node::getTraversable() { return _children; } void Node::setParent(const INodePtr& parent) { _parent = parent; } scene::INodePtr Node::getParent() const { return _parent.lock(); } void Node::getPathRecursively(Path& targetPath) { INodePtr parent = getParent(); assert(parent.get() != this); // avoid loopbacks if (parent != NULL) { std::dynamic_pointer_cast(parent)->getPathRecursively(targetPath); } // After passing the call to the parent, add self targetPath.push(shared_from_this()); } Path Node::getPath() { Path result; INodePtr parent = getParent(); if (parent != NULL) { // We have a parent, walk up the ancestry std::dynamic_pointer_cast(parent)->getPathRecursively(result); } // Finally, add "self" to the path result.push(shared_from_this()); return result; } const AABB& Node::worldAABB() const { evaluateBounds(); return _bounds; } void Node::evaluateBounds() const { if (_boundsChanged) { ASSERT_MESSAGE(!_boundsMutex, "re-entering bounds evaluation"); _boundsMutex = true; _bounds = childBounds(); _bounds.includeAABB( AABB::createFromOrientedAABBSafe(localAABB(), localToWorld()) ); _boundsMutex = false; _boundsChanged = false; // Now that our bounds are re-calculated, notify the scenegraph GraphPtr sceneGraph = _sceneGraph.lock(); if (sceneGraph) { sceneGraph->nodeBoundsChanged(const_cast(this)->shared_from_this()); } } } const AABB& Node::childBounds() const { evaluateChildBounds(); return _childBounds; } void Node::evaluateChildBounds() const { if (_childBoundsChanged) { ASSERT_MESSAGE(!_childBoundsMutex, "re-entering bounds evaluation"); _childBoundsMutex = true; _childBounds = AABB(); // Instantiate an AABB accumulator AABBAccumulateWalker accumulator(_childBounds); // greebo: traverse the children of this node traverseChildren(accumulator); _childBoundsMutex = false; _childBoundsChanged = false; } } void Node::boundsChanged() { _boundsChanged = true; _childBoundsChanged = true; INodePtr parent = _parent.lock(); if (parent != NULL) { parent->boundsChanged(); } // greebo: It's enough if only root nodes call the global scenegraph // as nodes are passing their calls up to their parents anyway if (_isRoot) { GraphPtr sceneGraph = _sceneGraph.lock(); if (sceneGraph) { sceneGraph->boundsChanged(); } } } const Matrix4& Node::localToWorld() const { evaluateTransform(); return _local2world; } void Node::evaluateTransform() const { if (_transformChanged && !_transformMutex) { //ASSERT_MESSAGE(!_transformMutex, "re-entering transform evaluation"); _transformMutex = true; INodePtr parent = _parent.lock(); if (parent != NULL) { parent->boundsChanged(); } _local2world = (parent != NULL) ? parent->localToWorld() : Matrix4::getIdentity(); const ITransformNode* transformNode = dynamic_cast(this); if (transformNode != NULL) { _local2world.multiplyBy(transformNode->localToParent()); } _transformMutex = false; _transformChanged = false; } } void Node::transformChangedLocal() { _transformChanged = true; _transformMutex = false; _boundsChanged = true; _childBoundsChanged = true; if (_transformChangedCallback) { _transformChangedCallback(); } } void Node::transformChanged() { // First, notify ourselves transformChangedLocal(); // Next, traverse the children and notify them _children.foreachNode([this] (const scene::INodePtr& child)->bool { child->transformChangedLocal(); return true; }); boundsChanged(); } void Node::setTransformChangedCallback(const Callback& callback) { _transformChangedCallback = callback; } RenderSystemPtr Node::getRenderSystem() const { return _renderSystem.lock(); } void Node::setRenderSystem(const RenderSystemPtr& renderSystem) { _renderSystem = renderSystem; if (_children.empty()) return; // Propagate this call to all children _children.setRenderSystem(renderSystem); } void Node::setForcedVisibility(bool forceVisible, bool includeChildren) { _forceVisible = forceVisible; if (includeChildren) { _children.foreachNode([&](const INodePtr& node) { node->setForcedVisibility(forceVisible, includeChildren); return true; }); } } bool Node::isForcedVisible() const { return _forceVisible; } unsigned long Node::_maxNodeId = 0; } // namespace scene DarkRadiant-2.5.0/libs/scene/Node.h000066400000000000000000000136741321750546400170100ustar00rootroot00000000000000#pragma once #include "inode.h" #include "ipath.h" #include "irender.h" #include #include "TraversableNodeSet.h" #include "math/AABB.h" #include "math/Matrix4.h" #include "generic/callback.h" namespace scene { class Graph; typedef std::weak_ptr GraphWeakPtr; /// Main implementation of INode class Node : public virtual INode, public std::enable_shared_from_this { public: enum { eVisible = 0, eHidden = 1 << 0, // manually hidden by the user eFiltered = 1 << 1, // excluded due to filter settings eExcluded = 1 << 2, // excluded due to regioning eLayered = 1 << 3 // invisible at the current layer settings }; private: unsigned int _state; bool _isRoot; unsigned long _id; // Auto-incrementing ID (contains the largest ID in use) static unsigned long _maxNodeId; TraversableNodeSet _children; // A weak reference to the parent node INodeWeakPtr _parent; mutable AABB _bounds; mutable AABB _childBounds; mutable bool _boundsChanged; mutable bool _boundsMutex; mutable bool _childBoundsChanged; mutable bool _childBoundsMutex; mutable bool _transformChanged; mutable bool _transformMutex; Callback _transformChangedCallback; mutable Matrix4 _local2world; // Is true when the node is part of the scenegraph bool _instantiated; // A special flag capable of overriding the ordinary state flags // We use this to force the rendering of hidden but selected nodes bool _forceVisible; // The list of layers this object is associated to LayerList _layers; protected: // If this node is attached to a parent entity, this is the reference to it IRenderEntity* _renderEntity; // The render system for passing down the child hierarchy later on RenderSystemWeakPtr _renderSystem; // The scene::Graph we're belonging to (weak reference to avoid circular references) GraphWeakPtr _sceneGraph; public: Node(); Node(const Node& other); virtual ~Node() {} static void resetIds(); static unsigned long getNewId(); // Default name for generic nodes std::string name() const override { return "node"; } void setSceneGraph(const GraphPtr& sceneGraph) override; bool isRoot() const override; void setIsRoot(bool isRoot) override; void enable(unsigned int state) override; void disable(unsigned int state) override; bool checkStateFlag(unsigned int state) const override; bool visible() const override; bool excluded() const override; // Set the "forced visible" flag, only to be used internally by subclasses void setForcedVisibility(bool forceVisible, bool includeChildren) override; // Layered implementation virtual void addToLayer(int layerId) override; virtual void removeFromLayer(int layerId) override; virtual void moveToLayer(int layerId) override; virtual LayerList getLayers() const override; virtual void assignToLayers(const LayerList& newLayers) override; virtual void addChildNode(const INodePtr& node) override; virtual void addChildNodeToFront(const INodePtr& node) override; virtual void removeChildNode(const INodePtr& node) override; virtual bool hasChildNodes() const override; virtual void traverse(NodeVisitor& visitor) override; virtual void traverseChildren(NodeVisitor& visitor) const override; virtual bool foreachNode(const VisitorFunc& functor) const override; virtual void setParent(const INodePtr& parent) override; virtual scene::INodePtr getParent() const override; const AABB& worldAABB() const override; const AABB& childBounds() const; virtual void boundsChanged() override; /** * Return the filtered status of this Instance. */ virtual bool isFiltered() const override { return (_state & eFiltered) != 0; } /** * Set the filtered status of this Node. Setting filtered to true will * prevent the node from being rendered. */ virtual void setFiltered(bool filtered) override { if (filtered) { _state |= eFiltered; } else { _state &= ~eFiltered; } } const Matrix4& localToWorld() const override; void transformChangedLocal() override; void transformChanged() override; void setTransformChangedCallback(const Callback& callback); // greebo: This gets called as soon as a scene::Node gets inserted into // the TraversableNodeSet. This triggers an instantiation call on the child node. virtual void onChildAdded(const INodePtr& child); virtual void onChildRemoved(const INodePtr& child); // Gets called when this node is inserted into a scene graph virtual void onInsertIntoScene(IMapRootNode& root) override; virtual void onRemoveFromScene(IMapRootNode& root) override; // Returns TRUE if this node is inserted in the scene, FALSE otherwise bool inScene() const override { return _instantiated; } /** * greebo: Constructs the scene path to this node. This will walk up the * ancestors until it reaches the top node, so don't expect this to be * super fast. */ scene::Path getPath(); // Returns a shared reference to this node scene::INodePtr getSelf() override; IRenderEntity* getRenderEntity() const override { return _renderEntity; } // Set the render entity this node is attached to void setRenderEntity(IRenderEntity* entity) override { _renderEntity = entity; } // Base renderable implementation virtual RenderSystemPtr getRenderSystem() const; virtual void setRenderSystem(const RenderSystemPtr& renderSystem) override; protected: // Method for subclasses to check whether this node is forcedly visible bool isForcedVisible() const; // Fills in the ancestors and self (in this order) into the given targetPath. void getPathRecursively(scene::Path& targetPath); TraversableNodeSet& getTraversable(); virtual void connectUndoSystem(IMapFileChangeTracker& changeTracker); virtual void disconnectUndoSystem(IMapFileChangeTracker& changeTracker); // Clears the TraversableNodeSet virtual void removeAllChildNodes(); private: void evaluateBounds() const; void evaluateChildBounds() const; void evaluateTransform() const; }; typedef std::shared_ptr NodePtr; } // namespace scene DarkRadiant-2.5.0/libs/scene/SelectableNode.cpp000066400000000000000000000135211321750546400213160ustar00rootroot00000000000000#include "SelectableNode.h" #include "iselection.h" #include "BasicUndoMemento.h" #include "imap.h" #include #include #include namespace scene { SelectableNode::SelectableNode() : _selected(false), _undoStateSaver(nullptr) {} SelectableNode::SelectableNode(const SelectableNode& other) : scene::Node(other), _selected(false), _undoStateSaver(nullptr) {} SelectableNode::~SelectableNode() { setSelected(false); } void SelectableNode::onInsertIntoScene(IMapRootNode& root) { connectUndoSystem(root.getUndoChangeTracker()); Node::onInsertIntoScene(root); // If the group ID set is not empty, this node was likely removed while // it was still member of one or more groups. // Try to add ourselves to any groups we were assigned to, if the group // is not there anymore, it will be created. for (std::size_t id : _groups) { selection::ISelectionGroupPtr group = GlobalSelectionGroupManager().findOrCreateSelectionGroup(id); if (group) { group->addNode(getSelf()); } } } void SelectableNode::onRemoveFromScene(IMapRootNode& root) { setSelected(false); disconnectUndoSystem(root.getUndoChangeTracker()); // When a node is removed from the scene with a non-empty group assignment // we do notify the SelectionGroup to remove ourselves, but we keep the ID list // That way we can re-add ourselves when being inserted into the scene again if (!_groups.empty()) { // Copy the group IDs, as calling removeNode() will alter the group ID list GroupIds copy(_groups); // Remove ourselves from all groups while (!_groups.empty()) { std::size_t id = _groups.front(); selection::ISelectionGroupPtr group = GlobalSelectionGroupManager().getSelectionGroup(id); if (group) { group->removeNode(getSelf()); } else { _groups.erase(_groups.begin()); } } // Now copy the values back in for later use _groups.swap(copy); } Node::onRemoveFromScene(root); } void SelectableNode::setSelected(bool select) { // Set selection status and notify group members if applicable setSelected(select, false); } void SelectableNode::addToGroup(std::size_t groupId) { if (std::find(_groups.begin(), _groups.end(), groupId) == _groups.end()) { undoSave(); _groups.push_back(groupId); } } void SelectableNode::removeFromGroup(std::size_t groupId) { std::vector::iterator i = std::find(_groups.begin(), _groups.end(), groupId); if (i != _groups.end()) { undoSave(); _groups.erase(i); } } bool SelectableNode::isGroupMember() { return !_groups.empty(); } std::size_t SelectableNode::getMostRecentGroupId() { if (_groups.empty()) throw std::runtime_error("This node is not a member of any group."); return _groups.back(); } const SelectableNode::GroupIds& SelectableNode::getGroupIds() { return _groups; } void SelectableNode::setSelected(bool select, bool changeGroupStatus) { // Change state and invoke callback only if the new state is different // from the current state if (select ^ _selected) { _selected = select; onSelectionStatusChange(changeGroupStatus); } } bool SelectableNode::isSelected() const { return _selected; } IUndoMementoPtr SelectableNode::exportState() const { return IUndoMementoPtr(new undo::BasicUndoMemento(_groups)); } void SelectableNode::importState(const IUndoMementoPtr& state) { undoSave(); // Process the incoming set difference GroupIds newGroups = std::static_pointer_cast< undo::BasicUndoMemento >(state)->data(); // The set_difference algorithm requires the sets to be sorted std::sort(_groups.begin(), _groups.end()); std::sort(newGroups.begin(), newGroups.end()); GroupIds removedGroups; std::set_difference(_groups.begin(), _groups.end(), newGroups.begin(), newGroups.end(), std::inserter(removedGroups, removedGroups.begin())); GroupIds addedGroups; std::set_difference(newGroups.begin(), newGroups.end(), _groups.begin(), _groups.end(), std::inserter(addedGroups, addedGroups.begin())); // Remove ourselves from each removed group ID for (GroupIds::value_type id : removedGroups) { selection::ISelectionGroupPtr group = GlobalSelectionGroupManager().getSelectionGroup(id); if (group) { group->removeNode(getSelf()); } } // Add ourselves to each missing group ID for (GroupIds::value_type id : addedGroups) { selection::ISelectionGroupPtr group = GlobalSelectionGroupManager().findOrCreateSelectionGroup(id); assert(group); group->addNode(getSelf()); } // The _groups array should now contain the same elements as the imported ones #if _DEBUG std::sort(_groups.begin(), _groups.end()); assert(std::equal(_groups.begin(), _groups.end(), newGroups.begin())); #endif // After undoing group assignments, highlight this node for better user feedback setSelected(true, false); } void SelectableNode::onSelectionStatusChange(bool changeGroupStatus) { bool selected = isSelected(); // Update the flag to render selected nodes regardless of their hidden status setForcedVisibility(selected, true); GlobalSelectionSystem().onSelectedChanged(Node::getSelf(), *this); // Check if this node is member of a group if (changeGroupStatus && !_groups.empty()) { std::size_t mostRecentGroupId = _groups.back(); // Propagate the selection status of this node to all members of the topmost group GlobalSelectionGroupManager().setGroupSelected(mostRecentGroupId, selected); } } void SelectableNode::connectUndoSystem(IMapFileChangeTracker& changeTracker) { _undoStateSaver = GlobalUndoSystem().getStateSaver(*this, changeTracker); Node::connectUndoSystem(changeTracker); } void SelectableNode::disconnectUndoSystem(IMapFileChangeTracker& changeTracker) { _undoStateSaver = nullptr; GlobalUndoSystem().releaseStateSaver(*this); Node::disconnectUndoSystem(changeTracker); } void SelectableNode::undoSave() { if (_undoStateSaver != nullptr) { _undoStateSaver->save(*this); } } } // namespace DarkRadiant-2.5.0/libs/scene/SelectableNode.h000066400000000000000000000034611321750546400207650ustar00rootroot00000000000000#pragma once #include "scene/Node.h" #include "iselectiongroup.h" #include "iundo.h" namespace scene { /** * \brief * Subclass of scene::Node which implements the Selectable interface. * * The GlobalSelectionSystem will be notified of selection changes. */ class SelectableNode : public scene::Node, public IGroupSelectable, public IUndoable { private: // Current selection state bool _selected; // The groups this node is a member of. The last value in the list // represents the group this node has been added to most recently GroupIds _groups; IUndoStateSaver* _undoStateSaver; public: SelectableNode(); SelectableNode(const SelectableNode& other); virtual ~SelectableNode(); virtual void onInsertIntoScene(IMapRootNode& root) override; // override scene::Inode::onRemoveFromScene to de-select self virtual void onRemoveFromScene(IMapRootNode& root) override; /** * \brief * Set the selection state. */ virtual void setSelected(bool select) override; virtual void addToGroup(std::size_t groupId) override; virtual void removeFromGroup(std::size_t groupId) override; virtual bool isGroupMember() override; virtual std::size_t getMostRecentGroupId() override; virtual const GroupIds& getGroupIds() override; virtual void setSelected(bool select, bool changeGroupStatus) override; virtual bool isSelected() const override; IUndoMementoPtr exportState() const override; void importState(const IUndoMementoPtr& state) override; protected: /** * \brief * Invoked when the selection status changes. */ virtual void onSelectionStatusChange(bool changeGroupStatus); virtual void connectUndoSystem(IMapFileChangeTracker& changeTracker) override; virtual void disconnectUndoSystem(IMapFileChangeTracker& changeTracker) override; private: void undoSave(); }; } // namespace DarkRadiant-2.5.0/libs/scene/TraversableNodeSet.cpp000066400000000000000000000206531321750546400222050ustar00rootroot00000000000000#include "TraversableNodeSet.h" #include "debugging/debugging.h" #include #include "LayerValidityCheckWalker.h" #include "BasicUndoMemento.h" #include "mapfile.h" #include "Node.h" namespace scene { // An ObserverFunctor does something with the given owner and a given child struct ObserverFunctor { virtual ~ObserverFunctor() {} virtual void operator() (Node& owner, const INodePtr& node) = 0; }; // This calls onChildRemoved() on the given struct ObserverEraseFunctor : public ObserverFunctor { void operator() (Node& owner, const INodePtr& node) { owner.onChildRemoved(node); } }; // Adds all visited nodes to the public list "nodes" class CollectNodesFunctor : public ObserverFunctor { private: TraversableNodeSet::NodeList& _target; public: CollectNodesFunctor(TraversableNodeSet::NodeList& target) : _target(target) {} void operator() (Node& owner, const INodePtr& node) { _target.push_back(node); } }; /** greebo: This iterator is required by the std::set_difference algorithm and * is used to call owning node's onChildAdded() or onChildRemoved() * as soon as the assignment operator is invoked by the set_difference algorithm. * * Note: The operator++ is apparently necessary, but is not doing anything, as this iterator * is only "fake" and is only used to trigger the observer call. */ class ObserverOutputIterator { protected: Node* _owner; ObserverFunctor& _functor; public: typedef std::output_iterator_tag iterator_category; typedef void difference_type; typedef void value_type; typedef void pointer; typedef void reference; ObserverOutputIterator(Node& owner, ObserverFunctor& functor) : _owner(&owner), _functor(functor) {} // This function is invoked by the std::set_difference algorithm ObserverOutputIterator& operator=(const INodePtr& node) { // Pass the call to the functor _functor(*_owner, node); return *this; } // Assignment operator, as needed by VC++ 2010's STL implementation ObserverOutputIterator& operator=(const ObserverOutputIterator& other) { // Pass the call to the functor _owner = other._owner; _functor = other._functor; return *this; } ObserverOutputIterator& operator*() { return *this; } ObserverOutputIterator& operator++() { return *this; } ObserverOutputIterator& operator++(int) { return *this; } }; typedef undo::BasicUndoMemento UndoListMemento; // Default constructor, creates an empty set TraversableNodeSet::TraversableNodeSet(Node& owner) : _owner(owner), _undoStateSaver(NULL) {} // Destructor TraversableNodeSet::~TraversableNodeSet() { notifyEraseAll(); } void TraversableNodeSet::append(const INodePtr& node) { // Submit the UndoMemento to the UndoSystem undoSave(); // Insert the child node at the end of the list _children.push_back(node); // Notify the owner (note: this usually triggers instantiation of the node) _owner.onChildAdded(node); } void TraversableNodeSet::prepend(const INodePtr& node) { // Submit the UndoMemento to the UndoSystem undoSave(); // Insert the child node at the front of the list _children.push_front(node); // Notify the owner (note: this usually triggers instantiation of the node) _owner.onChildAdded(node); } void TraversableNodeSet::erase(const INodePtr& node) { undoSave(); // Notify the Observer before actually removing the node _owner.onChildRemoved(node); // Lookup the node and remove it from the list NodeList::iterator i = std::find(_children.begin(), _children.end(), node); if (i != _children.end()) { _children.erase(i); } } void TraversableNodeSet::clear() { undoSave(); notifyEraseAll(); _children.clear(); } void TraversableNodeSet::traverse(NodeVisitor& visitor) const { for (NodeList::const_iterator i = _children.begin(); i != _children.end();) { const scene::INodePtr& child = *(i++); // readability shortcut // Traverse the child using the visitor child->traverse(visitor); } } bool TraversableNodeSet::foreachNode(const INode::VisitorFunc& functor) const { for (NodeList::const_iterator i = _children.begin(); i != _children.end();) { const scene::INodePtr& child = *(i++); // readability shortcut // First, invoke the functor with this child node, // stopping traversal if the functor returns false if (!functor(child)) { return false; } // Pass the functor down the line, respecting the result if (!child->foreachNode(functor)) { return false; } } return true; // all nodes passed } bool TraversableNodeSet::empty() const { return _children.empty(); } void TraversableNodeSet::connectUndoSystem(IMapFileChangeTracker& changeTracker) { _undoStateSaver = GlobalUndoSystem().getStateSaver(*this, changeTracker); } void TraversableNodeSet::disconnectUndoSystem(IMapFileChangeTracker& changeTracker) { _undoStateSaver = nullptr; GlobalUndoSystem().releaseStateSaver(*this); } void TraversableNodeSet::undoSave() { if (_undoStateSaver != nullptr) { _undoStateSaver->save(*this); } } IUndoMementoPtr TraversableNodeSet::exportState() const { // Copy the current list of children and return the UndoMemento return IUndoMementoPtr(new UndoListMemento(_children)); } void TraversableNodeSet::importState(const IUndoMementoPtr& state) { undoSave(); // Import the child set from the state const NodeList& other = std::static_pointer_cast(state)->data(); // Copy the current container into a temporary one for later comparison std::vector before_sorted(_children.begin(), _children.end()); std::vector after_sorted(other.begin(), other.end()); // greebo: Now sort these, the set_difference algorithm requires the sets to be sorted std::sort(before_sorted.begin(), before_sorted.end()); std::sort(after_sorted.begin(), after_sorted.end()); // Import the state, overwriting the current set _children = other; // Now, handle the difference of and // The owning node needs to know about all nodes which are removed in , these are // instantly removed from the scenegraph ObserverEraseFunctor eraseFunctor; // greebo: Now find all the nodes that exist in <_children>, but not in and // call the EraseFunctor for each of them (the iterator calls onChildRemoved() on the owning node). std::set_difference( before_sorted.begin(), before_sorted.end(), after_sorted.begin(), after_sorted.end(), ObserverOutputIterator(_owner, eraseFunctor) ); // A special treatment is necessary for insertions of new nodes, as calling onChildAdded // right away might lead to double-insertions into the scenegraph (in case the same node // has not been removed from another node yet - a race condition during undo). // Therefore, collect all nodes that need to be added and process them in postUndo/postRedo. CollectNodesFunctor collectFunctor(_undoInsertBuffer); // greebo: Next step is to find all nodes existing in , but not in <_children>, // these have to be added, that's why the onChildAdded() method is called for each of them std::set_difference( after_sorted.begin(), after_sorted.end(), before_sorted.begin(), before_sorted.end(), ObserverOutputIterator(_owner, collectFunctor) ); if (!_undoInsertBuffer.empty()) { // Register to get notified when the undo operation is complete _undoHandler = GlobalUndoSystem().signal_postUndo().connect( sigc::mem_fun(this, &TraversableNodeSet::onUndoRedoOperationFinished)); _redoHandler = GlobalUndoSystem().signal_postRedo().connect( sigc::mem_fun(this, &TraversableNodeSet::onUndoRedoOperationFinished)); } } void TraversableNodeSet::onUndoRedoOperationFinished() { _undoHandler.disconnect(); _redoHandler.disconnect(); processInsertBuffer(); } void TraversableNodeSet::processInsertBuffer() { for (NodeList::const_iterator i = _undoInsertBuffer.begin(); i != _undoInsertBuffer.end(); ++i) { _owner.onChildAdded(*i); LayerValidityCheckWalker::ProcessNode(*i); } // Clear the buffer after this operation _undoInsertBuffer.clear(); } void TraversableNodeSet::notifyInsertAll() { for (NodeList::iterator i = _children.begin(); i != _children.end(); ++i) { _owner.onChildAdded(*i); } } void TraversableNodeSet::notifyEraseAll() { for (NodeList::iterator i = _children.begin(); i != _children.end(); ++i) { _owner.onChildRemoved(*i); } } void TraversableNodeSet::setRenderSystem(const RenderSystemPtr& renderSystem) { for (NodeList::iterator i = _children.begin(); i != _children.end(); ++i) { (*i)->setRenderSystem(renderSystem); } } } // namespace scene DarkRadiant-2.5.0/libs/scene/TraversableNodeSet.h000066400000000000000000000060401321750546400216440ustar00rootroot00000000000000#pragma once #include "inode.h" #include "iundo.h" #include #include "util/Noncopyable.h" #include #include class IMapFileChangeTracker; namespace scene { class Node; /// \brief A sequence of node references which notifies an observer of inserts and deletions, and uses the global undo system to provide undo for modifications. /** greebo: This is the container holding all the child nodes of a scene::Node. * * The TraversableNodeSet is also reporting any changes to the UndoSystem, that's what the * onInsertIntoScene(root) methods are for. An UndoMemento is submitted to the UndoSystem as soon * as any child nodes are removed or inserted. When the user hits Undo, the UndoSystem sends back * the memento and asks the TraversableNodeSet to overwrite its current children with the saved state. */ class TraversableNodeSet : public IUndoable, public util::Noncopyable, public sigc::trackable { public: typedef std::list NodeList; private: NodeList _children; // The owning node which gets notified upon insertion/deletion of child nodes Node& _owner; IUndoStateSaver* _undoStateSaver; // A list collecting nodes for insertion in postUndo/postRedo NodeList _undoInsertBuffer; sigc::connection _undoHandler; sigc::connection _redoHandler; public: // Default constructor, creates an empty set TraversableNodeSet(Node& owner); // Destructor ~TraversableNodeSet(); /** * greebo: This inserts a child node at the end of the list, * saves the Undo state and notifies the owning node. */ void append(const INodePtr& node); /** * greebo: This inserts a child node at the front of the list, * saves the Undo state and notifies the owning node. */ void prepend(const INodePtr& node); /** * greebo: This removes the node from the local set, saves the UndoMemento and notifies the owning node. */ void erase(const INodePtr& node); /** * Removes all nodes from this container. Notifies the owning node for each deleted node. */ void clear(); /** * greebo: This visits all the child nodes using the given visitor scene::NodeVisitor. */ void traverse(NodeVisitor& visitor) const; /** * Visits each contained node with the given functor, recursively, depth-first. */ bool foreachNode(const INode::VisitorFunc& functor) const; /** * greebo: Returns TRUE if this NodeSet is empty. */ bool empty() const; void connectUndoSystem(IMapFileChangeTracker& changeTracker); void disconnectUndoSystem(IMapFileChangeTracker& changeTracker); // Undoable implementation IUndoMementoPtr exportState() const; void importState(const IUndoMementoPtr& state); void setRenderSystem(const RenderSystemPtr& renderSystem); private: // UndoSystem event handler void onUndoRedoOperationFinished(); // Sends the current state to the undosystem void undoSave(); // Calls the owning node for each node in the undo insert buffer, // this is called right after an undo operation void processInsertBuffer(); void notifyInsertAll(); void notifyEraseAll(); }; } // namespace scene DarkRadiant-2.5.0/libs/scenelib.h000066400000000000000000000130751321750546400166050ustar00rootroot00000000000000#pragma once #include "inode.h" #include "iscenegraph.h" #include "iselectable.h" #include "ipatch.h" #include "ibrush.h" #include "scene/Node.h" #include inline bool Node_isPrimitive(const scene::INodePtr& node) { scene::INode::Type type = node->getNodeType(); // greebo: Changed this routine to use the nodeType enum instead of two dynamic casts // There shouldn't be any discrepancies, but I'll leave this assertion in here for a while assert((type == scene::INode::Type::Brush || type == scene::INode::Type::Patch) == (Node_isBrush(node) || Node_isPatch(node))); return type == scene::INode::Type::Brush || type == scene::INode::Type::Patch; } namespace scene { class ParentPrimitives : public scene::NodeVisitor { private: scene::INodePtr _parent; public: ParentPrimitives(const scene::INodePtr& parent) : _parent(parent) {} virtual bool pre(const scene::INodePtr& node) { return false; } virtual void post(const scene::INodePtr& node) { if (Node_isPrimitive(node)) { // We need to keep the hard reference to the node, such that the refcount doesn't reach 0 scene::INodePtr nodeRef = node; scene::INodePtr oldParent = nodeRef->getParent(); if (oldParent) { // greebo: remove the node from the old parent first oldParent->removeChildNode(nodeRef); } _parent->addChildNode(nodeRef); } } }; inline void parentPrimitives(const scene::INodePtr& subgraph, const scene::INodePtr& parent) { ParentPrimitives visitor(parent); subgraph->traverseChildren(visitor); } /** * Returns true if the given node contains * child primitives. Being an entity is not enough. */ inline bool hasChildPrimitives(const INodePtr& node) { // A node without child nodes is not a group if (!node->hasChildNodes()) { return false; } bool hasPrimitives = false; node->foreachNode([&] (const INodePtr& child)->bool { if (Node_isPrimitive(child)) { hasPrimitives = true; return false; // don't traverse any further } else { return true; } }); return hasPrimitives; } /** * greebo: This removes the given node from its parent node. * The node is also deselected beforehand. */ inline void removeNodeFromParent(const INodePtr& node) { // Check if the node has a parent in the first place INodePtr parent = node->getParent(); if (parent != NULL) { // Unselect the node Node_setSelected(node, false); parent->removeChildNode(node); } } /** * This assigns every visited node to the given set of layers. * Any previous assignments of the node get overwritten by this routine. */ class AssignNodeToLayersWalker : public NodeVisitor { const LayerList& _layers; public: AssignNodeToLayersWalker(const LayerList& layers) : _layers(layers) {} bool pre(const INodePtr& node) { // Pass the call to the single-node method node->assignToLayers(_layers); return true; // full traverse } }; class UpdateNodeVisibilityWalker : public NodeVisitor { std::stack _visibilityStack; public: bool pre(const INodePtr& node) { // Update the node visibility and store the result bool nodeIsVisible = GlobalLayerSystem().updateNodeVisibility(node); // Add a new element for this level _visibilityStack.push(nodeIsVisible); return true; } void post(const INodePtr& node) { // Is this child visible? bool childIsVisible = _visibilityStack.top(); _visibilityStack.pop(); if (childIsVisible) { // Show the node, regardless whether it was hidden before // otherwise the parent would hide the visible children as well node->disable(Node::eLayered); } if (node->checkStateFlag(Node::eLayered)) { // Node is hidden by layers after update (and no children are visible), de-select Node_setSelected(node, false); } if (childIsVisible && !_visibilityStack.empty()) { // The child was visible, set this parent to true _visibilityStack.top() = true; } } }; /** * greebo: This method inserts the given node into the given container * and ensures that the container's layer visibility is updated. */ inline void addNodeToContainer(const INodePtr& node, const INodePtr& container) { // Insert the child container->addChildNode(node); // Ensure that worldspawn is visible UpdateNodeVisibilityWalker walker; container->traverse(walker); } } // namespace scene inline bool Node_hasSelectedChildNodes(const scene::INodePtr& node) { bool selected = false; node->foreachNode([&] (const scene::INodePtr& child)->bool { if (Node_isSelected(child)) { selected = true; return false; // stop searching } return true; }); return selected; } namespace scene { /** * greebo: This walker removes all encountered child nodes without * traversing each node's children. This deselects all removed nodes as well. * * Use this to clear all children from a node: * * NodeRemover walker(); * node->traverse(walker); */ class NodeRemover : public scene::NodeVisitor { public: bool pre(const INodePtr& node) { // Copy the node, the reference might point right to // the parent's container scene::INodePtr copy(node); removeNodeFromParent(copy); return false; } }; } // namespace scene DarkRadiant-2.5.0/libs/selectionlib.h000066400000000000000000000025151321750546400174720ustar00rootroot00000000000000#pragma once #include "iselection.h" #include "math/Vector3.h" #include "math/AABB.h" /** greebo: A structure containing information about the current * Selection. An instance of this is maintained by the * RadiantSelectionSystem, and a const reference can be * retrieved via the according getSelectionInfo() method. */ class SelectionInfo { public: int totalCount; // number of selected items int patchCount; // number of selected patches int brushCount; // -- " -- brushes int entityCount; // -- " -- entities int componentCount; // -- " -- components (faces, edges, vertices) SelectionInfo() : totalCount(0), patchCount(0), brushCount(0), entityCount(0), componentCount(0) {} // Zeroes all the counters void clear() { totalCount = 0; patchCount = 0; brushCount = 0; entityCount = 0; componentCount = 0; } }; namespace selection { /** * The selection "WorkZone" defines the bounds of the most * recent selection. On each selection, the workzone is * recalculated, nothing happens on deselection. */ struct WorkZone { // The corner points defining the selection workzone Vector3 min; Vector3 max; // The bounds of the selection workzone (equivalent to min/max) AABB bounds; WorkZone() : min(-64,-64,-64), max(64,64,64), bounds(AABB::createFromMinMax(min, max)) {} }; } // namespace selection DarkRadiant-2.5.0/libs/shaderlib.h000066400000000000000000000034661321750546400167610ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "string/string.h" #include "character.h" #include "ishaders.h" #include "gamelib.h" inline bool shader_equal(const std::string& shader, const std::string& other) { return string_equal_nocase(shader.c_str(), other.c_str()); } inline bool shader_equal_n(const char* shader, const char* other, std::size_t n) { return string_equal_nocase_n(shader, other, n); } inline bool shader_valid(const char* shader) { return string_is_ascii(shader) && strchr(shader, ' ') == 0 && strchr(shader, '\n') == 0 && strchr(shader, '\r') == 0 && strchr(shader, '\t') == 0 && strchr(shader, '\v') == 0 && strchr(shader, '\\') == 0; } inline const char* GlobalTexturePrefix_get() { return GlobalMaterialManager().getTexturePrefix(); } inline const char* shader_get_textureName(const char* name) { return name + std::strlen(GlobalTexturePrefix_get()); } inline const std::string& texdef_name_default() { static std::string _default = game::current::getValue("/defaults/defaultTexture", "_default"); return _default; } DarkRadiant-2.5.0/libs/splines/000077500000000000000000000000001321750546400163175ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/splines/.cvsignore000066400000000000000000000000111321750546400203070ustar00rootroot00000000000000.consign DarkRadiant-2.5.0/libs/splines/math_angles.cpp000066400000000000000000000066131321750546400213130ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_shared.h" #include angles_t ang_zero( 0.0f, 0.0f, 0.0f ); void toAngles( mat3_t &src, angles_t &dst ) { double theta; double cp; double sp; sp = src[ 0 ][ 2 ]; // cap off our sin value so that we don't get any NANs if ( sp > 1.0 ) { sp = 1.0; } else if ( sp < -1.0 ) { sp = -1.0; } theta = -asin( sp ); cp = cos( theta ); if ( cp > 8192 * FLT_EPSILON ) { dst.pitch = theta * 180 / M_PI; dst.yaw = atan2( src[ 0 ][ 1 ], src[ 0 ][ 0 ] ) * 180 / M_PI; dst.roll = atan2( src[ 1 ][ 2 ], src[ 2 ][ 2 ] ) * 180 / M_PI; } else { dst.pitch = theta * 180 / M_PI; dst.yaw = -atan2( src[ 1 ][ 0 ], src[ 1 ][ 1 ] ) * 180 / M_PI; dst.roll = 0; } } void toAngles( quat_t &src, angles_t &dst ) { mat3_t temp; toMatrix( src, temp ); toAngles( temp, dst ); } void toAngles( idVec3 &src, angles_t &dst ) { dst.pitch = src[ 0 ]; dst.yaw = src[ 1 ]; dst.roll = src[ 2 ]; } void angles_t::toVectors( idVec3 *forward, idVec3 *right, idVec3 *up ) { float angle; static float sr, sp, sy, cr, cp, cy; // static to help MS compiler fp bugs angle = yaw * ( M_PI * 2 / 360 ); sy = sin( angle ); cy = cos( angle ); angle = pitch * ( M_PI * 2 / 360 ); sp = sin( angle ); cp = cos( angle ); angle = roll * ( M_PI * 2 / 360 ); sr = sin( angle ); cr = cos( angle ); if ( forward ) { forward->set( cp * cy, cp * sy, -sp ); } if ( right ) { right->set( -sr * sp * cy + cr * sy, -sr * sp * sy + -cr * cy, -sr * cp ); } if ( up ) { up->set( cr * sp * cy + -sr * -sy, cr * sp * sy + -sr * cy, cr * cp ); } } idVec3 angles_t::toForward( void ) { float angle; static float sp, sy, cp, cy; // static to help MS compiler fp bugs angle = yaw * ( M_PI * 2 / 360 ); sy = sin( angle ); cy = cos( angle ); angle = pitch * ( M_PI * 2 / 360 ); sp = sin( angle ); cp = cos( angle ); return idVec3( cp * cy, cp * sy, -sp ); } /* ================= Normalize360 returns angles normalized to the range [0 <= angle < 360] ================= */ angles_t& angles_t::Normalize360( void ) { pitch = (360.0 / 65536) * ( ( int )( pitch * ( 65536 / 360.0 ) ) & 65535 ); yaw = (360.0 / 65536) * ( ( int )( yaw * ( 65536 / 360.0 ) ) & 65535 ); roll = (360.0 / 65536) * ( ( int )( roll * ( 65536 / 360.0 ) ) & 65535 ); return *this; } /* ================= Normalize180 returns angles normalized to the range [-180 < angle <= 180] ================= */ angles_t& angles_t::Normalize180( void ) { Normalize360(); if ( pitch > 180.0 ) { pitch -= 360.0; } if ( yaw > 180.0 ) { yaw -= 360.0; } if ( roll > 180.0 ) { roll -= 360.0; } return *this; } DarkRadiant-2.5.0/libs/splines/math_angles.h000066400000000000000000000111341321750546400207520ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __MATH_ANGLES_H__ #define __MATH_ANGLES_H__ #include #include #include "math_vector.h" class mat3_t; class quat_t; class idVec3; typedef idVec3 &vec3_p; class angles_t { public: float pitch; float yaw; float roll; angles_t(); angles_t( float pitch, float yaw, float roll ); angles_t( const idVec3 &vec ); friend void toAngles( idVec3 &src, angles_t &dst ); friend void toAngles( quat_t &src, angles_t &dst ); friend void toAngles( mat3_t &src, angles_t &dst ); operator vec3_p(); float operator[]( int index ) const; float& operator[]( int index ); void set( float pitch, float yaw, float roll ); void operator=( angles_t const &a ); void operator=( idVec3 const &a ); friend angles_t operator+( const angles_t &a, const angles_t &b ); angles_t &operator+=( angles_t const &a ); angles_t &operator+=( idVec3 const &a ); friend angles_t operator-( angles_t &a, angles_t &b ); angles_t &operator-=( angles_t &a ); friend angles_t operator*( const angles_t &a, float b ); friend angles_t operator*( float a, const angles_t &b ); angles_t &operator*=( float a ); friend int operator==( angles_t &a, angles_t &b ); friend int operator!=( angles_t &a, angles_t &b ); void toVectors( idVec3 *forward, idVec3 *right = NULL, idVec3 *up = NULL ); idVec3 toForward( void ); angles_t &Zero( void ); angles_t &Normalize360( void ); angles_t &Normalize180( void ); }; extern angles_t ang_zero; inline angles_t::angles_t() {} inline angles_t::angles_t( float pitch, float yaw, float roll ) { this->pitch = pitch; this->yaw = yaw; this->roll = roll; } inline angles_t::angles_t( const idVec3 &vec ) { this->pitch = vec.x; this->yaw = vec.y; this->roll = vec.z; } inline float angles_t::operator[]( int index ) const { assert( ( index >= 0 ) && ( index < 3 ) ); return ( &pitch )[ index ]; } inline float& angles_t::operator[]( int index ) { assert( ( index >= 0 ) && ( index < 3 ) ); return ( &pitch )[ index ]; } inline angles_t::operator vec3_p( void ) { return *( idVec3 * )&pitch; } inline void angles_t::set( float pitch, float yaw, float roll ) { this->pitch = pitch; this->yaw = yaw; this->roll = roll; } inline void angles_t::operator=( angles_t const &a ) { pitch = a.pitch; yaw = a.yaw; roll = a.roll; } inline void angles_t::operator=( idVec3 const &a ) { pitch = a[ 0 ]; yaw = a[ 1 ]; roll = a[ 2 ]; } inline angles_t operator+( const angles_t &a, const angles_t &b ) { return angles_t( a.pitch + b.pitch, a.yaw + b.yaw, a.roll + b.roll ); } inline angles_t& angles_t::operator+=( angles_t const &a ) { pitch += a.pitch; yaw += a.yaw; roll += a.roll; return *this; } inline angles_t& angles_t::operator+=( idVec3 const &a ) { pitch += a.x; yaw += a.y; roll += a.z; return *this; } inline angles_t operator-( angles_t &a, angles_t &b ) { return angles_t( a.pitch - b.pitch, a.yaw - b.yaw, a.roll - b.roll ); } inline angles_t& angles_t::operator-=( angles_t &a ) { pitch -= a.pitch; yaw -= a.yaw; roll -= a.roll; return *this; } inline angles_t operator*( const angles_t &a, float b ) { return angles_t( a.pitch * b, a.yaw * b, a.roll * b ); } inline angles_t operator*( float a, const angles_t &b ) { return angles_t( a * b.pitch, a * b.yaw, a * b.roll ); } inline angles_t& angles_t::operator*=( float a ) { pitch *= a; yaw *= a; roll *= a; return *this; } inline int operator==( angles_t &a, angles_t &b ) { return ( ( a.pitch == b.pitch ) && ( a.yaw == b.yaw ) && ( a.roll == b.roll ) ); } inline int operator!=( angles_t &a, angles_t &b ) { return ( ( a.pitch != b.pitch ) || ( a.yaw != b.yaw ) || ( a.roll != b.roll ) ); } inline angles_t& angles_t::Zero( void ) { pitch = 0.0f; yaw = 0.0f; roll = 0.0f; return *this; } #endif /* !__MATH_ANGLES_H__ */ DarkRadiant-2.5.0/libs/splines/math_matrix.cpp000066400000000000000000000061331321750546400213430ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_shared.h" mat3_t mat3_default( idVec3( 1, 0, 0 ), idVec3( 0, 1, 0 ), idVec3( 0, 0, 1 ) ); void toMatrix( quat_t const &src, mat3_t &dst ) { float wx, wy, wz; float xx, yy, yz; float xy, xz, zz; float x2, y2, z2; x2 = src.x + src.x; y2 = src.y + src.y; z2 = src.z + src.z; xx = src.x * x2; xy = src.x * y2; xz = src.x * z2; yy = src.y * y2; yz = src.y * z2; zz = src.z * z2; wx = src.w * x2; wy = src.w * y2; wz = src.w * z2; dst[ 0 ][ 0 ] = 1.0f - ( yy + zz ); dst[ 0 ][ 1 ] = xy - wz; dst[ 0 ][ 2 ] = xz + wy; dst[ 1 ][ 0 ] = xy + wz; dst[ 1 ][ 1 ] = 1.0f - ( xx + zz ); dst[ 1 ][ 2 ] = yz - wx; dst[ 2 ][ 0 ] = xz - wy; dst[ 2 ][ 1 ] = yz + wx; dst[ 2 ][ 2 ] = 1.0f - ( xx + yy ); } void toMatrix( angles_t const &src, mat3_t &dst ) { float angle; static float sr, sp, sy, cr, cp, cy; // static to help MS compiler fp bugs angle = src.yaw * ( M_PI * 2.0f / 360.0f ); sy = sin( angle ); cy = cos( angle ); angle = src.pitch * ( M_PI * 2.0f / 360.0f ); sp = sin( angle ); cp = cos( angle ); angle = src.roll * ( M_PI * 2.0f / 360.0f ); sr = sin( angle ); cr = cos( angle ); dst[ 0 ].set( cp * cy, cp * sy, -sp ); dst[ 1 ].set( sr * sp * cy + cr * -sy, sr * sp * sy + cr * cy, sr * cp ); dst[ 2 ].set( cr * sp * cy + -sr * -sy, cr * sp * sy + -sr * cy, cr * cp ); } void toMatrix( idVec3 const &src, mat3_t &dst ) { angles_t sup = src; toMatrix(sup, dst); } void mat3_t::ProjectVector( const idVec3 &src, idVec3 &dst ) const { dst.x = src * mat[ 0 ]; dst.y = src * mat[ 1 ]; dst.z = src * mat[ 2 ]; } void mat3_t::UnprojectVector( const idVec3 &src, idVec3 &dst ) const { dst = mat[ 0 ] * src.x + mat[ 1 ] * src.y + mat[ 2 ] * src.z; } void mat3_t::Transpose( mat3_t &matrix ) { int i; int j; for( i = 0; i < 3; i++ ) { for( j = 0; j < 3; j++ ) { matrix[ i ][ j ] = mat[ j ][ i ]; } } } void mat3_t::Transpose( void ) { float temp; int i; int j; for( i = 0; i < 3; i++ ) { for( j = i + 1; j < 3; j++ ) { temp = mat[ i ][ j ]; mat[ i ][ j ] = mat[ j ][ i ]; mat[ j ][ i ] = temp; } } } mat3_t mat3_t::Inverse( void ) const { mat3_t inv( *this ); inv.Transpose(); return inv; } void mat3_t::Clear( void ) { mat[0].set( 1, 0, 0 ); mat[1].set( 0, 1, 0 ); mat[2].set( 0, 0, 1 ); } DarkRadiant-2.5.0/libs/splines/math_matrix.h000066400000000000000000000170201321750546400210050ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __MATH_MATRIX_H__ #define __MATH_MATRIX_H__ #include #include "math_vector.h" #ifndef ID_INLINE #ifdef _WIN32 #define ID_INLINE __inline #else #define ID_INLINE inline #endif #endif class quat_t; class angles_t; class mat3_t { public: idVec3 mat[ 3 ]; mat3_t(); mat3_t( float src[ 3 ][ 3 ] ); mat3_t( idVec3 const &x, idVec3 const &y, idVec3 const &z ); mat3_t( const float xx, const float xy, const float xz, const float yx, const float yy, const float yz, const float zx, const float zy, const float zz ); friend void toMatrix( quat_t const &src, mat3_t &dst ); friend void toMatrix( angles_t const &src, mat3_t &dst ); friend void toMatrix( idVec3 const &src, mat3_t &dst ); idVec3 operator[]( int index ) const; idVec3 &operator[]( int index ); idVec3 operator*( const idVec3 &vec ) const; mat3_t operator*( const mat3_t &a ) const; mat3_t operator*( float a ) const; mat3_t operator+( mat3_t const &a ) const; mat3_t operator-( mat3_t const &a ) const; friend idVec3 operator*( const idVec3 &vec, const mat3_t &mat ); friend mat3_t operator*( float a, mat3_t const &b ); mat3_t &operator*=( float a ); mat3_t &operator+=( mat3_t const &a ); mat3_t &operator-=( mat3_t const &a ); void Clear( void ); void ProjectVector( const idVec3 &src, idVec3 &dst ) const; void UnprojectVector( const idVec3 &src, idVec3 &dst ) const; void OrthoNormalize( void ); void Transpose( mat3_t &matrix ); void Transpose( void ); mat3_t Inverse( void ) const; void Identity( void ); friend void InverseMultiply( const mat3_t &inv, const mat3_t &b, mat3_t &dst ); friend mat3_t SkewSymmetric( idVec3 const &src ); }; ID_INLINE mat3_t::mat3_t() { } ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) { memcpy( mat, src, sizeof( src ) ); } ID_INLINE mat3_t::mat3_t( idVec3 const &x, idVec3 const &y, idVec3 const &z ) { mat[ 0 ].x = x.x; mat[ 0 ].y = x.y; mat[ 0 ].z = x.z; mat[ 1 ].x = y.x; mat[ 1 ].y = y.y; mat[ 1 ].z = y.z; mat[ 2 ].x = z.x; mat[ 2 ].y = z.y; mat[ 2 ].z = z.z; } ID_INLINE mat3_t::mat3_t( const float xx, const float xy, const float xz, const float yx, const float yy, const float yz, const float zx, const float zy, const float zz ) { mat[ 0 ].x = xx; mat[ 0 ].y = xy; mat[ 0 ].z = xz; mat[ 1 ].x = yx; mat[ 1 ].y = yy; mat[ 1 ].z = yz; mat[ 2 ].x = zx; mat[ 2 ].y = zy; mat[ 2 ].z = zz; } ID_INLINE idVec3 mat3_t::operator[]( int index ) const { assert( ( index >= 0 ) && ( index < 3 ) ); return mat[ index ]; } ID_INLINE idVec3& mat3_t::operator[]( int index ) { assert( ( index >= 0 ) && ( index < 3 ) ); return mat[ index ]; } ID_INLINE idVec3 mat3_t::operator*( const idVec3 &vec ) const { return idVec3( mat[ 0 ].x * vec.x + mat[ 1 ].x * vec.y + mat[ 2 ].x * vec.z, mat[ 0 ].y * vec.x + mat[ 1 ].y * vec.y + mat[ 2 ].y * vec.z, mat[ 0 ].z * vec.x + mat[ 1 ].z * vec.y + mat[ 2 ].z * vec.z ); } ID_INLINE mat3_t mat3_t::operator*( const mat3_t &a ) const { return mat3_t( mat[0].x * a[0].x + mat[0].y * a[1].x + mat[0].z * a[2].x, mat[0].x * a[0].y + mat[0].y * a[1].y + mat[0].z * a[2].y, mat[0].x * a[0].z + mat[0].y * a[1].z + mat[0].z * a[2].z, mat[1].x * a[0].x + mat[1].y * a[1].x + mat[1].z * a[2].x, mat[1].x * a[0].y + mat[1].y * a[1].y + mat[1].z * a[2].y, mat[1].x * a[0].z + mat[1].y * a[1].z + mat[1].z * a[2].z, mat[2].x * a[0].x + mat[2].y * a[1].x + mat[2].z * a[2].x, mat[2].x * a[0].y + mat[2].y * a[1].y + mat[2].z * a[2].y, mat[2].x * a[0].z + mat[2].y * a[1].z + mat[2].z * a[2].z ); } ID_INLINE mat3_t mat3_t::operator*( float a ) const { return mat3_t( mat[0].x * a, mat[0].y * a, mat[0].z * a, mat[1].x * a, mat[1].y * a, mat[1].z * a, mat[2].x * a, mat[2].y * a, mat[2].z * a ); } ID_INLINE mat3_t mat3_t::operator+( mat3_t const &a ) const { return mat3_t( mat[0].x + a[0].x, mat[0].y + a[0].y, mat[0].z + a[0].z, mat[1].x + a[1].x, mat[1].y + a[1].y, mat[1].z + a[1].z, mat[2].x + a[2].x, mat[2].y + a[2].y, mat[2].z + a[2].z ); } ID_INLINE mat3_t mat3_t::operator-( mat3_t const &a ) const { return mat3_t( mat[0].x - a[0].x, mat[0].y - a[0].y, mat[0].z - a[0].z, mat[1].x - a[1].x, mat[1].y - a[1].y, mat[1].z - a[1].z, mat[2].x - a[2].x, mat[2].y - a[2].y, mat[2].z - a[2].z ); } ID_INLINE idVec3 operator*( const idVec3 &vec, const mat3_t &mat ) { return idVec3( mat[ 0 ].x * vec.x + mat[ 1 ].x * vec.y + mat[ 2 ].x * vec.z, mat[ 0 ].y * vec.x + mat[ 1 ].y * vec.y + mat[ 2 ].y * vec.z, mat[ 0 ].z * vec.x + mat[ 1 ].z * vec.y + mat[ 2 ].z * vec.z ); } ID_INLINE mat3_t operator*( float a, mat3_t const &b ) { return mat3_t( b[0].x * a, b[0].y * a, b[0].z * a, b[1].x * a, b[1].y * a, b[1].z * a, b[2].x * a, b[2].y * a, b[2].z * a ); } ID_INLINE mat3_t &mat3_t::operator*=( float a ) { mat[0].x *= a; mat[0].y *= a; mat[0].z *= a; mat[1].x *= a; mat[1].y *= a; mat[1].z *= a; mat[2].x *= a; mat[2].y *= a; mat[2].z *= a; return *this; } ID_INLINE mat3_t &mat3_t::operator+=( mat3_t const &a ) { mat[0].x += a[0].x; mat[0].y += a[0].y; mat[0].z += a[0].z; mat[1].x += a[1].x; mat[1].y += a[1].y; mat[1].z += a[1].z; mat[2].x += a[2].x; mat[2].y += a[2].y; mat[2].z += a[2].z; return *this; } ID_INLINE mat3_t &mat3_t::operator-=( mat3_t const &a ) { mat[0].x -= a[0].x; mat[0].y -= a[0].y; mat[0].z -= a[0].z; mat[1].x -= a[1].x; mat[1].y -= a[1].y; mat[1].z -= a[1].z; mat[2].x -= a[2].x; mat[2].y -= a[2].y; mat[2].z -= a[2].z; return *this; } ID_INLINE void mat3_t::OrthoNormalize( void ) { mat[ 0 ].Normalize(); mat[ 2 ].Cross( mat[ 0 ], mat[ 1 ] ); mat[ 2 ].Normalize(); mat[ 1 ].Cross( mat[ 2 ], mat[ 0 ] ); mat[ 1 ].Normalize(); } ID_INLINE void mat3_t::Identity( void ) { mat[ 0 ].x = 1.f; mat[ 0 ].y = 0.f; mat[ 0 ].z = 0.f; mat[ 1 ].x = 0.f; mat[ 1 ].y = 1.f; mat[ 1 ].z = 0.f; mat[ 2 ].x = 0.f; mat[ 2 ].y = 0.f; mat[ 2 ].z = 1.f; } ID_INLINE void InverseMultiply( const mat3_t &inv, const mat3_t &b, mat3_t &dst ) { dst[0].x = inv[0].x * b[0].x + inv[1].x * b[1].x + inv[2].x * b[2].x; dst[0].y = inv[0].x * b[0].y + inv[1].x * b[1].y + inv[2].x * b[2].y; dst[0].z = inv[0].x * b[0].z + inv[1].x * b[1].z + inv[2].x * b[2].z; dst[1].x = inv[0].y * b[0].x + inv[1].y * b[1].x + inv[2].y * b[2].x; dst[1].y = inv[0].y * b[0].y + inv[1].y * b[1].y + inv[2].y * b[2].y; dst[1].z = inv[0].y * b[0].z + inv[1].y * b[1].z + inv[2].y * b[2].z; dst[2].x = inv[0].z * b[0].x + inv[1].z * b[1].x + inv[2].z * b[2].x; dst[2].y = inv[0].z * b[0].y + inv[1].z * b[1].y + inv[2].z * b[2].y; dst[2].z = inv[0].z * b[0].z + inv[1].z * b[1].z + inv[2].z * b[2].z; } ID_INLINE mat3_t SkewSymmetric( idVec3 const &src ) { return mat3_t( 0.0f, -src.z, src.y, src.z, 0.0f, -src.x, -src.y, src.x, 0.0f ); } extern mat3_t mat3_default; #endif /* !__MATH_MATRIX_H__ */ DarkRadiant-2.5.0/libs/splines/math_quaternion.cpp000066400000000000000000000037231321750546400222260ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "math_quaternion.h" #include "math_matrix.h" void toQuat( idVec3 &src, quat_t &dst ) { dst.x = src.x; dst.y = src.y; dst.z = src.z; dst.w = 0.0f; } void toQuat( angles_t &src, quat_t &dst ) { mat3_t temp; toMatrix( src, temp ); toQuat( temp, dst ); } void toQuat( mat3_t &src, quat_t &dst ) { float trace; float s; int i; int j; int k; static int next[ 3 ] = { 1, 2, 0 }; trace = src[ 0 ][ 0 ] + src[ 1 ][ 1 ] + src[ 2 ][ 2 ]; if ( trace > 0.0f ) { s = ( float )sqrt( trace + 1.0f ); dst.w = s * 0.5f; s = 0.5f / s; dst.x = ( src[ 2 ][ 1 ] - src[ 1 ][ 2 ] ) * s; dst.y = ( src[ 0 ][ 2 ] - src[ 2 ][ 0 ] ) * s; dst.z = ( src[ 1 ][ 0 ] - src[ 0 ][ 1 ] ) * s; } else { i = 0; if ( src[ 1 ][ 1 ] > src[ 0 ][ 0 ] ) { i = 1; } if ( src[ 2 ][ 2 ] > src[ i ][ i ] ) { i = 2; } j = next[ i ]; k = next[ j ]; s = ( float )sqrt( ( src[ i ][ i ] - ( src[ j ][ j ] + src[ k ][ k ] ) ) + 1.0f ); dst[ i ] = s * 0.5f; s = 0.5f / s; dst.w = ( src[ k ][ j ] - src[ j ][ k ] ) * s; dst[ j ] = ( src[ j ][ i ] + src[ i ][ j ] ) * s; dst[ k ] = ( src[ k ][ i ] + src[ i ][ k ] ) * s; } } DarkRadiant-2.5.0/libs/splines/math_quaternion.h000066400000000000000000000077211321750546400216750ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __MATH_QUATERNION_H__ #define __MATH_QUATERNION_H__ #include #include class idVec3_t; class angles_t; class mat3_t; class quat_t { public: float x; float y; float z; float w; quat_t(); quat_t( float x, float y, float z, float w ); friend void toQuat( idVec3_t &src, quat_t &dst ); friend void toQuat( angles_t &src, quat_t &dst ); friend void toQuat( mat3_t &src, quat_t &dst ); float *vec4( void ); float operator[]( int index ) const; float &operator[]( int index ); void set( float x, float y, float z, float w ); void operator=( quat_t a ); friend quat_t operator+( quat_t a, quat_t b ); quat_t &operator+=( quat_t a ); friend quat_t operator-( quat_t a, quat_t b ); quat_t &operator-=( quat_t a ); friend quat_t operator*( quat_t a, float b ); friend quat_t operator*( float a, quat_t b ); quat_t &operator*=( float a ); friend int operator==( quat_t a, quat_t b ); friend int operator!=( quat_t a, quat_t b ); float Length( void ); quat_t &Normalize( void ); quat_t operator-(); }; inline quat_t::quat_t() { } inline quat_t::quat_t( float x, float y, float z, float w ) { this->x = x; this->y = y; this->z = z; this->w = w; } inline float *quat_t::vec4( void ) { return &x; } inline float quat_t::operator[]( int index ) const { assert( ( index >= 0 ) && ( index < 4 ) ); return ( &x )[ index ]; } inline float& quat_t::operator[]( int index ) { assert( ( index >= 0 ) && ( index < 4 ) ); return ( &x )[ index ]; } inline void quat_t::set( float x, float y, float z, float w ) { this->x = x; this->y = y; this->z = z; this->w = w; } inline void quat_t::operator=( quat_t a ) { x = a.x; y = a.y; z = a.z; w = a.w; } inline quat_t operator+( quat_t a, quat_t b ) { return quat_t( a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w ); } inline quat_t& quat_t::operator+=( quat_t a ) { x += a.x; y += a.y; z += a.z; w += a.w; return *this; } inline quat_t operator-( quat_t a, quat_t b ) { return quat_t( a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w ); } inline quat_t& quat_t::operator-=( quat_t a ) { x -= a.x; y -= a.y; z -= a.z; w -= a.w; return *this; } inline quat_t operator*( quat_t a, float b ) { return quat_t( a.x * b, a.y * b, a.z * b, a.w * b ); } inline quat_t operator*( float a, quat_t b ) { return b * a; } inline quat_t& quat_t::operator*=( float a ) { x *= a; y *= a; z *= a; w *= a; return *this; } inline int operator==( quat_t a, quat_t b ) { return ( ( a.x == b.x ) && ( a.y == b.y ) && ( a.z == b.z ) && ( a.w == b.w ) ); } inline int operator!=( quat_t a, quat_t b ) { return ( ( a.x != b.x ) || ( a.y != b.y ) || ( a.z != b.z ) && ( a.w != b.w ) ); } inline float quat_t::Length( void ) { float length; length = x * x + y * y + z * z + w * w; return ( float )sqrt( length ); } inline quat_t& quat_t::Normalize( void ) { float length; float ilength; length = this->Length(); if ( length ) { ilength = 1 / length; x *= ilength; y *= ilength; z *= ilength; w *= ilength; } return *this; } inline quat_t quat_t::operator-() { return quat_t( -x, -y, -z, -w ); } #endif /* !__MATH_QUATERNION_H__ */ DarkRadiant-2.5.0/libs/splines/math_vector.cpp000066400000000000000000000055171321750546400213460ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "math_vector.h" #include #include #include #include #include #include #include #include #define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h #define LERP_DELTA 1e-6 idVec3 vec_zero( 0.0f, 0.0f, 0.0f ); Bounds boundsZero; float idVec3::toYaw( void ) { float yaw; if ( ( y == 0 ) && ( x == 0 ) ) { yaw = 0; } else { yaw = atan2( y, x ) * 180 / M_PI; if ( yaw < 0 ) { yaw += 360; } } return yaw; } float idVec3::toPitch( void ) { float forward; float pitch; if ( ( x == 0 ) && ( y == 0 ) ) { if ( z > 0 ) { pitch = 90; } else { pitch = 270; } } else { forward = ( float )idSqrt( x * x + y * y ); pitch = atan2( z, forward ) * 180 / M_PI; if ( pitch < 0 ) { pitch += 360; } } return pitch; } /* angles_t idVec3::toAngles( void ) { float forward; float yaw; float pitch; if ( ( x == 0 ) && ( y == 0 ) ) { yaw = 0; if ( z > 0 ) { pitch = 90; } else { pitch = 270; } } else { yaw = atan2( y, x ) * 180 / M_PI; if ( yaw < 0 ) { yaw += 360; } forward = ( float )idSqrt( x * x + y * y ); pitch = atan2( z, forward ) * 180 / M_PI; if ( pitch < 0 ) { pitch += 360; } } return angles_t( -pitch, yaw, 0 ); } */ idVec3 LerpVector( idVec3 &w1, idVec3 &w2, const float t ) { float omega, cosom, sinom, scale0, scale1; cosom = w1 * w2; if ( ( 1.0 - cosom ) > LERP_DELTA ) { omega = acos( cosom ); sinom = sin( omega ); scale0 = sin( ( 1.0 - t ) * omega ) / sinom; scale1 = sin( t * omega ) / sinom; } else { scale0 = 1.0 - t; scale1 = t; } return ( w1 * scale0 + w2 * scale1 ); } /* ============= idVec3::string This is just a convenience function for printing vectors ============= */ char *idVec3::string( void ) { static int index = 0; static char str[ 8 ][ 36 ]; char *s; // use an array so that multiple toString's won't collide s = str[ index ]; index = (index + 1)&7; sprintf( s, "%.2f %.2f %.2f", x, y, z ); return s; } DarkRadiant-2.5.0/libs/splines/math_vector.h000066400000000000000000000315361321750546400210130ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __MATH_VECTOR_H__ #define __MATH_VECTOR_H__ #ifdef WIN32 #pragma warning(disable : 4244) #endif #include #include //#define DotProduct(a,b) ((a)[0]*(b)[0]+(a)[1]*(b)[1]+(a)[2]*(b)[2]) //#define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2]) //#define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2]) //#define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2]) //#define VectorCopy(a,b) ((b).x=(a).x,(b).y=(a).y,(b).z=(a).z]) //#define VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s)) #define __VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s)) //#define CrossProduct(a,b,c) ((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0]) #define DotProduct4(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]+(x)[3]*(y)[3]) #define VectorSubtract4(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2],(c)[3]=(a)[3]-(b)[3]) #define VectorAdd4(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2],(c)[3]=(a)[3]+(b)[3]) #define VectorCopy4(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) #define VectorScale4(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s),(o)[3]=(v)[3]*(s)) #define VectorMA4(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s),(o)[3]=(v)[3]+(b)[3]*(s)) //#define VectorClear(a) ((a)[0]=(a)[1]=(a)[2]=0) #define VectorNegate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2]) //#define VectorSet(v, x, y, z) ((v)[0]=(x), (v)[1]=(y), (v)[2]=(z)) #define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) #define SnapVector(v) {v[0]=(int)v[0];v[1]=(int)v[1];v[2]=(int)v[2];} //#include "util_heap.h" #ifndef EQUAL_EPSILON #define EQUAL_EPSILON 0.001 #endif float Q_fabs( float f ); #ifndef ID_INLINE #ifdef _WIN32 #define ID_INLINE __inline #else #define ID_INLINE inline #endif #endif // if this is defined, vec3 will take four elements, which may allow // easier SIMD optimizations //#define FAT_VEC3 //#ifdef __ppc__ //#pragma align(16) //#endif class angles_t; #ifdef __ppc__ // Vanilla PPC code, but since PPC has a reciprocal square root estimate instruction, // runs *much* faster than calling sqrt(). We'll use two Newton-Raphson // refinement steps to get bunch more precision in the 1/sqrt() value for very little cost. // We'll then multiply 1/sqrt times the original value to get the sqrt. // This is about 12.4 times faster than sqrt() and according to my testing (not exhaustive) // it returns fairly accurate results (error below 1.0e-5 up to 100000.0 in 0.1 increments). static inline float idSqrt(float x) { const float half = 0.5; const float one = 1.0; float B, y0, y1; // This'll NaN if it hits frsqrte. Handle both +0.0 and -0.0 if (fabs(x) == 0.0) return x; B = x; #ifdef __GNUC__ asm("frsqrte %0,%1" : "=f" (y0) : "f" (B)); #else y0 = __frsqrte(B); #endif /* First refinement step */ y1 = y0 + half*y0*(one - B*y0*y0); /* Second refinement step -- copy the output of the last step to the input of this step */ y0 = y1; y1 = y0 + half*y0*(one - B*y0*y0); /* Get sqrt(x) from x * 1/sqrt(x) */ return x * y1; } #else static inline double idSqrt(double x) { return sqrt(x); } #endif //class idVec3 : public idHeap { class idVec3 { public: #ifndef FAT_VEC3 float x,y,z; #else float x,y,z,dist; #endif #ifndef FAT_VEC3 idVec3() {}; #else idVec3() {dist = 0.0f;}; #endif idVec3( const float x, const float y, const float z ); operator float *(); float operator[]( const int index ) const; float &operator[]( const int index ); void set( const float x, const float y, const float z ); idVec3 operator-() const; idVec3 &operator=( const idVec3 &a ); float operator*( const idVec3 &a ) const; idVec3 operator*( const float a ) const; friend idVec3 operator*( float a, idVec3 b ); idVec3 operator+( const idVec3 &a ) const; idVec3 operator-( const idVec3 &a ) const; idVec3 &operator+=( const idVec3 &a ); idVec3 &operator-=( const idVec3 &a ); idVec3 &operator*=( const float a ); int operator==( const idVec3 &a ) const; int operator!=( const idVec3 &a ) const; idVec3 Cross( const idVec3 &a ) const; idVec3 &Cross( const idVec3 &a, const idVec3 &b ); float Length( void ) const; float Normalize( void ); void Zero( void ); void Snap( void ); void SnapTowards( const idVec3 &to ); float toYaw( void ); float toPitch( void ); angles_t toAngles( void ); friend idVec3 LerpVector( const idVec3 &w1, const idVec3 &w2, const float t ); char *string( void ); }; extern idVec3 vec_zero; ID_INLINE idVec3::idVec3( const float x, const float y, const float z ) { this->x = x; this->y = y; this->z = z; #ifdef FAT_VEC3 this->dist = 0.0f; #endif } ID_INLINE float idVec3::operator[]( const int index ) const { return ( &x )[ index ]; } ID_INLINE float &idVec3::operator[]( const int index ) { return ( &x )[ index ]; } ID_INLINE idVec3::operator float *( void ) { return &x; } ID_INLINE idVec3 idVec3::operator-() const { return idVec3( -x, -y, -z ); } ID_INLINE idVec3 &idVec3::operator=( const idVec3 &a ) { x = a.x; y = a.y; z = a.z; return *this; } ID_INLINE void idVec3::set( const float x, const float y, const float z ) { this->x = x; this->y = y; this->z = z; } ID_INLINE idVec3 idVec3::operator-( const idVec3 &a ) const { return idVec3( x - a.x, y - a.y, z - a.z ); } ID_INLINE float idVec3::operator*( const idVec3 &a ) const { return x * a.x + y * a.y + z * a.z; } ID_INLINE idVec3 idVec3::operator*( const float a ) const { return idVec3( x * a, y * a, z * a ); } ID_INLINE idVec3 operator*( const float a, const idVec3 b ) { return idVec3( b.x * a, b.y * a, b.z * a ); } ID_INLINE idVec3 idVec3::operator+( const idVec3 &a ) const { return idVec3( x + a.x, y + a.y, z + a.z ); } ID_INLINE idVec3 &idVec3::operator+=( const idVec3 &a ) { x += a.x; y += a.y; z += a.z; return *this; } ID_INLINE idVec3 &idVec3::operator-=( const idVec3 &a ) { x -= a.x; y -= a.y; z -= a.z; return *this; } ID_INLINE idVec3 &idVec3::operator*=( const float a ) { x *= a; y *= a; z *= a; return *this; } ID_INLINE int idVec3::operator==( const idVec3 &a ) const { if ( Q_fabs( x - a.x ) > EQUAL_EPSILON ) { return false; } if ( Q_fabs( y - a.y ) > EQUAL_EPSILON ) { return false; } if ( Q_fabs( z - a.z ) > EQUAL_EPSILON ) { return false; } return true; } ID_INLINE int idVec3::operator!=( const idVec3 &a ) const { if ( Q_fabs( x - a.x ) > EQUAL_EPSILON ) { return true; } if ( Q_fabs( y - a.y ) > EQUAL_EPSILON ) { return true; } if ( Q_fabs( z - a.z ) > EQUAL_EPSILON ) { return true; } return false; } ID_INLINE idVec3 idVec3::Cross( const idVec3 &a ) const { return idVec3( y * a.z - z * a.y, z * a.x - x * a.z, x * a.y - y * a.x ); } ID_INLINE idVec3 &idVec3::Cross( const idVec3 &a, const idVec3 &b ) { x = a.y * b.z - a.z * b.y; y = a.z * b.x - a.x * b.z; z = a.x * b.y - a.y * b.x; return *this; } ID_INLINE float idVec3::Length( void ) const { float length; length = x * x + y * y + z * z; return ( float )idSqrt( length ); } ID_INLINE float idVec3::Normalize( void ) { float length; float ilength; length = this->Length(); if ( length ) { ilength = 1.0f / length; x *= ilength; y *= ilength; z *= ilength; } return length; } ID_INLINE void idVec3::Zero( void ) { x = 0.0f; y = 0.0f; z = 0.0f; } ID_INLINE void idVec3::Snap( void ) { x = float( int( x ) ); y = float( int( y ) ); z = float( int( z ) ); } /* ====================== SnapTowards Round a vector to integers for more efficient network transmission, but make sure that it rounds towards a given point rather than blindly truncating. This prevents it from truncating into a wall. ====================== */ ID_INLINE void idVec3::SnapTowards( const idVec3 &to ) { if ( to.x <= x ) { x = float( int( x ) ); } else { x = float( int( x ) + 1 ); } if ( to.y <= y ) { y = float( int( y ) ); } else { y = float( int( y ) + 1 ); } if ( to.z <= z ) { z = float( int( z ) ); } else { z = float( int( z ) + 1 ); } } //=============================================================== class Bounds { public: idVec3 b[2]; Bounds(); Bounds( const idVec3 &mins, const idVec3 &maxs ); void Clear(); void Zero(); float Radius(); // radius from origin, not from center idVec3 Center(); void AddPoint( const idVec3 &v ); void AddBounds( const Bounds &bb ); bool IsCleared(); bool ContainsPoint( const idVec3 &p ); bool IntersectsBounds( const Bounds &b2 ); // touching is NOT intersecting }; extern Bounds boundsZero; ID_INLINE Bounds::Bounds(){ } ID_INLINE bool Bounds::IsCleared() { return b[0][0] > b[1][0]; } ID_INLINE bool Bounds::ContainsPoint( const idVec3 &p ) { if ( p[0] < b[0][0] || p[1] < b[0][1] || p[2] < b[0][2] || p[0] > b[1][0] || p[1] > b[1][1] || p[2] > b[1][2] ) { return false; } return true; } ID_INLINE bool Bounds::IntersectsBounds( const Bounds &b2 ) { if ( b2.b[1][0] < b[0][0] || b2.b[1][1] < b[0][1] || b2.b[1][2] < b[0][2] || b2.b[0][0] > b[1][0] || b2.b[0][1] > b[1][1] || b2.b[0][2] > b[1][2] ) { return false; } return true; } ID_INLINE Bounds::Bounds( const idVec3 &mins, const idVec3 &maxs ) { b[0] = mins; b[1] = maxs; } ID_INLINE idVec3 Bounds::Center() { return idVec3( ( b[1][0] + b[0][0] ) * 0.5f, ( b[1][1] + b[0][1] ) * 0.5f, ( b[1][2] + b[0][2] ) * 0.5f ); } ID_INLINE void Bounds::Clear() { b[0][0] = b[0][1] = b[0][2] = 99999; b[1][0] = b[1][1] = b[1][2] = -99999; } ID_INLINE void Bounds::Zero() { b[0][0] = b[0][1] = b[0][2] = b[1][0] = b[1][1] = b[1][2] = 0; } ID_INLINE void Bounds::AddPoint( const idVec3 &v ) { if ( v[0] < b[0][0]) { b[0][0] = v[0]; } if ( v[0] > b[1][0]) { b[1][0] = v[0]; } if ( v[1] < b[0][1] ) { b[0][1] = v[1]; } if ( v[1] > b[1][1]) { b[1][1] = v[1]; } if ( v[2] < b[0][2] ) { b[0][2] = v[2]; } if ( v[2] > b[1][2]) { b[1][2] = v[2]; } } ID_INLINE void Bounds::AddBounds( const Bounds &bb ) { if ( bb.b[0][0] < b[0][0]) { b[0][0] = bb.b[0][0]; } if ( bb.b[0][1] < b[0][1]) { b[0][1] = bb.b[0][1]; } if ( bb.b[0][2] < b[0][2]) { b[0][2] = bb.b[0][2]; } if ( bb.b[1][0] > b[1][0]) { b[1][0] = bb.b[1][0]; } if ( bb.b[1][1] > b[1][1]) { b[1][1] = bb.b[1][1]; } if ( bb.b[1][2] > b[1][2]) { b[1][2] = bb.b[1][2]; } } ID_INLINE float Bounds::Radius( ) { int i; float total; float a, aa; total = 0; for (i=0 ; i<3 ; i++) { a = (float)fabs( b[0][i] ); aa = (float)fabs( b[1][i] ); if ( aa > a ) { a = aa; } total += a * a; } return (float)idSqrt( total ); } //=============================================================== class idVec2 { public: float x; float y; operator float *(); float operator[]( int index ) const; float &operator[]( int index ); }; ID_INLINE float idVec2::operator[]( int index ) const { return ( &x )[ index ]; } ID_INLINE float& idVec2::operator[]( int index ) { return ( &x )[ index ]; } ID_INLINE idVec2::operator float *( void ) { return &x; } class idVec4 : public idVec3 { public: #ifndef FAT_VEC3 float dist; #endif idVec4(); ~idVec4() {}; idVec4( float x, float y, float z, float dist ); float operator[]( int index ) const; float &operator[]( int index ); }; ID_INLINE idVec4::idVec4() {} ID_INLINE idVec4::idVec4( float x, float y, float z, float dist ) { this->x = x; this->y = y; this->z = z; this->dist = dist; } ID_INLINE float idVec4::operator[]( int index ) const { return ( &x )[ index ]; } ID_INLINE float& idVec4::operator[]( int index ) { return ( &x )[ index ]; } class idVec5_t : public idVec3 { public: float s; float t; float operator[]( int index ) const; float &operator[]( int index ); }; ID_INLINE float idVec5_t::operator[]( int index ) const { return ( &x )[ index ]; } ID_INLINE float& idVec5_t::operator[]( int index ) { return ( &x )[ index ]; } #endif /* !__MATH_VECTOR_H__ */ DarkRadiant-2.5.0/libs/splines/q_parse.cpp000066400000000000000000000243521321750546400204630ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // q_parse.c -- support for parsing text files #include "q_shared.h" /* ============================================================================ PARSING ============================================================================ */ // multiple character punctuation tokens static const char *punctuation[] = { "+=", "-=", "*=", "/=", "&=", "|=", "++", "--", "&&", "||", "<=", ">=", "==", "!=", NULL }; typedef struct { char token[MAX_TOKEN_CHARS]; int lines; qboolean ungetToken; char parseFile[MAX_QPATH]; } parseInfo_t; #define MAX_PARSE_INFO 16 static parseInfo_t parseInfo[MAX_PARSE_INFO]; static int parseInfoNum; static parseInfo_t *pi = &parseInfo[0]; /* =================== Com_BeginParseSession =================== */ void Com_BeginParseSession( const char *filename ) { if ( parseInfoNum == MAX_PARSE_INFO - 1 ) { Com_Error( ERR_FATAL, "Com_BeginParseSession: session overflow" ); } parseInfoNum++; pi = &parseInfo[parseInfoNum]; pi->lines = 1; Q_strncpyz( pi->parseFile, filename, sizeof( pi->parseFile ) ); } /* =================== Com_EndParseSession =================== */ void Com_EndParseSession( void ) { if ( parseInfoNum == 0 ) { Com_Error( ERR_FATAL, "Com_EndParseSession: session underflow" ); } parseInfoNum--; pi = &parseInfo[parseInfoNum]; } /* =================== Com_GetCurrentParseLine =================== */ int Com_GetCurrentParseLine( void ) { return pi->lines; } /* =================== Com_ScriptError Prints the script name and line number in the message =================== */ void Com_ScriptError( const char *msg, ... ) { va_list argptr; char string[32000]; va_start( argptr, msg ); vsprintf( string, msg,argptr ); va_end( argptr ); Com_Error( ERR_DROP, "File %s, line %i: %s", pi->parseFile, pi->lines, string ); } void Com_ScriptWarning( const char *msg, ... ) { va_list argptr; char string[32000]; va_start( argptr, msg ); vsprintf( string, msg,argptr ); va_end( argptr ); Com_Printf( "File %s, line %i: %s", pi->parseFile, pi->lines, string ); } /* =================== Com_UngetToken Calling this will make the next Com_Parse return the current token instead of advancing the pointer =================== */ void Com_UngetToken( void ) { if ( pi->ungetToken ) { Com_ScriptError( "UngetToken called twice" ); } pi->ungetToken = qtrue; } static const char *SkipWhitespace( const char (*data), qboolean *hasNewLines ) { int c; while( (c = *data) <= ' ') { if( !c ) { return NULL; } if( c == '\n' ) { pi->lines++; *hasNewLines = qtrue; } data++; } return data; } /* ============== Com_ParseExt Parse a token out of a string Will never return NULL, just empty strings. An empty string will only be returned at end of file. If "allowLineBreaks" is qtrue then an empty string will be returned if the next token is a newline. ============== */ static char *Com_ParseExt( const char *(*data_p), qboolean allowLineBreaks ) { int c = 0, len; qboolean hasNewLines = qfalse; const char *data; const char **punc; if ( !data_p ) { Com_Error( ERR_FATAL, "Com_ParseExt: NULL data_p" ); } data = *data_p; len = 0; pi->token[0] = 0; // make sure incoming data is valid if ( !data ) { *data_p = NULL; return pi->token; } // skip any leading whitespace while ( 1 ) { // skip whitespace data = SkipWhitespace( data, &hasNewLines ); if ( !data ) { *data_p = NULL; return pi->token; } if ( hasNewLines && !allowLineBreaks ) { *data_p = data; return pi->token; } c = *data; // skip double slash comments if ( c == '/' && data[1] == '/' ) { while (*data && *data != '\n') { data++; } continue; } // skip /* */ comments if ( c=='/' && data[1] == '*' ) { while ( *data && ( *data != '*' || data[1] != '/' ) ) { if( *data == '\n' ) { pi->lines++; } data++; } if ( *data ) { data += 2; } continue; } // a real token to parse break; } // handle quoted strings if ( c == '\"' ) { data++; while( 1 ) { c = *data++; if ( ( c=='\\' ) && ( *data == '\"' ) ) { // allow quoted strings to use \" to indicate the " character data++; } else if ( c=='\"' || !c ) { pi->token[len] = 0; *data_p = ( char * ) data; return pi->token; } else if( *data == '\n' ) { pi->lines++; } if ( len < MAX_TOKEN_CHARS - 1 ) { pi->token[len] = c; len++; } } } // check for a number // is this parsing of negative numbers going to cause expression problems if ( ( c >= '0' && c <= '9' ) || ( c == '-' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) || ( c == '.' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ) { do { if (len < MAX_TOKEN_CHARS - 1) { pi->token[len] = c; len++; } data++; c = *data; } while ( ( c >= '0' && c <= '9' ) || c == '.' ); // parse the exponent if ( c == 'e' || c == 'E' ) { if (len < MAX_TOKEN_CHARS - 1) { pi->token[len] = c; len++; } data++; c = *data; if ( c == '-' || c == '+' ) { if (len < MAX_TOKEN_CHARS - 1) { pi->token[len] = c; len++; } data++; c = *data; } do { if (len < MAX_TOKEN_CHARS - 1) { pi->token[len] = c; len++; } data++; c = *data; } while ( c >= '0' && c <= '9' ); } if (len == MAX_TOKEN_CHARS) { len = 0; } pi->token[len] = 0; *data_p = ( char * ) data; return pi->token; } // check for a regular word // we still allow forward and back slashes in name tokens for pathnames // and also colons for drive letters if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' || c == '/' || c == '\\' ) { do { if (len < MAX_TOKEN_CHARS - 1) { pi->token[len] = c; len++; } data++; c = *data; } while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' || ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' ); if (len == MAX_TOKEN_CHARS) { len = 0; } pi->token[len] = 0; *data_p = ( char * ) data; return pi->token; } // check for multi-character punctuation token for ( punc = punctuation ; *punc ; punc++ ) { int l; int j; l = strlen( *punc ); for ( j = 0 ; j < l ; j++ ) { if ( data[j] != (*punc)[j] ) { break; } } if ( j == l ) { // a valid multi-character punctuation memcpy( pi->token, *punc, l ); pi->token[l] = 0; data += l; *data_p = (char *)data; return pi->token; } } // single character punctuation pi->token[0] = *data; pi->token[1] = 0; data++; *data_p = (char *)data; return pi->token; } /* =================== Com_Parse =================== */ const char *Com_Parse( const char *(*data_p) ) { if ( pi->ungetToken ) { pi->ungetToken = qfalse; return pi->token; } return Com_ParseExt( data_p, qtrue ); } /* =================== Com_ParseOnLine =================== */ const char *Com_ParseOnLine( const char *(*data_p) ) { if ( pi->ungetToken ) { pi->ungetToken = qfalse; return pi->token; } return Com_ParseExt( data_p, qfalse ); } /* ================== Com_MatchToken ================== */ void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ) { const char *token; token = Com_Parse( buf_p ); if ( strcmp( token, match ) ) { if (warning) { Com_ScriptWarning( "MatchToken: %s != %s", token, match ); } else { Com_ScriptError( "MatchToken: %s != %s", token, match ); } } } /* ================= Com_SkipBracedSection The next token should be an open brace. Skips until a matching close brace is found. Internal brace depths are properly skipped. ================= */ void Com_SkipBracedSection( const char *(*program) ) { const char *token; int depth; depth = 0; do { token = Com_Parse( program ); if( token[1] == 0 ) { if( token[0] == '{' ) { depth++; } else if( token[0] == '}' ) { depth--; } } } while( depth && *program ); } /* ================= Com_SkipRestOfLine ================= */ void Com_SkipRestOfLine ( const char *(*data) ) { const char *p; int c; p = *data; while ( (c = *p++) != 0 ) { if ( c == '\n' ) { pi->lines++; break; } } *data = p; } /* ==================== Com_ParseRestOfLine ==================== */ const char *Com_ParseRestOfLine( const char *(*data_p) ) { static char line[MAX_TOKEN_CHARS]; const char *token; line[0] = 0; while( 1 ) { token = Com_ParseOnLine( data_p ); if ( !token[0] ) { break; } if ( line[0] ) { Q_strcat( line, sizeof(line), " " ); } Q_strcat( line, sizeof(line), token ); } return line; } float Com_ParseFloat( const char *(*buf_p) ) { const char *token; token = Com_Parse( buf_p ); if ( !token[0] ) { return 0; } return atof( token ); } int Com_ParseInt( const char *(*buf_p) ) { const char *token; token = Com_Parse( buf_p ); if ( !token[0] ) { return 0; } return (int)atof( token ); } void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ) { const char *token; int i; Com_MatchToken( buf_p, "(" ); for (i = 0 ; i < x ; i++) { token = Com_Parse(buf_p); m[i] = atof(token); } Com_MatchToken( buf_p, ")" ); } void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ) { int i; Com_MatchToken( buf_p, "(" ); for (i = 0 ; i < y ; i++) { Com_Parse1DMatrix (buf_p, x, m + i * x); } Com_MatchToken( buf_p, ")" ); } void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) { int i; Com_MatchToken( buf_p, "(" ); for (i = 0 ; i < z ; i++) { Com_Parse2DMatrix (buf_p, y, x, m + i * x*y); } Com_MatchToken( buf_p, ")" ); } DarkRadiant-2.5.0/libs/splines/q_shared.cpp000066400000000000000000000421121321750546400206110ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // q_shared.c -- stateless support routines that are included in each code dll #include "q_shared.h" /* ============================================================================ GROWLISTS ============================================================================ */ // malloc / free all in one place for debugging extern "C" void *Com_Allocate( int bytes ); extern "C" void Com_Dealloc( void *ptr ); void Com_InitGrowList( growList_t *list, int maxElements ) { list->maxElements = maxElements; list->currentElements = 0; list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) ); } int Com_AddToGrowList( growList_t *list, void *data ) { void **old; if ( list->currentElements != list->maxElements ) { list->elements[list->currentElements] = data; return list->currentElements++; } // grow, reallocate and move old = list->elements; if ( list->maxElements < 0 ) { Com_Error( ERR_FATAL, "Com_AddToGrowList: maxElements = %i", list->maxElements ); } if ( list->maxElements == 0 ) { // initialize the list to hold 100 elements Com_InitGrowList( list, 100 ); return Com_AddToGrowList( list, data ); } list->maxElements *= 2; Com_DPrintf( "Resizing growlist to %i maxElements\n", list->maxElements ); list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) ); if ( !list->elements ) { Com_Error( ERR_DROP, "Growlist alloc failed" ); } memcpy( list->elements, old, list->currentElements * sizeof( void * ) ); Com_Dealloc( old ); return Com_AddToGrowList( list, data ); } void *Com_GrowListElement( const growList_t *list, int index ) { if ( index < 0 || index >= list->currentElements ) { Com_Error( ERR_DROP, "Com_GrowListElement: %i out of range of %i", index, list->currentElements ); } return list->elements[index]; } int Com_IndexForGrowListElement( const growList_t *list, const void *element ) { int i; for ( i = 0 ; i < list->currentElements ; i++ ) { if ( list->elements[i] == element ) { return i; } } return -1; } //============================================================================ float Com_Clamp( float min, float max, float value ) { if ( value < min ) { return min; } if ( value > max ) { return max; } return value; } /* ============ Com_StringContains ============ */ const char *Com_StringContains( const char *str1, const char *str2, int casesensitive) { int len, i, j; len = strlen(str1) - strlen(str2); for (i = 0; i <= len; i++, str1++) { for (j = 0; str2[j]; j++) { if (casesensitive) { if (str1[j] != str2[j]) { break; } } else { if (toupper(str1[j]) != toupper(str2[j])) { break; } } } if (!str2[j]) { return str1; } } return NULL; } /* ============ Com_Filter ============ */ int Com_Filter( const char *filter, const char *name, int casesensitive) { char buf[MAX_TOKEN_CHARS]; const char *ptr; int i, found; while(*filter) { if (*filter == '*') { filter++; for (i = 0; *filter; i++) { if (*filter == '*' || *filter == '?') break; buf[i] = *filter; filter++; } buf[i] = '\0'; if (strlen(buf)) { ptr = Com_StringContains(name, buf, casesensitive); if (!ptr) return qfalse; name = ptr + strlen(buf); } } else if (*filter == '?') { filter++; name++; } else if (*filter == '[' && *(filter+1) == '[') { filter++; } else if (*filter == '[') { filter++; found = qfalse; while(*filter && !found) { if (*filter == ']' && *(filter+1) != ']') break; if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) { if (casesensitive) { if (*name >= *filter && *name <= *(filter+2)) found = qtrue; } else { if (toupper(*name) >= toupper(*filter) && toupper(*name) <= toupper(*(filter+2))) found = qtrue; } filter += 3; } else { if (casesensitive) { if (*filter == *name) found = qtrue; } else { if (toupper(*filter) == toupper(*name)) found = qtrue; } filter++; } } if (!found) return qfalse; while(*filter) { if (*filter == ']' && *(filter+1) != ']') break; filter++; } filter++; name++; } else { if (casesensitive) { if (*filter != *name) return qfalse; } else { if (toupper(*filter) != toupper(*name)) return qfalse; } filter++; name++; } } return qtrue; } /* ================ Com_HashString ================ */ int Com_HashString( const char *fname ) { int i; long hash; char letter; hash = 0; i = 0; while (fname[i] != '\0') { letter = tolower(fname[i]); if (letter =='.') break; // don't include extension if (letter =='\\') letter = '/'; // damn path names hash+=(long)(letter)*(i+119); i++; } hash &= (FILE_HASH_SIZE-1); return hash; } /* ============ Com_SkipPath ============ */ char *Com_SkipPath (char *pathname) { char *last; last = pathname; while (*pathname) { if (*pathname=='/') last = pathname+1; pathname++; } return last; } /* ============ Com_StripExtension ============ */ void Com_StripExtension( const char *in, char *out ) { while ( *in && *in != '.' ) { *out++ = *in++; } *out = 0; } /* ================== Com_DefaultExtension ================== */ void Com_DefaultExtension (char *path, int maxSize, const char *extension ) { char oldPath[MAX_QPATH]; char *src; // // if path doesn't have a .EXT, append extension // (extension should include the .) // src = path + strlen(path) - 1; while (*src != '/' && src != path) { if ( *src == '.' ) { return; // it has an extension } src--; } Q_strncpyz( oldPath, path, sizeof( oldPath ) ); Com_sprintf( path, maxSize, "%s%s", oldPath, extension ); } /* ============================================================================ BYTE ORDER FUNCTIONS ============================================================================ */ // can't just use function pointers, or dll linkage can // mess up when qcommon is included in multiple places static short (*_BigShort) (short l); static short (*_LittleShort) (short l); static int (*_BigLong) (int l); static int (*_LittleLong) (int l); static float (*_BigFloat) (float l); static float (*_LittleFloat) (float l); short BigShort(short l){return _BigShort(l);} short LittleShort(short l) {return _LittleShort(l);} int BigLong (int l) {return _BigLong(l);} int LittleLong (int l) {return _LittleLong(l);} float BigFloat (float l) {return _BigFloat(l);} float LittleFloat (float l) {return _LittleFloat(l);} short ShortSwap (short l) { byte b1,b2; b1 = l&255; b2 = (l>>8)&255; return (b1<<8) + b2; } short ShortNoSwap (short l) { return l; } int LongSwap (int l) { byte b1,b2,b3,b4; b1 = l&255; b2 = (l>>8)&255; b3 = (l>>16)&255; b4 = (l>>24)&255; return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; } int LongNoSwap (int l) { return l; } float FloatSwap (float f) { union { float f; byte b[4]; } dat1, dat2; dat1.f = f; dat2.b[0] = dat1.b[3]; dat2.b[1] = dat1.b[2]; dat2.b[2] = dat1.b[1]; dat2.b[3] = dat1.b[0]; return dat2.f; } float FloatNoSwap (float f) { return f; } /* ================ Swap_Init ================ */ void Swap_Init (void) { byte swaptest[2] = {1,0}; // set the byte swapping variables in a portable manner if ( *(short *)swaptest == 1) { _BigShort = ShortSwap; _LittleShort = ShortNoSwap; _BigLong = LongSwap; _LittleLong = LongNoSwap; _BigFloat = FloatSwap; _LittleFloat = FloatNoSwap; } else { _BigShort = ShortNoSwap; _LittleShort = ShortSwap; _BigLong = LongNoSwap; _LittleLong = LongSwap; _BigFloat = FloatNoSwap; _LittleFloat = FloatSwap; } } /* =============== Com_ParseInfos =============== */ int Com_ParseInfos( const char *buf, int max, char infos[][MAX_INFO_STRING] ) { const char *token; int count; char key[MAX_TOKEN_CHARS]; count = 0; while ( 1 ) { token = Com_Parse( &buf ); if ( !token[0] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } infos[count][0] = 0; while ( 1 ) { token = Com_Parse( &buf ); if ( !token[0] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = Com_ParseOnLine( &buf ); if ( !token[0] ) { token = ""; } Info_SetValueForKey( infos[count], key, token ); } count++; } return count; } /* ============================================================================ LIBRARY REPLACEMENT FUNCTIONS ============================================================================ */ int Q_isprint( int c ) { if ( c >= 0x20 && c <= 0x7E ) return ( 1 ); return ( 0 ); } int Q_islower( int c ) { if (c >= 'a' && c <= 'z') return ( 1 ); return ( 0 ); } int Q_isupper( int c ) { if (c >= 'A' && c <= 'Z') return ( 1 ); return ( 0 ); } int Q_isalpha( int c ) { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return ( 1 ); return ( 0 ); } char* Q_strrchr( const char* string, int c ) { char cc = c; char *s; char *sp=(char *)0; s = (char*)string; while (*s) { if (*s == cc) sp = s; s++; } if (cc == 0) sp = s; return sp; } /* ============= Q_strncpyz Safe strncpy that ensures a trailing zero ============= */ void Q_strncpyz( char *dest, const char *src, int destsize ) { if ( !src ) { Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" ); } if ( destsize < 1 ) { Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" ); } strncpy( dest, src, destsize-1 ); dest[destsize-1] = 0; } int Q_stricmpn (const char *s1, const char *s2, int n) { int c1, c2; do { c1 = *s1++; c2 = *s2++; if (!n--) { return 0; // strings are equal until end point } if (c1 != c2) { if (c1 >= 'a' && c1 <= 'z') { c1 -= ('a' - 'A'); } if (c2 >= 'a' && c2 <= 'z') { c2 -= ('a' - 'A'); } if (c1 != c2) { return c1 < c2 ? -1 : 1; } } } while (c1); return 0; // strings are equal } int Q_strncmp (const char *s1, const char *s2, int n) { int c1, c2; do { c1 = *s1++; c2 = *s2++; if (!n--) { return 0; // strings are equal until end point } if (c1 != c2) { return c1 < c2 ? -1 : 1; } } while (c1); return 0; // strings are equal } int Q_stricmp (const char *s1, const char *s2) { return Q_stricmpn (s1, s2, 99999); } char *Q_strlwr( char *s1 ) { char *s; s = s1; while ( *s ) { *s = tolower(*s); s++; } return s1; } char *Q_strupr( char *s1 ) { char *s; s = s1; while ( *s ) { *s = toupper(*s); s++; } return s1; } // never goes past bounds or leaves without a terminating 0 void Q_strcat( char *dest, int size, const char *src ) { int l1; l1 = strlen( dest ); if ( l1 >= size ) { Com_Error( ERR_FATAL, "Q_strcat: already overflowed" ); } Q_strncpyz( dest + l1, src, size - l1 ); } int Q_PrintStrlen( const char *string ) { int len; const char *p; if( !string ) { return 0; } len = 0; p = string; while( *p ) { if( Q_IsColorString( p ) ) { p += 2; continue; } p++; len++; } return len; } char *Q_CleanStr( char *string ) { char* d; char* s; int c; s = string; d = string; while ((c = *s) != 0 ) { if ( Q_IsColorString( s ) ) { s++; } else if ( c >= 0x20 && c <= 0x7E ) { *d++ = c; } s++; } *d = '\0'; return string; } void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) { int len; va_list argptr; char bigbuffer[32000]; // big, but small enough to fit in PPC stack va_start (argptr,fmt); len = vsprintf (bigbuffer,fmt,argptr); va_end (argptr); if ( len >= sizeof( bigbuffer ) ) { Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" ); } if (len >= size) { Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size); } Q_strncpyz (dest, bigbuffer, size ); } /* ============ va does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functions. FIXME: make this buffer size safe someday ============ */ char * QDECL va( char *format, ... ) { va_list argptr; static char string[2][32000]; // in case va is called by nested functions static int index = 0; char *buf; buf = string[index & 1]; index++; va_start (argptr, format); vsprintf (buf, format,argptr); va_end (argptr); return buf; } /* ===================================================================== INFO STRINGS ===================================================================== */ /* =============== Info_ValueForKey Searches the string for the given key and returns the associated value, or an empty string. FIXME: overflow check? =============== */ char *Info_ValueForKey( const char *s, const char *key ) { char pkey[MAX_INFO_KEY]; static char value[2][MAX_INFO_VALUE]; // use two buffers so compares // work without stomping on each other static int valueindex = 0; char *o; if ( !s || !key ) { return ""; } if ( strlen( s ) >= MAX_INFO_STRING ) { Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" ); } valueindex ^= 1; if (*s == '\\') s++; while (1) { o = pkey; while (*s != '\\') { if (!*s) return ""; *o++ = *s++; } *o = 0; s++; o = value[valueindex]; while (*s != '\\' && *s) { *o++ = *s++; } *o = 0; if (!Q_stricmp (key, pkey) ) return value[valueindex]; if (!*s) break; s++; } return ""; } /* =================== Info_NextPair Used to itterate through all the key/value pairs in an info string =================== */ void Info_NextPair( const char *(*head), char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ) { char *o; const char *s; s = *head; if ( *s == '\\' ) { s++; } key[0] = 0; value[0] = 0; o = key; while ( *s != '\\' ) { if ( !*s ) { *o = 0; *head = s; return; } *o++ = *s++; } *o = 0; s++; o = value; while ( *s != '\\' && *s ) { *o++ = *s++; } *o = 0; *head = s; } /* =================== Info_RemoveKey =================== */ void Info_RemoveKey( char *s, const char *key ) { char *start; char pkey[MAX_INFO_KEY]; char value[MAX_INFO_VALUE]; char *o; if ( strlen( s ) >= MAX_INFO_STRING ) { Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" ); } if (strchr (key, '\\')) { return; } while (1) { start = s; if (*s == '\\') s++; o = pkey; while (*s != '\\') { if (!*s) return; *o++ = *s++; } *o = 0; s++; o = value; while (*s != '\\' && *s) { if (!*s) return; *o++ = *s++; } *o = 0; if (!strcmp (key, pkey) ) { strcpy (start, s); // remove this part return; } if (!*s) return; } } /* ================== Info_Validate Some characters are illegal in info strings because they can mess up the server's parsing ================== */ qboolean Info_Validate( const char *s ) { if ( strchr( s, '\"' ) ) { return qfalse; } if ( strchr( s, ';' ) ) { return qfalse; } return qtrue; } /* ================== Info_SetValueForKey Changes or adds a key/value pair ================== */ void Info_SetValueForKey( char *s, const char *key, const char *value ) { char newi[MAX_INFO_STRING]; if ( strlen( s ) >= MAX_INFO_STRING ) { Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" ); } if (strchr (key, '\\') || strchr (value, '\\')) { Com_Printf ("Can't use keys or values with a \\\n"); return; } if (strchr (key, ';') || strchr (value, ';')) { Com_Printf ("Can't use keys or values with a semicolon\n"); return; } if (strchr (key, '\"') || strchr (value, '\"')) { Com_Printf ("Can't use keys or values with a \"\n"); return; } Info_RemoveKey (s, key); if (!value || !strlen(value)) return; Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value); if (strlen(newi) + strlen(s) > MAX_INFO_STRING) { Com_Printf ("Info string length exceeded\n"); return; } strcat (s, newi); } //==================================================================== /* =============== ParseHex =============== */ int ParseHex( const char *text ) { int value; int c; value = 0; while ( ( c = *text++ ) != 0 ) { if ( c >= '0' && c <= '9' ) { value = value * 16 + c - '0'; continue; } if ( c >= 'a' && c <= 'f' ) { value = value * 16 + 10 + c - 'a'; continue; } if ( c >= 'A' && c <= 'F' ) { value = value * 16 + 10 + c - 'A'; continue; } } return value; } DarkRadiant-2.5.0/libs/splines/q_shared.h000066400000000000000000000534551321750546400202720ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __Q_SHARED_H #define __Q_SHARED_H // q_shared.h -- included first by ALL program modules. // these are the definitions that have no dependance on // central system services, and can be used by any part // of the program without any state issues. // A user mod should never modify this file #define Q3_VERSION "DOOM 0.01" // alignment macros for SIMD #define ALIGN_ON #define ALIGN_OFF #ifdef _WIN32 #pragma warning(disable : 4018) // signed/unsigned mismatch #pragma warning(disable : 4032) #pragma warning(disable : 4051) #pragma warning(disable : 4057) // slightly different base types #pragma warning(disable : 4100) // unreferenced formal parameter #pragma warning(disable : 4115) #pragma warning(disable : 4125) // decimal digit terminates octal escape sequence #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4136) #pragma warning(disable : 4201) #pragma warning(disable : 4214) #pragma warning(disable : 4244) #pragma warning(disable : 4305) // truncation from const double to float #pragma warning(disable : 4310) // cast truncates constant value #pragma warning(disable : 4514) #pragma warning(disable : 4711) // selected for automatic inline expansion #pragma warning(disable : 4220) // varargs matches remaining parameters #endif #include #include #include #include #include #include #include #include #ifdef WIN32 // mac doesn't have malloc.h #include // for _alloca() #endif #ifdef _WIN32 //#pragma intrinsic( memset, memcpy ) #endif // this is the define for determining if we have an asm version of a C function #if (defined _M_IX86 || defined __i386__) && !defined __sun__ && !defined __LCC__ #define id386 1 #else #define id386 0 #endif // for windows fastcall option #define QDECL //======================= WIN32 DEFINES ================================= #ifdef WIN32 #define MAC_STATIC #undef QDECL #define QDECL __cdecl // buildstring will be incorporated into the version string #ifdef NDEBUG #ifdef _M_IX86 #define CPUSTRING "win-x86" #elif defined _M_ALPHA #define CPUSTRING "win-AXP" #endif #else #ifdef _M_IX86 #define CPUSTRING "win-x86-debug" #elif defined _M_ALPHA #define CPUSTRING "win-AXP-debug" #endif #endif #define PATH_SEP '\\' #endif //======================= MAC OS X SERVER DEFINES ===================== #if defined(__MACH__) && defined(__APPLE__) #define MAC_STATIC #ifdef __ppc__ #define CPUSTRING "MacOSXS-ppc" #elif defined __i386__ #define CPUSTRING "MacOSXS-i386" #else #define CPUSTRING "MacOSXS-other" #endif #define PATH_SEP '/' #define GAME_HARD_LINKED #define CGAME_HARD_LINKED #define UI_HARD_LINKED #define _alloca alloca #undef ALIGN_ON #undef ALIGN_OFF #define ALIGN_ON #pragma align(16) #define ALIGN_OFF #pragma align() #ifdef __cplusplus extern "C" { #endif void *osxAllocateMemory(long size); void osxFreeMemory(void *pointer); #ifdef __cplusplus } #endif #endif //======================= MAC DEFINES ================================= #ifdef __MACOS__ #define MAC_STATIC static #define CPUSTRING "MacOS-PPC" #define PATH_SEP ':' void Sys_PumpEvents( void ); #endif #ifdef __MRC__ #define MAC_STATIC #define CPUSTRING "MacOS-PPC" #define PATH_SEP ':' void Sys_PumpEvents( void ); #undef QDECL #define QDECL __cdecl #define _alloca alloca #endif //======================= LINUX DEFINES ================================= // the mac compiler can't handle >32k of locals, so we // just waste space and make big arrays static... #ifdef __linux__ #define MAC_STATIC #ifdef __i386__ #define CPUSTRING "linux-i386" #elif defined __axp__ #define CPUSTRING "linux-alpha" #else #define CPUSTRING "linux-other" #endif #define PATH_SEP '/' #endif //============================================================= typedef enum {qfalse, qtrue} qboolean; typedef unsigned char byte; #define EQUAL_EPSILON 0.001 typedef int qhandle_t; typedef int sfxHandle_t; typedef int fileHandle_t; typedef int clipHandle_t; typedef enum { INVALID_JOINT = -1 } jointHandle_t; #ifndef NULL #define NULL ((void *)0) #endif #define MAX_QINT 0x7fffffff #define MIN_QINT (-MAX_QINT-1) #ifndef max #define max( x, y ) ( ( ( x ) > ( y ) ) ? ( x ) : ( y ) ) #define min( x, y ) ( ( ( x ) < ( y ) ) ? ( x ) : ( y ) ) #endif #ifndef sign #define sign( f ) ( ( f > 0 ) ? 1 : ( ( f < 0 ) ? -1 : 0 ) ) #endif // angle indexes #define PITCH 0 // up / down #define YAW 1 // left / right #define ROLL 2 // fall over // the game guarantees that no string from the network will ever // exceed MAX_STRING_CHARS #define MAX_STRING_CHARS 1024 // max length of a string passed to Cmd_TokenizeString #define MAX_STRING_TOKENS 256 // max tokens resulting from Cmd_TokenizeString #define MAX_TOKEN_CHARS 1024 // max length of an individual token #define MAX_INFO_STRING 1024 #define MAX_INFO_KEY 1024 #define MAX_INFO_VALUE 1024 #define MAX_QPATH 64 // max length of a quake game pathname #define MAX_OSPATH 128 // max length of a filesystem pathname #define MAX_NAME_LENGTH 32 // max length of a client name // paramters for command buffer stuffing typedef enum { EXEC_NOW, // don't return until completed, a VM should NEVER use this, // because some commands might cause the VM to be unloaded... EXEC_INSERT, // insert at current position, but don't run yet EXEC_APPEND // add to end of the command buffer (normal case) } cbufExec_t; // // these aren't needed by any of the VMs. put in another header? // #define MAX_MAP_AREA_BYTES 32 // bit vector of area visibility #undef ERR_FATAL // malloc.h on unix // parameters to the main Error routine typedef enum { ERR_NONE, ERR_FATAL, // exit the entire game with a popup window ERR_DROP, // print to console and disconnect from game ERR_DISCONNECT, // don't kill server ERR_NEED_CD // pop up the need-cd dialog } errorParm_t; // font rendering values used by ui and cgame #define PROP_GAP_WIDTH 3 #define PROP_SPACE_WIDTH 8 #define PROP_HEIGHT 27 #define PROP_SMALL_SIZE_SCALE 0.75 #define BLINK_DIVISOR 200 #define PULSE_DIVISOR 75 #define UI_LEFT 0x00000000 // default #define UI_CENTER 0x00000001 #define UI_RIGHT 0x00000002 #define UI_FORMATMASK 0x00000007 #define UI_SMALLFONT 0x00000010 #define UI_BIGFONT 0x00000020 // default #define UI_GIANTFONT 0x00000040 #define UI_DROPSHADOW 0x00000800 #define UI_BLINK 0x00001000 #define UI_INVERSE 0x00002000 #define UI_PULSE 0x00004000 /* ============================================================== MATHLIB ============================================================== */ #ifdef __cplusplus // so we can include this in C code #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 #define SIDE_CROSS 3 #define Q_PI 3.14159265358979323846 #ifndef M_PI #define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h #endif #include "math_vector.h" #include "math_angles.h" #include "math_matrix.h" #include "math_quaternion.h" class idVec3; // for defining vectors typedef idVec3 &vec3_p; // for passing vectors as function arguments typedef const idVec3 &vec3_c; // for passing vectors as const function arguments class angles_t; // for defining angle vectors typedef angles_t &angles_p; // for passing angles as function arguments typedef const angles_t &angles_c; // for passing angles as const function arguments class mat3_t; // for defining matrices typedef mat3_t &mat3_p; // for passing matrices as function arguments typedef const mat3_t &mat3_c; // for passing matrices as const function arguments #define NUMVERTEXNORMALS 162 extern idVec3 bytedirs[NUMVERTEXNORMALS]; // all drawing is done to a 640*480 virtual screen size // and will be automatically scaled to the real resolution #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 #define TINYCHAR_WIDTH (SMALLCHAR_WIDTH) #define TINYCHAR_HEIGHT (SMALLCHAR_HEIGHT/2) #define SMALLCHAR_WIDTH 8 #define SMALLCHAR_HEIGHT 16 #define BIGCHAR_WIDTH 16 #define BIGCHAR_HEIGHT 16 #define GIANTCHAR_WIDTH 32 #define GIANTCHAR_HEIGHT 48 extern idVec4 colorBlack; extern idVec4 colorRed; extern idVec4 colorGreen; extern idVec4 colorBlue; extern idVec4 colorYellow; extern idVec4 colorMagenta; extern idVec4 colorCyan; extern idVec4 colorWhite; extern idVec4 colorLtGrey; extern idVec4 colorMdGrey; extern idVec4 colorDkGrey; #define Q_COLOR_ESCAPE '^' #define Q_IsColorString(p) ( p && *(p) == Q_COLOR_ESCAPE && *((p)+1) && *((p)+1) != Q_COLOR_ESCAPE ) #define COLOR_BLACK '0' #define COLOR_RED '1' #define COLOR_GREEN '2' #define COLOR_YELLOW '3' #define COLOR_BLUE '4' #define COLOR_CYAN '5' #define COLOR_MAGENTA '6' #define COLOR_WHITE '7' #define ColorIndex(c) ( ( (c) - '0' ) & 7 ) #define S_COLOR_BLACK "^0" #define S_COLOR_RED "^1" #define S_COLOR_GREEN "^2" #define S_COLOR_YELLOW "^3" #define S_COLOR_BLUE "^4" #define S_COLOR_CYAN "^5" #define S_COLOR_MAGENTA "^6" #define S_COLOR_WHITE "^7" extern idVec4 g_color_table[8]; #define MAKERGB( v, r, g, b ) v[0]=r;v[1]=g;v[2]=b #define MAKERGBA( v, r, g, b, a ) v[0]=r;v[1]=g;v[2]=b;v[3]=a #define DEG2RAD( a ) ( ( (a) * M_PI ) / 180.0F ) #define RAD2DEG( a ) ( ( (a) * 180.0f ) / M_PI ) struct cplane_s; extern idVec3 vec3_origin; extern idVec4 vec4_origin; extern mat3_t axisDefault; #define nanmask (255<<23) #define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) float Q_fabs( float f ); float Q_rsqrt( float f ); // reciprocal square root #define SQRTFAST( x ) ( 1.0f / Q_rsqrt( x ) ) signed char ClampChar( int i ); signed short ClampShort( int i ); // this isn't a real cheap function to call! int DirToByte( const idVec3 &dir ); void ByteToDir( int b, vec3_p dir ); #define DotProduct(a,b) ((a)[0]*(b)[0]+(a)[1]*(b)[1]+(a)[2]*(b)[2]) #define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2]) #define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2]) #define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2]) //#define VectorCopy(a,b) ((b).x=(a).x,(b).y=(a).y,(b).z=(a).z]) #define VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s)) #define VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s)) #define CrossProduct(a,b,c) ((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0]) #define DotProduct4(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]+(x)[3]*(y)[3]) #define VectorSubtract4(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2],(c)[3]=(a)[3]-(b)[3]) #define VectorAdd4(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2],(c)[3]=(a)[3]+(b)[3]) #define VectorCopy4(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) #define VectorScale4(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s),(o)[3]=(v)[3]*(s)) #define VectorMA4(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s),(o)[3]=(v)[3]+(b)[3]*(s)) #define VectorClear(a) ((a)[0]=(a)[1]=(a)[2]=0) #define VectorNegate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2]) #define VectorSet(v, x, y, z) ((v)[0]=(x), (v)[1]=(y), (v)[2]=(z)) #define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) #define SnapVector(v) {v[0]=(int)v[0];v[1]=(int)v[1];v[2]=(int)v[2];} float NormalizeColor( vec3_c in, vec3_p out ); int VectorCompare( vec3_c v1, vec3_c v2 ); float VectorLength( vec3_c v ); float Distance( vec3_c p1, vec3_c p2 ); float DistanceSquared( vec3_c p1, vec3_c p2 ); float VectorNormalize (vec3_p v); // returns vector length void VectorNormalizeFast(vec3_p v); // does NOT return vector length, uses rsqrt approximation float VectorNormalize2( vec3_c v, vec3_p out ); void VectorInverse (vec3_p v); void VectorRotate( vec3_c in, mat3_c matrix, vec3_p out ); void VectorPolar(vec3_p v, float radius, float theta, float phi); void VectorSnap(vec3_p v); void Vector53Copy( const idVec5_t &in, vec3_p out); void Vector5Scale( const idVec5_t &v, float scale, idVec5_t &out); void Vector5Add( const idVec5_t &va, const idVec5_t &vb, idVec5_t &out); void VectorRotate3( vec3_c vIn, vec3_c vRotation, vec3_p out); void VectorRotate3Origin(vec3_c vIn, vec3_c vRotation, vec3_c vOrigin, vec3_p out); int Q_log2(int val); int Q_rand( int *seed ); float Q_random( int *seed ); float Q_crandom( int *seed ); #define random() ((rand () & 0x7fff) / ((float)0x7fff)) #define crandom() (2.0 * (random() - 0.5)) float Q_rint( float in ); void vectoangles( vec3_c value1, angles_p angles); void AnglesToAxis( angles_c angles, mat3_p axis ); void AxisCopy( mat3_c in, mat3_p out ); qboolean AxisRotated( mat3_c in ); // assumes a non-degenerate axis int SignbitsForNormal( vec3_c normal ); int BoxOnPlaneSide( const Bounds &b, struct cplane_s *p ); float AngleMod(float a); float LerpAngle (float from, float to, float frac); float AngleSubtract( float a1, float a2 ); void AnglesSubtract( angles_c v1, angles_c v2, angles_p v3 ); float AngleNormalize360 ( float angle ); float AngleNormalize180 ( float angle ); float AngleDelta ( float angle1, float angle2 ); qboolean PlaneFromPoints( idVec4 &plane, vec3_c a, vec3_c b, vec3_c c ); void ProjectPointOnPlane( vec3_p dst, vec3_c p, vec3_c normal ); void RotatePointAroundVector( vec3_p dst, vec3_c dir, vec3_c point, float degrees ); void RotateAroundDirection( mat3_p axis, float yaw ); void MakeNormalVectors( vec3_c forward, vec3_p right, vec3_p up ); // perpendicular vector could be replaced by this int PlaneTypeForNormal( vec3_c normal ); void MatrixMultiply( mat3_c in1, mat3_c in2, mat3_p out ); void MatrixInverseMultiply( mat3_c in1, mat3_c in2, mat3_p out ); // in2 is transposed during multiply void MatrixTransformVector( vec3_c in, mat3_c matrix, vec3_p out ); void MatrixProjectVector( vec3_c in, mat3_c matrix, vec3_p out ); // Places the vector into a new coordinate system. void AngleVectors( angles_c angles, vec3_p forward, vec3_p right, vec3_p up); void PerpendicularVector( vec3_p dst, vec3_c src ); float TriangleArea( vec3_c a, vec3_c b, vec3_c c ); #endif // __cplusplus //============================================= float Com_Clamp( float min, float max, float value ); #define FILE_HASH_SIZE 1024 int Com_HashString( const char *fname ); char *Com_SkipPath( char *pathname ); // it is ok for out == in void Com_StripExtension( const char *in, char *out ); // "extension" should include the dot: ".map" void Com_DefaultExtension( char *path, int maxSize, const char *extension ); int Com_ParseInfos( const char *buf, int max, char infos[][MAX_INFO_STRING] ); /* ===================================================================================== SCRIPT PARSING ===================================================================================== */ // this just controls the comment printing, it doesn't actually load a file void Com_BeginParseSession( const char *filename ); void Com_EndParseSession( void ); int Com_GetCurrentParseLine( void ); // Will never return NULL, just empty strings. // An empty string will only be returned at end of file. // ParseOnLine will return empty if there isn't another token on this line // this funny typedef just means a moving pointer into a const char * buffer const char *Com_Parse( const char *(*data_p) ); const char *Com_ParseOnLine( const char *(*data_p) ); const char *Com_ParseRestOfLine( const char *(*data_p) ); void Com_UngetToken( void ); #ifdef __cplusplus void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning = qfalse ); #else void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ); #endif void Com_ScriptError( const char *msg, ... ); void Com_ScriptWarning( const char *msg, ... ); void Com_SkipBracedSection( const char *(*program) ); void Com_SkipRestOfLine( const char *(*data) ); float Com_ParseFloat( const char *(*buf_p) ); int Com_ParseInt( const char *(*buf_p) ); void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ); void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ); void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ); //===================================================================================== #ifdef __cplusplus extern "C" { #endif void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...); // mode parm for FS_FOpenFile typedef enum { FS_READ, FS_WRITE, FS_APPEND, FS_APPEND_SYNC } fsMode_t; typedef enum { FS_SEEK_CUR, FS_SEEK_END, FS_SEEK_SET } fsOrigin_t; //============================================= int Q_isprint( int c ); int Q_islower( int c ); int Q_isupper( int c ); int Q_isalpha( int c ); // portable case insensitive compare int Q_stricmp (const char *s1, const char *s2); int Q_strncmp (const char *s1, const char *s2, int n); int Q_stricmpn (const char *s1, const char *s2, int n); char *Q_strlwr( char *s1 ); char *Q_strupr( char *s1 ); char *Q_strrchr( const char* string, int c ); // buffer size safe library replacements void Q_strncpyz( char *dest, const char *src, int destsize ); void Q_strcat( char *dest, int size, const char *src ); // strlen that discounts Quake color sequences int Q_PrintStrlen( const char *string ); // removes color sequences from string char *Q_CleanStr( char *string ); int Com_Filter( const char *filter, const char *name, int casesensitive ); const char *Com_StringContains( const char *str1, const char *str2, int casesensitive ); //============================================= short BigShort(short l); short LittleShort(short l); int BigLong (int l); int LittleLong (int l); float BigFloat (float l); float LittleFloat (float l); void Swap_Init (void); char * QDECL va(char *format, ...); #ifdef __cplusplus } #endif //============================================= #ifdef __cplusplus // // mapfile parsing // typedef struct ePair_s { char *key; char *value; } ePair_t; typedef struct mapSide_s { char material[MAX_QPATH]; idVec4 plane; idVec4 textureVectors[2]; } mapSide_t; typedef struct { int numSides; mapSide_t **sides; } mapBrush_t; typedef struct { idVec3 xyz; float st[2]; } patchVertex_t; typedef struct { char material[MAX_QPATH]; int width, height; patchVertex_t *patchVerts; } mapPatch_t; typedef struct { char modelName[MAX_QPATH]; float matrix[16]; } mapModel_t; typedef struct mapPrimitive_s { int numEpairs; ePair_t **ePairs; // only one of these will be non-NULL mapBrush_t *brush; mapPatch_t *patch; mapModel_t *model; } mapPrimitive_t; typedef struct mapEntity_s { int numPrimitives; mapPrimitive_t **primitives; int numEpairs; ePair_t **ePairs; } mapEntity_t; typedef struct { int numEntities; mapEntity_t **entities; } mapFile_t; // the order of entities, brushes, and sides will be maintained, the // lists won't be swapped on each load or save mapFile_t *ParseMapFile( const char *text ); void FreeMapFile( mapFile_t *mapFile ); void WriteMapFile( const mapFile_t *mapFile, FILE *f ); // key names are case-insensitive const char *ValueForMapEntityKey( const mapEntity_t *ent, const char *key ); float FloatForMapEntityKey( const mapEntity_t *ent, const char *key ); qboolean GetVectorForMapEntityKey( const mapEntity_t *ent, const char *key, idVec3 &vec ); typedef struct { idVec3 xyz; idVec2 st; idVec3 normal; idVec3 tangents[2]; byte smoothing[4]; // colors for silhouette smoothing } drawVert_t; typedef struct { int width, height; drawVert_t *verts; } drawVertMesh_t; // Tesselate a map patch into smoothed, drawable vertexes // MaxError of around 4 is reasonable drawVertMesh_t *SubdivideMapPatch( const mapPatch_t *patch, float maxError ); #endif // __cplusplus //========================================= #ifdef __cplusplus extern "C" { #endif void QDECL Com_Error( int level, const char *error, ... ); void QDECL Com_Printf( const char *msg, ... ); void QDECL Com_DPrintf( const char *msg, ... ); #ifdef __cplusplus } #endif typedef struct { qboolean frameMemory; int currentElements; int maxElements; // will reallocate and move when exceeded void **elements; } growList_t; // you don't need to init the growlist if you don't mind it growing and moving // the list as it expands void Com_InitGrowList( growList_t *list, int maxElements ); int Com_AddToGrowList( growList_t *list, void *data ); void *Com_GrowListElement( const growList_t *list, int index ); int Com_IndexForGrowListElement( const growList_t *list, const void *element ); // // key / value info strings // char *Info_ValueForKey( const char *s, const char *key ); void Info_RemoveKey( char *s, const char *key ); void Info_SetValueForKey( char *s, const char *key, const char *value ); qboolean Info_Validate( const char *s ); void Info_NextPair( const char *(*s), char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ); // get cvar defs, collision defs, etc //#include "../shared/interface.h" // get key code numbers for events //#include "../shared/keycodes.h" #ifdef __cplusplus // get the polygon winding functions //#include "../shared/windings.h" // get the flags class //#include "../shared/idflags.h" #endif // __cplusplus #endif // __Q_SHARED_H DarkRadiant-2.5.0/libs/splines/splines.cpp000066400000000000000000001040241321750546400205010ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_shared.h" #include "splines.h" extern "C" { int FS_Write( const void *buffer, int len, fileHandle_t h ); int FS_ReadFile( const char *qpath, void **buffer ); void FS_FreeFile( void *buffer ); fileHandle_t FS_FOpenFileWrite( const char *filename ); void FS_FCloseFile( fileHandle_t f ); void Cbuf_AddText( const char *text ); void Cbuf_Execute (void); } float Q_fabs( float f ) { int tmp = * ( int * ) &f; tmp &= 0x7FFFFFFF; return * ( float * ) &tmp; } // (SA) making a list of cameras so I can use // the splines as targets for other things. // Certainly better ways to do this, but this lets // me get underway quickly with ents that need spline // targets. #define MAX_CAMERAS 64 idCameraDef camera[MAX_CAMERAS]; extern "C" { qboolean loadCamera(int camNum, const char *name) { if(camNum < 0 || camNum >= MAX_CAMERAS ) return qfalse; camera[camNum].clear(); return (qboolean)camera[camNum].load(name); } qboolean getCameraInfo(int camNum, int time, float *origin, float *angles, float *fov) { idVec3 dir, org; if(camNum < 0 || camNum >= MAX_CAMERAS ) return qfalse; org[0] = origin[0]; org[1] = origin[1]; org[2] = origin[2]; if (camera[camNum].getCameraInfo(time, org, dir, fov)) { origin[0] = org[0]; origin[1] = org[1]; origin[2] = org[2]; angles[1] = atan2 (dir[1], dir[0])*180/3.14159; angles[0] = asin (dir[2])*180/3.14159; return qtrue; } return qfalse; } void startCamera(int camNum, int time) { if(camNum < 0 || camNum >= MAX_CAMERAS ) return; camera[camNum].startCamera(time); } } //#include "../shared/windings.h" //#include "../qcommon/qcommon.h" //#include "../sys/sys_public.h" //#include "../game/game_entity.h" idCameraDef splineList; idCameraDef *g_splineList = &splineList; idVec3 idSplineList::zero(0,0,0); void glLabeledPoint(idVec3 &color, idVec3 &point, float size, const char *label) { glColor3fv(color); glPointSize(size); glBegin(GL_POINTS); glVertex3fv(point); glEnd(); idVec3 v = point; v.x += 1; v.y += 1; v.z += 1; glRasterPos3fv (v); glCallLists (strlen(label), GL_UNSIGNED_BYTE, label); } void glBox(idVec3 &color, idVec3 &point, float size) { idVec3 mins(point); idVec3 maxs(point); mins[0] -= size; mins[1] += size; mins[2] -= size; maxs[0] += size; maxs[1] -= size; maxs[2] += size; glColor3fv(color); glBegin(GL_LINE_LOOP); glVertex3f(mins[0],mins[1],mins[2]); glVertex3f(maxs[0],mins[1],mins[2]); glVertex3f(maxs[0],maxs[1],mins[2]); glVertex3f(mins[0],maxs[1],mins[2]); glEnd(); glBegin(GL_LINE_LOOP); glVertex3f(mins[0],mins[1],maxs[2]); glVertex3f(maxs[0],mins[1],maxs[2]); glVertex3f(maxs[0],maxs[1],maxs[2]); glVertex3f(mins[0],maxs[1],maxs[2]); glEnd(); glBegin(GL_LINES); glVertex3f(mins[0],mins[1],mins[2]); glVertex3f(mins[0],mins[1],maxs[2]); glVertex3f(mins[0],maxs[1],maxs[2]); glVertex3f(mins[0],maxs[1],mins[2]); glVertex3f(maxs[0],mins[1],mins[2]); glVertex3f(maxs[0],mins[1],maxs[2]); glVertex3f(maxs[0],maxs[1],maxs[2]); glVertex3f(maxs[0],maxs[1],mins[2]); glEnd(); } void splineTest() { //g_splineList->load("p:/doom/base/maps/test_base1.camera"); } void splineDraw() { //g_splineList->addToRenderer(); } //extern void D_DebugLine( const idVec3 &color, const idVec3 &start, const idVec3 &end ); void debugLine(idVec3 &color, float x, float y, float z, float x2, float y2, float z2) { idVec3 from(x, y, z); idVec3 to(x2, y2, z2); //D_DebugLine(color, from, to); } void idSplineList::addToRenderer() { if (controlPoints.Num() == 0) { return; } idVec3 mins, maxs; idVec3 yellow(1.0, 1.0, 0); idVec3 white(1.0, 1.0, 1.0); int i; for(i = 0; i < controlPoints.Num(); i++) { VectorCopy(*controlPoints[i], mins); VectorCopy(mins, maxs); mins[0] -= 8; mins[1] += 8; mins[2] -= 8; maxs[0] += 8; maxs[1] -= 8; maxs[2] += 8; debugLine( yellow, mins[0], mins[1], mins[2], maxs[0], mins[1], mins[2]); debugLine( yellow, maxs[0], mins[1], mins[2], maxs[0], maxs[1], mins[2]); debugLine( yellow, maxs[0], maxs[1], mins[2], mins[0], maxs[1], mins[2]); debugLine( yellow, mins[0], maxs[1], mins[2], mins[0], mins[1], mins[2]); debugLine( yellow, mins[0], mins[1], maxs[2], maxs[0], mins[1], maxs[2]); debugLine( yellow, maxs[0], mins[1], maxs[2], maxs[0], maxs[1], maxs[2]); debugLine( yellow, maxs[0], maxs[1], maxs[2], mins[0], maxs[1], maxs[2]); debugLine( yellow, mins[0], maxs[1], maxs[2], mins[0], mins[1], maxs[2]); } int step = 0; idVec3 step1; for(i = 3; i < controlPoints.Num(); i++) { for (float tension = 0.0f; tension < 1.001f; tension += 0.1f) { float x = 0; float y = 0; float z = 0; for (int j = 0; j < 4; j++) { x += controlPoints[i - (3 - j)]->x * calcSpline(j, tension); y += controlPoints[i - (3 - j)]->y * calcSpline(j, tension); z += controlPoints[i - (3 - j)]->z * calcSpline(j, tension); } if (step == 0) { step1[0] = x; step1[1] = y; step1[2] = z; step = 1; } else { debugLine( white, step1[0], step1[1], step1[2], x, y, z); step = 0; } } } } void idSplineList::buildSpline() { //int start = Sys_Milliseconds(); clearSpline(); for(int i = 3; i < controlPoints.Num(); i++) { for (float tension = 0.0f; tension < 1.001f; tension += granularity) { float x = 0; float y = 0; float z = 0; for (int j = 0; j < 4; j++) { x += controlPoints[i - (3 - j)]->x * calcSpline(j, tension); y += controlPoints[i - (3 - j)]->y * calcSpline(j, tension); z += controlPoints[i - (3 - j)]->z * calcSpline(j, tension); } splinePoints.Append(new idVec3(x, y, z)); } } dirty = false; //Com_Printf("Spline build took %f seconds\n", (float)(Sys_Milliseconds() - start) / 1000); } void idSplineList::draw(bool editMode) { int i; idVec4 yellow(1, 1, 0, 1); if (controlPoints.Num() == 0) { return; } if (dirty) { buildSpline(); } glColor3fv(controlColor); glPointSize(5); glBegin(GL_POINTS); for (i = 0; i < controlPoints.Num(); i++) { glVertex3fv(*controlPoints[i]); } glEnd(); if (editMode) { for(i = 0; i < controlPoints.Num(); i++) { glBox(activeColor, *controlPoints[i], 4); } } //Draw the curve glColor3fv(pathColor); glBegin(GL_LINE_STRIP); int count = splinePoints.Num(); for (i = 0; i < count; i++) { glVertex3fv(*splinePoints[i]); } glEnd(); if (editMode) { glColor3fv(segmentColor); glPointSize(3); glBegin(GL_POINTS); for (i = 0; i < count; i++) { glVertex3fv(*splinePoints[i]); } glEnd(); } if (count > 0) { //assert(activeSegment >=0 && activeSegment < count); if (activeSegment >=0 && activeSegment < count) { glBox(activeColor, *splinePoints[activeSegment], 6); glBox(yellow, *splinePoints[activeSegment], 8); } } } float idSplineList::totalDistance() { // FIXME: save dist and return // if (controlPoints.Num() == 0) { return 0.0; } if (dirty) { buildSpline(); } float dist = 0.0; idVec3 temp; int count = splinePoints.Num(); for(int i = 1; i < count; i++) { temp = *splinePoints[i-1]; temp -= *splinePoints[i]; dist += temp.Length(); } return dist; } void idSplineList::initPosition(long bt, long totalTime) { if (dirty) { buildSpline(); } if (splinePoints.Num() == 0) { return; } baseTime = bt; time = totalTime; // calc distance to travel ( this will soon be broken into time segments ) splineTime.Clear(); splineTime.Append(bt); double dist = totalDistance(); double distSoFar = 0.0; idVec3 temp; int count = splinePoints.Num(); //for(int i = 2; i < count - 1; i++) { for(int i = 1; i < count; i++) { temp = *splinePoints[i-1]; temp -= *splinePoints[i]; distSoFar += temp.Length(); double percent = distSoFar / dist; percent *= totalTime; splineTime.Append(percent + bt); } assert(splineTime.Num() == splinePoints.Num()); activeSegment = 0; } float idSplineList::calcSpline(int step, float tension) { switch(step) { case 0: return (pow(1 - tension, 3)) / 6; case 1: return (3 * pow(tension, 3) - 6 * pow(tension, 2) + 4) / 6; case 2: return (-3 * pow(tension, 3) + 3 * pow(tension, 2) + 3 * tension + 1) / 6; case 3: return pow(tension, 3) / 6; } return 0.0; } void idSplineList::updateSelection(const idVec3 &move) { if (selected) { dirty = true; VectorAdd(*selected, move, *selected); } } void idSplineList::setSelectedPoint(idVec3 *p) { if (p) { p->Snap(); for(int i = 0; i < controlPoints.Num(); i++) { if (*p == *controlPoints[i]) { selected = controlPoints[i]; } } } else { selected = NULL; } } const idVec3 *idSplineList::getPosition(long t) { static idVec3 interpolatedPos; static long lastTime = -1; int count = splineTime.Num(); if (count == 0) { return &zero; } // Com_Printf("Time: %d\n", t); assert(splineTime.Num() == splinePoints.Num()); while (activeSegment < count) { if (splineTime[activeSegment] >= t) { if (activeSegment > 0 && activeSegment < count - 1) { double timeHi = splineTime[activeSegment + 1]; double timeLo = splineTime[activeSegment - 1]; double percent = (timeHi - t) / (timeHi - timeLo); // pick two bounding points idVec3 v1 = *splinePoints[activeSegment-1]; idVec3 v2 = *splinePoints[activeSegment+1]; v2 *= (1.0 - percent); v1 *= percent; v2 += v1; interpolatedPos = v2; return &interpolatedPos; } return splinePoints[activeSegment]; } else { activeSegment++; } } return splinePoints[count-1]; } void idSplineList::parse(const char *(*text) ) { const char *token; //Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !Q_stricmp (token, "}") ) { break; } do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !Q_stricmp (token, "(") || !Q_stricmp(token, "}")) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine(text); const char *token = Com_Parse(text); if (Q_stricmp(key.c_str(), "granularity") == 0) { granularity = atof(token); } else if (Q_stricmp(key.c_str(), "name") == 0) { name = token; } token = Com_Parse(text); } while (1); if ( !Q_stricmp (token, "}") ) { break; } Com_UngetToken(); // read the control point idVec3 point; Com_Parse1DMatrix( text, 3, point ); addPoint(point.x, point.y, point.z); } while (1); //Com_UngetToken(); //Com_MatchToken( text, "}" ); dirty = true; } void idSplineList::write(fileHandle_t file, const char *p) { idStr s = va("\t\t%s {\n", p); FS_Write(s.c_str(), s.length(), file); //s = va("\t\tname %s\n", name.c_str()); //FS_Write(s.c_str(), s.length(), file); s = va("\t\t\tgranularity %f\n", granularity); FS_Write(s.c_str(), s.length(), file); int count = controlPoints.Num(); for (int i = 0; i < count; i++) { s = va("\t\t\t( %f %f %f )\n", controlPoints[i]->x, controlPoints[i]->y, controlPoints[i]->z); FS_Write(s.c_str(), s.length(), file); } s = "\t\t}\n"; FS_Write(s.c_str(), s.length(), file); } void idCameraDef::getActiveSegmentInfo(int segment, idVec3 &origin, idVec3 &direction, float *fov) { #if 0 if (!cameraSpline.validTime()) { buildCamera(); } double d = (double)segment / numSegments(); getCameraInfo(d * totalTime * 1000, origin, direction, fov); #endif /* if (!cameraSpline.validTime()) { buildCamera(); } origin = *cameraSpline.getSegmentPoint(segment); idVec3 temp; int numTargets = getTargetSpline()->controlPoints.Num(); int count = cameraSpline.splineTime.Num(); if (numTargets == 0) { // follow the path if (cameraSpline.getActiveSegment() < count - 1) { temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1]; } } else if (numTargets == 1) { temp = *getTargetSpline()->controlPoints[0]; } else { temp = *getTargetSpline()->getSegmentPoint(segment); } temp -= origin; temp.Normalize(); direction = temp; */ } bool idCameraDef::getCameraInfo(long time, idVec3 &origin, idVec3 &direction, float *fv) { char buff[1024]; if ((time - startTime) / 1000 > totalTime) { return false; } for (int i = 0; i < events.Num(); i++) { if (time >= startTime + events[i]->getTime() && !events[i]->getTriggered()) { events[i]->setTriggered(true); if (events[i]->getType() == idCameraEvent::EVENT_TARGET) { setActiveTargetByName(events[i]->getParam()); getActiveTarget()->start(startTime + events[i]->getTime()); //Com_Printf("Triggered event switch to target: %s\n",events[i]->getParam()); } else if (events[i]->getType() == idCameraEvent::EVENT_TRIGGER) { //idEntity *ent = NULL; //ent = level.FindTarget( ent, events[i]->getParam()); //if (ent) { // ent->signal( SIG_TRIGGER ); // ent->ProcessEvent( &EV_Activate, world ); //} } else if (events[i]->getType() == idCameraEvent::EVENT_FOV) { memset(buff, 0, sizeof(buff)); strcpy(buff, events[i]->getParam()); const char *param1 = strtok(buff, " \t,\0"); const char *param2 = strtok(NULL, " \t,\0"); float len = (param2) ? atof(param2) : 0; float newfov = (param1) ? atof(param1) : 90; fov.reset(fov.getFOV(time), newfov, time, len); //*fv = fov = atof(events[i]->getParam()); } else if (events[i]->getType() == idCameraEvent::EVENT_FADEIN) { float time = atof(events[i]->getParam()); Cbuf_AddText(va("fade 0 0 0 0 %f", time)); Cbuf_Execute(); } else if (events[i]->getType() == idCameraEvent::EVENT_FADEOUT) { float time = atof(events[i]->getParam()); Cbuf_AddText(va("fade 0 0 0 255 %f", time)); Cbuf_Execute(); } else if (events[i]->getType() == idCameraEvent::EVENT_CAMERA) { memset(buff, 0, sizeof(buff)); strcpy(buff, events[i]->getParam()); const char *param1 = strtok(buff, " \t,\0"); const char *param2 = strtok(NULL, " \t,\0"); if(param2) { loadCamera(atoi(param1), va("cameras/%s.camera", param2)); startCamera(time); } else { loadCamera(0, va("cameras/%s.camera", events[i]->getParam())); startCamera(time); } return true; } else if (events[i]->getType() == idCameraEvent::EVENT_STOP) { return false; } } } origin = *cameraPosition->getPosition(time); *fv = fov.getFOV(time); idVec3 temp = origin; int numTargets = targetPositions.Num(); if (numTargets == 0) { /* // follow the path if (cameraSpline.getActiveSegment() < count - 1) { temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1]; if (temp == origin) { int index = cameraSpline.getActiveSegment() + 2; while (temp == origin && index < count - 1) { temp = *cameraSpline.splinePoints[index++]; } } } */ } else { if( getActiveTarget()->numPoints() > 0 ) { temp = *getActiveTarget()->getPosition(time); } } temp -= origin; temp.Normalize(); direction = temp; return true; } bool idCameraDef::waitEvent(int index) { //for (int i = 0; i < events.Num(); i++) { // if (events[i]->getSegment() == index && events[i]->getType() == idCameraEvent::EVENT_WAIT) { // return true; // } //} return false; } #define NUM_CCELERATION_SEGS 10 #define CELL_AMT 5 void idCameraDef::buildCamera() { int i; int lastSwitch = 0; idList waits; idList targets; totalTime = baseTime; cameraPosition->setTime((long)totalTime * 1000); // we have a base time layout for the path and the target path // now we need to layer on any wait or speed changes for (i = 0; i < events.Num(); i++) { idCameraEvent *ev = events[i]; events[i]->setTriggered(false); switch (events[i]->getType()) { case idCameraEvent::EVENT_TARGET : { targets.Append(i); break; } case idCameraEvent::EVENT_FEATHER : { long startTime = 0; float speed = 0; long loopTime = 10; float stepGoal = cameraPosition->getBaseVelocity() / (1000 / loopTime); while (startTime <= 1000) { cameraPosition->addVelocity(startTime, loopTime, speed); speed += stepGoal; if (speed > cameraPosition->getBaseVelocity()) { speed = cameraPosition->getBaseVelocity(); } startTime += loopTime; } startTime = (long)(totalTime * 1000 - 1000); long endTime = startTime + 1000; speed = cameraPosition->getBaseVelocity(); while (startTime < endTime) { speed -= stepGoal; if (speed < 0) { speed = 0; } cameraPosition->addVelocity(startTime, loopTime, speed); startTime += loopTime; } break; } case idCameraEvent::EVENT_WAIT : { waits.Append(atof(events[i]->getParam())); //FIXME: this is quite hacky for Wolf E3, accel and decel needs // do be parameter based etc.. long startTime = events[i]->getTime() - 1000; if (startTime < 0) { startTime = 0; } float speed = cameraPosition->getBaseVelocity(); long loopTime = 10; float steps = speed / ((events[i]->getTime() - startTime) / loopTime); while (startTime <= events[i]->getTime() - loopTime) { cameraPosition->addVelocity(startTime, loopTime, speed); speed -= steps; startTime += loopTime; } cameraPosition->addVelocity(events[i]->getTime(), (long)atof(events[i]->getParam()) * 1000, 0); startTime = (long)(events[i]->getTime() + atof(events[i]->getParam()) * 1000); long endTime = startTime + 1000; speed = 0; while (startTime <= endTime) { cameraPosition->addVelocity(startTime, loopTime, speed); speed += steps; startTime += loopTime; } break; } case idCameraEvent::EVENT_TARGETWAIT : { //targetWaits.Append(i); break; } case idCameraEvent::EVENT_SPEED : { /* // take the average delay between up to the next five segments float adjust = atof(events[i]->getParam()); int index = events[i]->getSegment(); total = 0; count = 0; // get total amount of time over the remainder of the segment for (j = index; j < cameraSpline.numSegments() - 1; j++) { total += cameraSpline.getSegmentTime(j + 1) - cameraSpline.getSegmentTime(j); count++; } // multiply that by the adjustment double newTotal = total * adjust; // what is the difference.. newTotal -= total; totalTime += newTotal / 1000; // per segment difference newTotal /= count; int additive = newTotal; // now propogate that difference out to each segment for (j = index; j < cameraSpline.numSegments(); j++) { cameraSpline.addSegmentTime(j, additive); additive += newTotal; } break; */ } } } for (i = 0; i < waits.Num(); i++) { totalTime += waits[i]; } // on a new target switch, we need to take time to this point ( since last target switch ) // and allocate it across the active target, then reset time to this point long timeSoFar = 0; long total = (long)(totalTime * 1000); for (i = 0; i < targets.Num(); i++) { long t; if (i < targets.Num() - 1) { t = events[targets[i+1]]->getTime(); } else { t = total - timeSoFar; } // t is how much time to use for this target setActiveTargetByName(events[targets[i]]->getParam()); getActiveTarget()->setTime(t); timeSoFar += t; } } void idCameraDef::startCamera(long t) { cameraPosition->clearVelocities(); cameraPosition->start(t); buildCamera(); fov.reset(90, 90, t, 0); //for (int i = 0; i < targetPositions.Num(); i++) { // targetPositions[i]-> //} startTime = t; cameraRunning = true; } void idCameraDef::parse(const char *(*text) ) { const char *token; do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !Q_stricmp (token, "}") ) { break; } if (Q_stricmp(token, "time") == 0) { baseTime = Com_ParseFloat(text); } else if (Q_stricmp(token, "camera_fixed") == 0) { cameraPosition = new idFixedPosition(); cameraPosition->parse(text); } else if (Q_stricmp(token, "camera_interpolated") == 0) { cameraPosition = new idInterpolatedPosition(); cameraPosition->parse(text); } else if (Q_stricmp(token, "camera_spline") == 0) { cameraPosition = new idSplinePosition(); cameraPosition->parse(text); } else if (Q_stricmp(token, "target_fixed") == 0) { idFixedPosition *pos = new idFixedPosition(); pos->parse(text); targetPositions.Append(pos); } else if (Q_stricmp(token, "target_interpolated") == 0) { idInterpolatedPosition *pos = new idInterpolatedPosition(); pos->parse(text); targetPositions.Append(pos); } else if (Q_stricmp(token, "target_spline") == 0) { idSplinePosition *pos = new idSplinePosition(); pos->parse(text); targetPositions.Append(pos); } else if (Q_stricmp(token, "fov") == 0) { fov.parse(text); } else if (Q_stricmp(token, "event") == 0) { idCameraEvent *event = new idCameraEvent(); event->parse(text); addEvent(event); } } while (1); if ( !cameraPosition ) { Com_Printf( "no camera position specified\n" ); // prevent a crash later on cameraPosition = new idFixedPosition(); } Com_UngetToken(); Com_MatchToken( text, "}" ); } bool idCameraDef::load(const char *filename) { char *buf; const char *buf_p; int length = FS_ReadFile( filename, (void **)&buf ); if ( !buf ) { return false; } clear(); Com_BeginParseSession( filename ); buf_p = buf; parse(&buf_p); Com_EndParseSession(); FS_FreeFile( buf ); return true; } void idCameraDef::save(const char *filename) { fileHandle_t file = FS_FOpenFileWrite(filename); if (file) { int i; idStr s = "cameraPathDef { \n"; FS_Write(s.c_str(), s.length(), file); s = va("\ttime %f\n", baseTime); FS_Write(s.c_str(), s.length(), file); cameraPosition->write(file, va("camera_%s",cameraPosition->typeStr())); for (i = 0; i < numTargets(); i++) { targetPositions[i]->write(file, va("target_%s", targetPositions[i]->typeStr())); } for (i = 0; i < events.Num(); i++) { events[i]->write(file, "event"); } fov.write(file, "fov"); s = "}\n"; FS_Write(s.c_str(), s.length(), file); } FS_FCloseFile(file); } int idCameraDef::sortEvents(const void *p1, const void *p2) { idCameraEvent *ev1 = (idCameraEvent*)(p1); idCameraEvent *ev2 = (idCameraEvent*)(p2); if (ev1->getTime() > ev2->getTime()) { return -1; } if (ev1->getTime() < ev2->getTime()) { return 1; } return 0; } void idCameraDef::addEvent(idCameraEvent *event) { events.Append(event); //events.Sort(&sortEvents); } void idCameraDef::addEvent(idCameraEvent::eventType t, const char *param, long time) { addEvent(new idCameraEvent(t, param, time)); buildCamera(); } void idCameraDef::removeEvent(int index) { events.RemoveIndex(index); buildCamera(); } const char *idCameraEvent::eventStr[] = { "NA", "WAIT", "TARGETWAIT", "SPEED", "TARGET", "SNAPTARGET", "FOV", "CMD", "TRIGGER", "STOP", "CAMERA", "FADEOUT", "FADEIN", "FEATHER" }; void idCameraEvent::parse(const char *(*text) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp (token, "}") ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine(text); const char *token = Com_Parse(text); if (Q_stricmp(key.c_str(), "type") == 0) { type = static_cast(atoi(token)); } else if (Q_stricmp(key.c_str(), "param") == 0) { paramStr = token; } else if (Q_stricmp(key.c_str(), "time") == 0) { time = atoi(token); } token = Com_Parse(text); } while (1); if ( !strcmp (token, "}") ) { break; } } while (1); Com_UngetToken(); Com_MatchToken( text, "}" ); } void idCameraEvent::write(fileHandle_t file, const char *name) { idStr s = va("\t%s {\n", name); FS_Write(s.c_str(), s.length(), file); s = va("\t\ttype %d\n", static_cast(type)); FS_Write(s.c_str(), s.length(), file); s = va("\t\tparam \"%s\"\n", paramStr.c_str()); FS_Write(s.c_str(), s.length(), file); s = va("\t\ttime %d\n", time); FS_Write(s.c_str(), s.length(), file); s = "\t}\n"; FS_Write(s.c_str(), s.length(), file); } const char *idCameraPosition::positionStr[] = { "Fixed", "Interpolated", "Spline", }; const idVec3 *idInterpolatedPosition::getPosition(long t) { static idVec3 interpolatedPos; float velocity = getVelocity(t); float timePassed = t - lastTime; lastTime = t; // convert to seconds timePassed /= 1000; float distToTravel = timePassed * velocity; idVec3 temp = startPos; temp -= endPos; float distance = temp.Length(); distSoFar += distToTravel; float percent = (float)(distSoFar) / distance; if (percent > 1.0) { percent = 1.0; } else if (percent < 0.0) { percent = 0.0; } // the following line does a straigt calc on percentage of time // float percent = (float)(startTime + time - t) / time; idVec3 v1 = startPos; idVec3 v2 = endPos; v1 *= (1.0 - percent); v2 *= percent; v1 += v2; interpolatedPos = v1; return &interpolatedPos; } void idCameraFOV::parse(const char *(*text) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp (token, "}") ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine(text); const char *token = Com_Parse(text); if (Q_stricmp(key.c_str(), "fov") == 0) { fov = atof(token); } else if (Q_stricmp(key.c_str(), "startFOV") == 0) { startFOV = atof(token); } else if (Q_stricmp(key.c_str(), "endFOV") == 0) { endFOV = atof(token); } else if (Q_stricmp(key.c_str(), "time") == 0) { time = atoi(token); } token = Com_Parse(text); } while (1); if ( !strcmp (token, "}") ) { break; } } while (1); Com_UngetToken(); Com_MatchToken( text, "}" ); } bool idCameraPosition::parseToken(const char *key, const char *(*text)) { const char *token = Com_Parse(text); if (Q_stricmp(key, "time") == 0) { time = atol(token); return true; } else if (Q_stricmp(key, "type") == 0) { type = static_cast(atoi(token)); return true; } else if (Q_stricmp(key, "velocity") == 0) { long t = atol(token); token = Com_Parse(text); long d = atol(token); token = Com_Parse(text); float s = atof(token); addVelocity(t, d, s); return true; } else if (Q_stricmp(key, "baseVelocity") == 0) { baseVelocity = atof(token); return true; } else if (Q_stricmp(key, "name") == 0) { name = token; return true; } else if (Q_stricmp(key, "time") == 0) { time = atoi(token); return true; } Com_UngetToken(); return false; } void idFixedPosition::parse(const char *(*text) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp (token, "}") ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine(text); const char *token = Com_Parse(text); if (Q_stricmp(key.c_str(), "pos") == 0) { Com_UngetToken(); Com_Parse1DMatrix( text, 3, pos ); } else { Com_UngetToken(); idCameraPosition::parseToken(key.c_str(), text); } token = Com_Parse(text); } while (1); if ( !strcmp (token, "}") ) { break; } } while (1); Com_UngetToken(); Com_MatchToken( text, "}" ); } void idInterpolatedPosition::parse(const char *(*text) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp (token, "}") ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine(text); const char *token = Com_Parse(text); if (Q_stricmp(key.c_str(), "startPos") == 0) { Com_UngetToken(); Com_Parse1DMatrix( text, 3, startPos ); } else if (Q_stricmp(key.c_str(), "endPos") == 0) { Com_UngetToken(); Com_Parse1DMatrix( text, 3, endPos ); } else { Com_UngetToken(); idCameraPosition::parseToken(key.c_str(), text); } token = Com_Parse(text); } while (1); if ( !strcmp (token, "}") ) { break; } } while (1); Com_UngetToken(); Com_MatchToken( text, "}" ); } void idSplinePosition::parse(const char *(*text) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp (token, "}") ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine(text); const char *token = Com_Parse(text); if (Q_stricmp(key.c_str(), "target") == 0) { target.parse(text); } else { Com_UngetToken(); idCameraPosition::parseToken(key.c_str(), text); } token = Com_Parse(text); } while (1); if ( !strcmp (token, "}") ) { break; } } while (1); Com_UngetToken(); Com_MatchToken( text, "}" ); } void idCameraFOV::write(fileHandle_t file, const char *p) { idStr s = va("\t%s {\n", p); FS_Write(s.c_str(), s.length(), file); s = va("\t\tfov %f\n", fov); FS_Write(s.c_str(), s.length(), file); s = va("\t\tstartFOV %f\n", startFOV); FS_Write(s.c_str(), s.length(), file); s = va("\t\tendFOV %f\n", endFOV); FS_Write(s.c_str(), s.length(), file); s = va("\t\ttime %i\n", time); FS_Write(s.c_str(), s.length(), file); s = "\t}\n"; FS_Write(s.c_str(), s.length(), file); } void idCameraPosition::write(fileHandle_t file, const char *p) { idStr s = va("\t\ttime %i\n", time); FS_Write(s.c_str(), s.length(), file); s = va("\t\ttype %i\n", static_cast(type)); FS_Write(s.c_str(), s.length(), file); s = va("\t\tname %s\n", name.c_str()); FS_Write(s.c_str(), s.length(), file); s = va("\t\tbaseVelocity %f\n", baseVelocity); FS_Write(s.c_str(), s.length(), file); for (int i = 0; i < velocities.Num(); i++) { s = va("\t\tvelocity %i %i %f\n", velocities[i]->startTime, velocities[i]->time, velocities[i]->speed); FS_Write(s.c_str(), s.length(), file); } } void idFixedPosition::write(fileHandle_t file, const char *p) { idStr s = va("\t%s {\n", p); FS_Write(s.c_str(), s.length(), file); idCameraPosition::write(file, p); s = va("\t\tpos ( %f %f %f )\n", pos.x, pos.y, pos.z); FS_Write(s.c_str(), s.length(), file); s = "\t}\n"; FS_Write(s.c_str(), s.length(), file); } void idInterpolatedPosition::write(fileHandle_t file, const char *p) { idStr s = va("\t%s {\n", p); FS_Write(s.c_str(), s.length(), file); idCameraPosition::write(file, p); s = va("\t\tstartPos ( %f %f %f )\n", startPos.x, startPos.y, startPos.z); FS_Write(s.c_str(), s.length(), file); s = va("\t\tendPos ( %f %f %f )\n", endPos.x, endPos.y, endPos.z); FS_Write(s.c_str(), s.length(), file); s = "\t}\n"; FS_Write(s.c_str(), s.length(), file); } void idSplinePosition::write(fileHandle_t file, const char *p) { idStr s = va("\t%s {\n", p); FS_Write(s.c_str(), s.length(), file); idCameraPosition::write(file, p); target.write(file, "target"); s = "\t}\n"; FS_Write(s.c_str(), s.length(), file); } void idCameraDef::addTarget(const char *name, idCameraPosition::positionType type) { const char *text = (name == NULL) ? va("target0%d", numTargets()+1) : name; idCameraPosition *pos = newFromType(type); if (pos) { pos->setName(name); targetPositions.Append(pos); activeTarget = numTargets()-1; if (activeTarget == 0) { // first one addEvent(idCameraEvent::EVENT_TARGET, name, 0); } } } const idVec3 *idSplinePosition::getPosition(long t) { static idVec3 interpolatedPos; float velocity = getVelocity(t); float timePassed = t - lastTime; lastTime = t; // convert to seconds timePassed /= 1000; float distToTravel = timePassed * velocity; distSoFar += distToTravel; double tempDistance = target.totalDistance(); double percent = (double)(distSoFar) / tempDistance; double targetDistance = percent * tempDistance; tempDistance = 0; double lastDistance1,lastDistance2; lastDistance1 = lastDistance2 = 0; idVec3 temp; int count = target.numSegments(); int i; for(i = 1; i < count; i++) { temp = *target.getSegmentPoint(i-1); temp -= *target.getSegmentPoint(i); tempDistance += temp.Length(); if (i & 1) { lastDistance1 = tempDistance; } else { lastDistance2 = tempDistance; } if (tempDistance >= targetDistance) { break; } } if ( i >= count - 1) { interpolatedPos = *target.getSegmentPoint(i-1); } else { #if 0 double timeHi = target.getSegmentTime(i + 1); double timeLo = target.getSegmentTime(i - 1); double percent = (timeHi - t) / (timeHi - timeLo); idVec3 v1 = *target.getSegmentPoint(i - 1); idVec3 v2 = *target.getSegmentPoint(i + 1); v2 *= (1.0 - percent); v1 *= percent; v2 += v1; interpolatedPos = v2; #else if (lastDistance1 > lastDistance2) { double d = lastDistance2; lastDistance2 = lastDistance1; lastDistance1 = d; } idVec3 v1 = *target.getSegmentPoint(i - 1); idVec3 v2 = *target.getSegmentPoint(i); double percent = (lastDistance2 - targetDistance) / (lastDistance2 - lastDistance1); v2 *= (1.0 - percent); v1 *= percent; v2 += v1; interpolatedPos = v2; #endif } return &interpolatedPos; } DarkRadiant-2.5.0/libs/splines/splines.h000066400000000000000000000517151321750546400201560ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __SPLINES_H #define __SPLINES_H #define GTKRADIANT #ifdef GTKRADIANT #include "igl.h" #endif #include "util_list.h" #include "util_str.h" #include "math_vector.h" typedef int fileHandle_t; extern void glBox(idVec3 &color, idVec3 &point, float size); extern void glLabeledPoint(idVec3 &color, idVec3 &point, float size, const char *label); static idVec4 blue(0, 0, 1, 1); static idVec4 red(1, 0, 0, 1); class idPointListInterface { public: idPointListInterface() { selectedPoints.Clear(); }; virtual ~idPointListInterface() {}; virtual int numPoints() { return 0; } virtual void addPoint(const float x, const float y, const float z) {} virtual void addPoint(const idVec3 &v) {} virtual void removePoint(int index) {} virtual idVec3 *getPoint(int index) { return NULL; } int selectPointByRay(float ox, float oy, float oz, float dx, float dy, float dz, bool single) { idVec3 origin(ox, oy, oz); idVec3 dir(dx, dy, dz); return selectPointByRay(origin, dir, single); } int selectPointByRay(const idVec3 origin, const idVec3 direction, bool single) { int i, besti, count; float d, bestd; idVec3 temp, temp2; // find the point closest to the ray besti = -1; bestd = 8; count = numPoints(); for (i=0; i < count; i++) { temp = *getPoint(i); temp2 = temp; temp -= origin; d = DotProduct(temp, direction); __VectorMA (origin, d, direction, temp); temp2 -= temp; d = temp2.Length(); if (d <= bestd) { bestd = d; besti = i; } } if (besti >= 0) { selectPoint(besti, single); } return besti; } int isPointSelected(int index) { int count = selectedPoints.Num(); for (int i = 0; i < count; i++) { if (selectedPoints[i] == index) { return i; } } return -1; } int selectPoint(int index, bool single) { if (index >= 0 && index < numPoints()) { if (single) { deselectAll(); } else { if (isPointSelected(index) >= 0) { selectedPoints.Remove(index); } } return selectedPoints.Append(index); } return -1; } void selectAll() { selectedPoints.Clear(); for (int i = 0; i < numPoints(); i++) { selectedPoints.Append(i); } } void deselectAll() { selectedPoints.Clear(); } int numSelectedPoints(); idVec3 *getSelectedPoint(int index) { assert(index >= 0 && index < numSelectedPoints()); return getPoint(selectedPoints[index]); } virtual void updateSelection(float x, float y, float z) { idVec3 move(x, y, z); updateSelection(move); } virtual void updateSelection(const idVec3 &move) { int count = selectedPoints.Num(); for (int i = 0; i < count; i++) { *getPoint(selectedPoints[i]) += move; } } void drawSelection() { int count = selectedPoints.Num(); for (int i = 0; i < count; i++) { glBox(red, *getPoint(selectedPoints[i]), 4); } } protected: idList selectedPoints; }; class idSplineList { public: idSplineList() { clear(); } idSplineList(const char *p) { clear(); name = p; }; ~idSplineList() { clear(); }; void clearControl() { for (int i = 0; i < controlPoints.Num(); i++) { delete controlPoints[i]; } controlPoints.Clear(); } void clearSpline() { for (int i = 0; i < splinePoints.Num(); i++) { delete splinePoints[i]; } splinePoints.Clear(); } void parse(const char *(*text)); void write(fileHandle_t file, const char *name); void clear() { clearControl(); clearSpline(); splineTime.Clear(); selected = NULL; dirty = true; activeSegment = 0; granularity = 0.025f; pathColor.set(1.0f, 0.5f, 0.0f); controlColor.set(0.7f, 0.0f, 1.0f); segmentColor.set(0.0f, 0.0f, 1.0f); activeColor.set(1.0f, 0.0f, 0.0f); } void initPosition(long startTime, long totalTime); const idVec3 *getPosition(long time); void draw(bool editMode); void addToRenderer(); void setSelectedPoint(idVec3 *p); idVec3 *getSelectedPoint() { return selected; } void addPoint(const idVec3 &v) { controlPoints.Append(new idVec3(v)); dirty = true; } void addPoint(float x, float y, float z) { controlPoints.Append(new idVec3(x, y, z)); dirty = true; } void updateSelection(const idVec3 &move); void startEdit() { editMode = true; } void stopEdit() { editMode = false; } void buildSpline(); void setGranularity(float f) { granularity = f; } float getGranularity() { return granularity; } int numPoints() { return controlPoints.Num(); } idVec3 *getPoint(int index) { assert(index >= 0 && index < controlPoints.Num()); return controlPoints[index]; } idVec3 *getSegmentPoint(int index) { assert(index >= 0 && index < splinePoints.Num()); return splinePoints[index]; } void setSegmentTime(int index, int time) { assert(index >= 0 && index < splinePoints.Num()); splineTime[index] = time; } int getSegmentTime(int index) { assert(index >= 0 && index < splinePoints.Num()); return (int)splineTime[index]; } void addSegmentTime(int index, int time) { assert(index >= 0 && index < splinePoints.Num()); splineTime[index] += time; } float totalDistance(); static idVec3 zero; int getActiveSegment() { return activeSegment; } void setActiveSegment(int i) { //assert(i >= 0 && (splinePoints.Num() > 0 && i < splinePoints.Num())); activeSegment = i; } int numSegments() { return splinePoints.Num(); } void setColors(idVec3 &path, idVec3 &segment, idVec3 &control, idVec3 &active) { pathColor = path; segmentColor = segment; controlColor = control; activeColor = active; } const char *getName() { return name.c_str(); } void setName(const char *p) { name = p; } bool validTime() { if (dirty) { buildSpline(); } // gcc doesn't allow static casting away from bools // why? I've no idea... return (bool)(splineTime.Num() > 0 && splineTime.Num() == splinePoints.Num()); } void setTime(long t) { time = t; } void setBaseTime(long t) { baseTime = t; } protected: idStr name; float calcSpline(int step, float tension); idList controlPoints; idList splinePoints; idList splineTime; idVec3 *selected; idVec3 pathColor, segmentColor, controlColor, activeColor; float granularity; bool editMode; bool dirty; int activeSegment; long baseTime; long time; friend class idCamera; }; // time in milliseconds // velocity where 1.0 equal rough walking speed struct idVelocity { idVelocity(long start, long duration, float s) { startTime = start; time = duration; speed = s; } long startTime; long time; float speed; }; // can either be a look at or origin position for a camera // class idCameraPosition : public idPointListInterface { public: virtual void clearVelocities() { for (int i = 0; i < velocities.Num(); i++) { delete velocities[i]; velocities[i] = NULL; } velocities.Clear(); } virtual void clear() { editMode = false; clearVelocities(); } idCameraPosition(const char *p) { name = p; } idCameraPosition() { time = 0; name = "position"; } idCameraPosition(long t) { time = t; } virtual ~idCameraPosition() { clear(); } // this can be done with RTTI syntax but i like the derived classes setting a type // makes serialization a bit easier to see // enum positionType { FIXED = 0x00, INTERPOLATED, SPLINE, POSITION_COUNT }; virtual void start(long t) { startTime = t; } long getTime() { return time; } virtual void setTime(long t) { time = t; } float getBaseVelocity() { return baseVelocity; } float getVelocity(long t) { long check = t - startTime; for (int i = 0; i < velocities.Num(); i++) { if (check >= velocities[i]->startTime && check <= velocities[i]->startTime + velocities[i]->time) { return velocities[i]->speed; } } return baseVelocity; } void addVelocity(long start, long duration, float speed) { velocities.Append(new idVelocity(start, duration, speed)); } virtual const idVec3 *getPosition(long t) { assert(true); return NULL; } virtual void draw(bool editMode) {}; virtual void parse(const char *(*text)) {}; virtual void write(fileHandle_t file, const char *name); virtual bool parseToken(const char *key, const char *(*text)); const char *getName() { return name.c_str(); } void setName(const char *p) { name = p; } virtual void startEdit() { editMode = true; } virtual void stopEdit() { editMode = false; } virtual void draw() {}; const char *typeStr() { return positionStr[static_cast(type)]; } void calcVelocity(float distance) { float secs = (float)time / 1000; baseVelocity = distance / secs; } protected: static const char* positionStr[POSITION_COUNT]; long startTime; long time; idCameraPosition::positionType type; idStr name; bool editMode; idList velocities; float baseVelocity; }; class idFixedPosition : public idCameraPosition { public: void init() { pos.Zero(); type = idCameraPosition::FIXED; } idFixedPosition() : idCameraPosition() { init(); } idFixedPosition(idVec3 p) : idCameraPosition() { init(); pos = p; } virtual void addPoint(const idVec3 &v) { pos = v; } virtual void addPoint(const float x, const float y, const float z) { pos.set(x, y, z); } ~idFixedPosition() { } virtual const idVec3 *getPosition(long t) { return &pos; } void parse(const char *(*text)); void write(fileHandle_t file, const char *name); virtual int numPoints() { return 1; } virtual idVec3 *getPoint(int index) { if (index != 0) { assert(true); }; return &pos; } virtual void draw(bool editMode) { glLabeledPoint(blue, pos, (editMode) ? 5 : 3, "Fixed point"); } protected: idVec3 pos; }; class idInterpolatedPosition : public idCameraPosition { public: void init() { type = idCameraPosition::INTERPOLATED; first = true; startPos.Zero(); endPos.Zero(); } idInterpolatedPosition() : idCameraPosition() { init(); } idInterpolatedPosition(idVec3 start, idVec3 end, long time) : idCameraPosition(time) { init(); startPos = start; endPos = end; } ~idInterpolatedPosition() { } virtual const idVec3 *getPosition(long t); void parse(const char *(*text)); void write(fileHandle_t file, const char *name); virtual int numPoints() { return 2; } virtual idVec3 *getPoint(int index) { assert(index >= 0 && index < 2); if (index == 0) { return &startPos; } return &endPos; } virtual void addPoint(const float x, const float y, const float z) { if (first) { startPos.set(x, y, z); first = false; } else { endPos.set(x, y, z); first = true; } } virtual void addPoint(const idVec3 &v) { if (first) { startPos = v; first = false; } else { endPos = v; first = true; } } virtual void draw(bool editMode) { glLabeledPoint(blue, startPos, (editMode) ? 5 : 3, "Start interpolated"); glLabeledPoint(blue, endPos, (editMode) ? 5 : 3, "End interpolated"); glBegin(GL_LINES); glVertex3fv(startPos); glVertex3fv(endPos); glEnd(); } virtual void start(long t) { idCameraPosition::start(t); lastTime = startTime; distSoFar = 0.0; idVec3 temp = startPos; temp -= endPos; calcVelocity(temp.Length()); } protected: bool first; idVec3 startPos; idVec3 endPos; long lastTime; float distSoFar; }; class idSplinePosition : public idCameraPosition { public: void init() { type = idCameraPosition::SPLINE; } idSplinePosition() : idCameraPosition() { init(); } idSplinePosition(long time) : idCameraPosition(time) { init(); } ~idSplinePosition() { } virtual void start(long t) { idCameraPosition::start(t); target.initPosition(t, time); lastTime = startTime; distSoFar = 0.0; calcVelocity(target.totalDistance()); } //virtual const idVec3 *getPosition(long t) { // return target.getPosition(t); //} virtual const idVec3 *getPosition(long t); //virtual const idVec3 *getPosition(long t) const { void addControlPoint(idVec3 &v) { target.addPoint(v); } void parse(const char *(*text)); void write(fileHandle_t file, const char *name); virtual int numPoints() { return target.numPoints(); } virtual idVec3 *getPoint(int index) { return target.getPoint(index); } virtual void addPoint(const idVec3 &v) { target.addPoint(v); } virtual void addPoint(const float x, const float y, const float z) { target.addPoint(x, y, z); } virtual void draw(bool editMode) { target.draw(editMode); } virtual void updateSelection(const idVec3 &move) { idCameraPosition::updateSelection(move); target.buildSpline(); } protected: idSplineList target; long lastTime; float distSoFar; }; class idCameraFOV { public: idCameraFOV() { time = 0; length = 0; fov = 90; } idCameraFOV(int v) { time = 0; length = 0; fov = v; } idCameraFOV(int s, int e, long t) { startFOV = s; endFOV = e; length = t; } ~idCameraFOV(){} void setFOV(float f) { fov = f; } float getFOV(long t) { if (length) { float percent = (t - startTime) / length; if (percent < 0.0) { percent = 0.0; } else if (percent > 1.0) { percent = 1.0; } float temp = endFOV - startFOV; temp *= percent; fov = startFOV + temp; if (percent == 1.0) { length = 0.0; } } return fov; } void start(long t) { startTime = t; } void reset(float startfov, float endfov, int start, float len) { startFOV = startfov; endFOV = endfov; startTime = start; length = len * 1000; } void parse(const char *(*text)); void write(fileHandle_t file, const char *name); protected: float fov; float startFOV; float endFOV; int startTime; int time; float length; }; class idCameraEvent { public: // parameters enum eventType { EVENT_NA = 0x00, EVENT_WAIT, // EVENT_TARGETWAIT, // EVENT_SPEED, // EVENT_TARGET, // char(name) EVENT_SNAPTARGET, // EVENT_FOV, // int(time), int(targetfov) EVENT_CMD, // EVENT_TRIGGER, // EVENT_STOP, // EVENT_CAMERA, // EVENT_FADEOUT, // int(time) EVENT_FADEIN, // int(time) EVENT_FEATHER, // EVENT_COUNT }; static const char* eventStr[EVENT_COUNT]; idCameraEvent() { paramStr = ""; type = EVENT_NA; time = 0; } idCameraEvent(eventType t, const char *param, long n) { type = t; paramStr = param; time = n; } ~idCameraEvent() {}; eventType getType() { return type; } const char *typeStr() { return eventStr[static_cast(type)]; } const char *getParam() { return paramStr.c_str(); } long getTime() { return time; } void setTime(long n) { time = n; } void parse(const char *(*text)); void write(fileHandle_t file, const char *name); void setTriggered(bool b) { triggered = b; } bool getTriggered() { return triggered; } protected: eventType type; idStr paramStr; long time; bool triggered; }; class idCameraDef { public: void clear() { currentCameraPosition = 0; cameraRunning = false; lastDirection.Zero(); baseTime = 30; activeTarget = 0; name = "camera01"; fov.setFOV(90); int i; for (i = 0; i < targetPositions.Num(); i++) { delete targetPositions[i]; } for (i = 0; i < events.Num(); i++) { delete events[i]; } delete cameraPosition; cameraPosition = NULL; events.Clear(); targetPositions.Clear(); } idCameraPosition *startNewCamera(idCameraPosition::positionType type) { clear(); if (type == idCameraPosition::SPLINE) { cameraPosition = new idSplinePosition(); } else if (type == idCameraPosition::INTERPOLATED) { cameraPosition = new idInterpolatedPosition(); } else { cameraPosition = new idFixedPosition(); } return cameraPosition; } idCameraDef() { cameraPosition = NULL; clear(); } ~idCameraDef() { clear(); } void addEvent(idCameraEvent::eventType t, const char *param, long time); void addEvent(idCameraEvent *event); void removeEvent( int index); static int sortEvents(const void *p1, const void *p2); int numEvents() { return events.Num(); } idCameraEvent *getEvent(int index) { assert(index >= 0 && index < events.Num()); return events[index]; } void parse(const char *(*text)); bool load(const char *filename); void save(const char *filename); void buildCamera(); //idSplineList *getcameraPosition() { // return &cameraPosition; //} static idCameraPosition *newFromType(idCameraPosition::positionType t) { switch (t) { case idCameraPosition::FIXED : return new idFixedPosition(); case idCameraPosition::INTERPOLATED : return new idInterpolatedPosition(); case idCameraPosition::SPLINE : return new idSplinePosition(); default: break; }; return NULL; } void addTarget(const char *name, idCameraPosition::positionType type); idCameraPosition *getActiveTarget() { if (targetPositions.Num() == 0) { addTarget(NULL, idCameraPosition::FIXED); } return targetPositions[activeTarget]; } idCameraPosition *getActiveTarget(int index) { if (targetPositions.Num() == 0) { addTarget(NULL, idCameraPosition::FIXED); return targetPositions[0]; } return targetPositions[index]; } int numTargets() { return targetPositions.Num(); } void setActiveTargetByName(const char *name) { for (int i = 0; i < targetPositions.Num(); i++) { if (Q_stricmp(name, targetPositions[i]->getName()) == 0) { setActiveTarget(i); return; } } } void setActiveTarget(int index) { assert(index >= 0 && index < targetPositions.Num()); activeTarget = index; } void setRunning(bool b) { cameraRunning = b; } void setBaseTime(float f) { baseTime = f; } float getBaseTime() { return baseTime; } float getTotalTime() { return totalTime; } void startCamera(long t); void stopCamera() { cameraRunning = true; } void getActiveSegmentInfo(int segment, idVec3 &origin, idVec3 &direction, float *fv); bool getCameraInfo(long time, idVec3 &origin, idVec3 &direction, float *fv); bool getCameraInfo(long time, float *origin, float *direction, float *fv) { idVec3 org, dir; org[0] = origin[0]; org[1] = origin[1]; org[2] = origin[2]; dir[0] = direction[0]; dir[1] = direction[1]; dir[2] = direction[2]; bool b = getCameraInfo(time, org, dir, fv); origin[0] = org[0]; origin[1] = org[1]; origin[2] = org[2]; direction[0] = dir[0]; direction[1] = dir[1]; direction[2] = dir[2]; return b; } void draw(bool editMode) { // gcc doesn't allow casting away from bools // why? I've no idea... if (cameraPosition) { cameraPosition->draw((bool)((editMode || cameraRunning) && cameraEdit)); int count = targetPositions.Num(); for (int i = 0; i < count; i++) { targetPositions[i]->draw((bool)((editMode || cameraRunning) && i == activeTarget && !cameraEdit)); } } } /* int numSegments() { if (cameraEdit) { return cameraPosition.numSegments(); } return getTargetSpline()->numSegments(); } int getActiveSegment() { if (cameraEdit) { return cameraPosition.getActiveSegment(); } return getTargetSpline()->getActiveSegment(); } void setActiveSegment(int i) { if (cameraEdit) { cameraPosition.setActiveSegment(i); } else { getTargetSpline()->setActiveSegment(i); } } */ int numPoints() { if (cameraEdit) { return cameraPosition->numPoints(); } return getActiveTarget()->numPoints(); } const idVec3 *getPoint(int index) { if (cameraEdit) { return cameraPosition->getPoint(index); } return getActiveTarget()->getPoint(index); } void stopEdit() { editMode = false; if (cameraEdit) { cameraPosition->stopEdit(); } else { getActiveTarget()->stopEdit(); } } void startEdit(bool camera) { cameraEdit = camera; if (camera) { cameraPosition->startEdit(); for (int i = 0; i < targetPositions.Num(); i++) { targetPositions[i]->stopEdit(); } } else { getActiveTarget()->startEdit(); cameraPosition->stopEdit(); } editMode = true; } bool waitEvent(int index); const char *getName() { return name.c_str(); } void setName(const char *p) { name = p; } idCameraPosition *getPositionObj() { if (cameraPosition == NULL) { cameraPosition = new idFixedPosition(); } return cameraPosition; } protected: idStr name; int currentCameraPosition; idVec3 lastDirection; bool cameraRunning; idCameraPosition *cameraPosition; idList targetPositions; idList events; idCameraFOV fov; int activeTarget; float totalTime; float baseTime; long startTime; bool cameraEdit; bool editMode; }; extern bool g_splineMode; extern idCameraDef *g_splineList; #endif DarkRadiant-2.5.0/libs/splines/util_list.h000066400000000000000000000140571321750546400205070ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __UTIL_LIST_H__ #define __UTIL_LIST_H__ #include #include template< class type > class idList { private: int m_num; int m_size; int m_granularity; type *m_list; public: idList( int granularity = 16 ); ~idList(); void Clear( void ); int Num( void ); void SetNum( int num ); void SetGranularity( int granularity ); void Condense( void ); int Size( void ); void Resize( int size ); type operator[]( int index ) const; type &operator[]( int index ); int Append( type const & obj ); int AddUnique( type const & obj ); type *Find( type const & obj, int *index = NULL ); bool RemoveIndex( int index ); bool Remove( type const & obj ); typedef int cmp_t(const void *, const void *); void Sort( cmp_t *compare ); }; /* ================ idList::idList( int ) ================ */ template< class type > inline idList::idList( int granularity ) { assert( granularity > 0 ); m_list = NULL; m_granularity = granularity; Clear(); } /* ================ idList::~idList ================ */ template< class type > inline idList::~idList() { Clear(); } /* ================ idList::Clear ================ */ template< class type > inline void idList::Clear( void ) { if ( m_list ) { delete[] m_list; } m_list = NULL; m_num = 0; m_size = 0; } /* ================ idList::Num ================ */ template< class type > inline int idList::Num( void ) { return m_num; } /* ================ idList::SetNum ================ */ template< class type > inline void idList::SetNum( int num ) { assert( num >= 0 ); if ( num > m_size ) { // resize it up to the closest level of granularity Resize( ( ( num + m_granularity - 1 ) / m_granularity ) * m_granularity ); } m_num = num; } /* ================ idList::SetGranularity ================ */ template< class type > inline void idList::SetGranularity( int granularity ) { int newsize; assert( granularity > 0 ); m_granularity = granularity; if ( m_list ) { // resize it to the closest level of granularity newsize = ( ( m_num + m_granularity - 1 ) / m_granularity ) * m_granularity; if ( newsize != m_size ) { Resize( newsize ); } } } /* ================ idList::Condense Resizes the array to exactly the number of elements it contains ================ */ template< class type > inline void idList::Condense( void ) { if ( m_list ) { if ( m_num ) { Resize( m_num ); } else { Clear(); } } } /* ================ idList::Size ================ */ template< class type > inline int idList::Size( void ) { return m_size; } /* ================ idList::Resize ================ */ template< class type > inline void idList::Resize( int size ) { type *temp; int i; assert( size > 0 ); if ( size <= 0 ) { Clear(); return; } temp = m_list; m_size = size; if ( m_size < m_num ) { m_num = m_size; } m_list = new type[ m_size ]; for( i = 0; i < m_num; i++ ) { m_list[ i ] = temp[ i ]; } if ( temp ) { delete[] temp; } } /* ================ idList::operator[] const ================ */ template< class type > inline type idList::operator[]( int index ) const { assert( index >= 0 ); assert( index < m_num ); return m_list[ index ]; } /* ================ idList::operator[] ================ */ template< class type > inline type &idList::operator[]( int index ) { assert( index >= 0 ); assert( index < m_num ); return m_list[ index ]; } /* ================ idList::Append ================ */ template< class type > inline int idList::Append( type const & obj ) { if ( !m_list ) { Resize( m_granularity ); } if ( m_num == m_size ) { Resize( m_size + m_granularity ); } m_list[ m_num ] = obj; m_num++; return m_num - 1; } /* ================ idList::AddUnique ================ */ template< class type > inline int idList::AddUnique( type const & obj ) { int index; if ( !Find( obj, &index ) ) { index = Append( obj ); } return index; } /* ================ idList::Find ================ */ template< class type > inline type *idList::Find( type const & obj, int *index ) { int i; for( i = 0; i < m_num; i++ ) { if ( m_list[ i ] == obj ) { if ( index ) { *index = i; } return &m_list[ i ]; } } return NULL; } /* ================ idList::RemoveIndex ================ */ template< class type > inline bool idList::RemoveIndex( int index ) { int i; if ( !m_list || !m_num ) { return false; } assert( index >= 0 ); assert( index < m_num ); if ( ( index < 0 ) || ( index >= m_num ) ) { return false; } m_num--; for( i = index; i < m_num; i++ ) { m_list[ i ] = m_list[ i + 1 ]; } return true; } /* ================ idList::Remove ================ */ template< class type > inline bool idList::Remove( type const & obj ) { int index; if ( Find( obj, &index ) ) { return RemoveIndex( index ); } return false; } /* ================ idList::Sort ================ */ template< class type > inline void idList::Sort( cmp_t *compare ) { if ( !m_list ) { return; } qsort( ( void * )m_list, ( size_t )m_num, sizeof( type ), compare ); } #endif /* !__UTIL_LIST_H__ */ DarkRadiant-2.5.0/libs/splines/util_str.cpp000066400000000000000000000262611321750546400206770ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ //need to rewrite this #include "util_str.h" #include #include #include #include #ifdef _WIN32 #pragma warning(disable : 4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data #pragma warning(disable : 4710) // function 'blah' not inlined #endif static const int STR_ALLOC_GRAN = 20; // screwy but intentional #ifdef __APPLE_BUG__ char *idStr::__tolower #else char *idStr::tolower #endif ( char *s1 ) { char *s; s = s1; while( *s ) { *s = ::tolower( *s ); s++; } return s1; } // screwy but intentional #ifdef __APPLE_BUG__ char *idStr::__toupper #else char *idStr::toupper #endif ( char *s1 ) { char *s; s = s1; while( *s ) { *s = ::toupper( *s ); s++; } return s1; } int idStr::icmpn ( const char *s1, const char *s2, int n ) { int c1; int c2; do { c1 = *s1++; c2 = *s2++; if ( !n-- ) { // idStrings are equal until end point return 0; } if ( c1 != c2 ) { if ( c1 >= 'a' && c1 <= 'z' ) { c1 -= ( 'a' - 'A' ); } if ( c2 >= 'a' && c2 <= 'z' ) { c2 -= ( 'a' - 'A' ); } if ( c1 < c2 ) { // strings less than return -1; } else if ( c1 > c2 ) { // strings greater than return 1; } } } while( c1 ); // strings are equal return 0; } int idStr::icmp ( const char *s1, const char *s2 ) { int c1; int c2; do { c1 = *s1++; c2 = *s2++; if ( c1 != c2 ) { if ( c1 >= 'a' && c1 <= 'z' ) { c1 -= ( 'a' - 'A' ); } if ( c2 >= 'a' && c2 <= 'z' ) { c2 -= ( 'a' - 'A' ); } if ( c1 < c2 ) { // strings less than return -1; } else if ( c1 > c2 ) { // strings greater than return 1; } } } while( c1 ); // strings are equal return 0; } int idStr::cmpn ( const char *s1, const char *s2, int n ) { int c1; int c2; do { c1 = *s1++; c2 = *s2++; if ( !n-- ) { // strings are equal until end point return 0; } if ( c1 < c2 ) { // strings less than return -1; } else if ( c1 > c2 ) { // strings greater than return 1; } } while( c1 ); // strings are equal return 0; } int idStr::cmp ( const char *s1, const char *s2 ) { int c1; int c2; do { c1 = *s1++; c2 = *s2++; if ( c1 < c2 ) { // strings less than return -1; } else if ( c1 > c2 ) { // strings greater than return 1; } } while( c1 ); // strings are equal return 0; } /* ============ IsNumeric Checks a string to see if it contains only numerical values. ============ */ bool idStr::isNumeric ( const char *str ) { int len; int i; bool dot; if ( *str == '-' ) { str++; } dot = false; len = strlen( str ); for( i = 0; i < len; i++ ) { if ( !isdigit( str[ i ] ) ) { if ( ( str[ i ] == '.' ) && !dot ) { dot = true; continue; } return false; } } return true; } idStr operator+ ( const idStr& a, const float b ) { char text[ 20 ]; idStr result( a ); sprintf( text, "%f", b ); result.append( text ); return result; } idStr operator+ ( const idStr& a, const int b ) { char text[ 20 ]; idStr result( a ); sprintf( text, "%d", b ); result.append( text ); return result; } idStr operator+ ( const idStr& a, const unsigned b ) { char text[ 20 ]; idStr result( a ); sprintf( text, "%u", b ); result.append( text ); return result; } idStr& idStr::operator+= ( const float a ) { char text[ 20 ]; sprintf( text, "%f", a ); append( text ); return *this; } idStr& idStr::operator+= ( const int a ) { char text[ 20 ]; sprintf( text, "%d", a ); append( text ); return *this; } idStr& idStr::operator+= ( const unsigned a ) { char text[ 20 ]; sprintf( text, "%u", a ); append( text ); return *this; } void idStr::CapLength ( int newlen ) { assert ( m_data ); if ( length() <= newlen ) return; EnsureDataWritable (); m_data->data[newlen] = 0; m_data->len = newlen; } void idStr::EnsureDataWritable ( void ) { assert ( m_data ); strdata *olddata; int len; if ( !m_data->refcount ) return; olddata = m_data; len = length(); m_data = new strdata; EnsureAlloced ( len + 1, false ); strncpy ( m_data->data, olddata->data, len+1 ); m_data->len = len; olddata->DelRef (); } void idStr::EnsureAlloced (int amount, bool keepold) { if ( !m_data ) { m_data = new strdata(); } // Now, let's make sure it's writable EnsureDataWritable (); char *newbuffer; bool wasalloced = ( m_data->alloced != 0 ); if ( amount < m_data->alloced ) { return; } assert ( amount ); if ( amount == 1 ) { m_data->alloced = 1; } else { int newsize, mod; mod = amount % STR_ALLOC_GRAN; if ( !mod ) { newsize = amount; } else { newsize = amount + STR_ALLOC_GRAN - mod; } m_data->alloced = newsize; } newbuffer = new char[m_data->alloced]; if ( wasalloced && keepold ) { strcpy ( newbuffer, m_data->data ); } if ( m_data->data ) { delete [] m_data->data; } m_data->data = newbuffer; } void idStr::BackSlashesToSlashes ( void ) { int i; EnsureDataWritable (); for ( i=0; i < m_data->len; i++ ) { if ( m_data->data[i] == '\\' ) m_data->data[i] = '/'; } } void idStr::snprintf ( char *dst, int size, const char *fmt, ... ) { char buffer[0x10000]; int len; va_list argptr; va_start (argptr,fmt); len = vsprintf (buffer,fmt,argptr); va_end (argptr); assert ( len < size ); strncpy (dst, buffer, size-1); } #ifdef _WIN32 #pragma warning(disable : 4189) // local variable is initialized but not referenced #endif /* ================= TestStringClass This is a fairly rigorous test of the idStr class's functionality. Because of the fairly global and subtle ramifications of a bug occuring in this class, it should be run after any changes to the class. Add more tests as functionality is changed. Tests should include any possible bounds violation and NULL data tests. ================= */ void TestStringClass ( void ) { char ch; // ch == ? idStr *t; // t == ? idStr a; // a.len == 0, a.data == "\0" idStr b; // b.len == 0, b.data == "\0" idStr c( "test" ); // c.len == 4, c.data == "test\0" idStr d( c ); // d.len == 4, d.data == "test\0" idStr e( reinterpret_cast(NULL) ); // e.len == 0, e.data == "\0" ASSERT! int i; // i == ? i = a.length(); // i == 0 i = c.length(); // i == 4 const char *s1 = a.c_str(); // s1 == "\0" const char *s2 = c.c_str(); // s2 == "test\0" t = new idStr(); // t->len == 0, t->data == "\0" delete t; // t == ? b = "test"; // b.len == 4, b.data == "test\0" t = new idStr( "test" ); // t->len == 4, t->data == "test\0" delete t; // t == ? a = c; // a.len == 4, a.data == "test\0" // a = ""; a = NULL; // a.len == 0, a.data == "\0" ASSERT! a = c + d; // a.len == 8, a.data == "testtest\0" a = c + "wow"; // a.len == 7, a.data == "testwow\0" a = c + reinterpret_cast(NULL); // a.len == 4, a.data == "test\0" ASSERT! a = "this" + d; // a.len == 8, a.data == "thistest\0" a = reinterpret_cast(NULL) + d; // a.len == 4, a.data == "test\0" ASSERT! a += c; // a.len == 8, a.data == "testtest\0" a += "wow"; // a.len == 11, a.data == "testtestwow\0" a += reinterpret_cast(NULL); // a.len == 11, a.data == "testtestwow\0" ASSERT! a = "test"; // a.len == 4, a.data == "test\0" ch = a[ 0 ]; // ch == 't' ch = a[ -1 ]; // ch == 0 ASSERT! ch = a[ 1000 ]; // ch == 0 ASSERT! ch = a[ 0 ]; // ch == 't' ch = a[ 1 ]; // ch == 'e' ch = a[ 2 ]; // ch == 's' ch = a[ 3 ]; // ch == 't' ch = a[ 4 ]; // ch == '\0' ASSERT! ch = a[ 5 ]; // ch == '\0' ASSERT! a[ 1 ] = 'b'; // a.len == 4, a.data == "tbst\0" a[ -1 ] = 'b'; // a.len == 4, a.data == "tbst\0" ASSERT! a[ 0 ] = '0'; // a.len == 4, a.data == "0bst\0" a[ 1 ] = '1'; // a.len == 4, a.data == "01st\0" a[ 2 ] = '2'; // a.len == 4, a.data == "012t\0" a[ 3 ] = '3'; // a.len == 4, a.data == "0123\0" a[ 4 ] = '4'; // a.len == 4, a.data == "0123\0" ASSERT! a[ 5 ] = '5'; // a.len == 4, a.data == "0123\0" ASSERT! a[ 7 ] = '7'; // a.len == 4, a.data == "0123\0" ASSERT! a = "test"; // a.len == 4, a.data == "test\0" b = "no"; // b.len == 2, b.data == "no\0" i = ( a == b ); // i == 0 i = ( a == c ); // i == 1 i = ( a == "blow" ); // i == 0 i = ( a == "test" ); // i == 1 i = ( a == NULL ); // i == 0 ASSERT! i = ( "test" == b ); // i == 0 i = ( "test" == a ); // i == 1 i = ( NULL == a ); // i == 0 ASSERT! i = ( a != b ); // i == 1 i = ( a != c ); // i == 0 i = ( a != "blow" ); // i == 1 i = ( a != "test" ); // i == 0 i = ( a != NULL ); // i == 1 ASSERT! i = ( "test" != b ); // i == 1 i = ( "test" != a ); // i == 0 i = ( NULL != a ); // i == 1 ASSERT! a = "test"; // a.data == "test" b = a; // b.data == "test" a = "not"; // a.data == "not", b.data == "test" a = b; // a.data == b.data == "test" a += b; // a.data == "testtest", b.data = "test" a = b; a[1] = '1'; // a.data = "t1st", b.data = "test" } #ifdef _WIN32 #pragma warning(default : 4189) // local variable is initialized but not referenced #pragma warning(disable : 4514) // unreferenced inline function has been removed #endif DarkRadiant-2.5.0/libs/splines/util_str.h000066400000000000000000000327121321750546400203420ustar00rootroot00000000000000/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ //need to rewrite this #ifndef __UTIL_STR_H__ #define __UTIL_STR_H__ #include #include #include #ifdef _WIN32 #pragma warning(disable : 4710) // function 'blah' not inlined #endif void TestStringClass (); class strdata { public: strdata () : len( 0 ), refcount ( 0 ), data ( NULL ), alloced ( 0 ) {} ~strdata () { if ( data ) delete [] data; } void AddRef () { refcount++; } bool DelRef () // True if killed { refcount--; if ( refcount < 0 ) { delete this; return true; } return false; } int len; int refcount; char *data; int alloced; }; class idStr { protected: strdata *m_data; void EnsureAlloced ( int, bool keepold = true ); void EnsureDataWritable (); public: ~idStr(); idStr(); idStr( const char *text ); idStr( const idStr& string ); idStr( const idStr string, int start, int end ); idStr( const char ch ); idStr( const int num ); idStr( const float num ); idStr( const unsigned num ); int length( void ) const; int allocated( void ) const; const char * c_str( void ) const; void append( const char *text ); void append( const idStr& text ); char operator[]( int index ) const; char& operator[]( int index ); void operator=( const idStr& text ); void operator=( const char *text ); friend idStr operator+( const idStr& a, const idStr& b ); friend idStr operator+( const idStr& a, const char *b ); friend idStr operator+( const char *a, const idStr& b ); friend idStr operator+( const idStr& a, const float b ); friend idStr operator+( const idStr& a, const int b ); friend idStr operator+( const idStr& a, const unsigned b ); friend idStr operator+( const idStr& a, const bool b ); friend idStr operator+( const idStr& a, const char b ); idStr& operator+=( const idStr& a ); idStr& operator+=( const char *a ); idStr& operator+=( const float a ); idStr& operator+=( const char a ); idStr& operator+=( const int a ); idStr& operator+=( const unsigned a ); idStr& operator+=( const bool a ); friend bool operator==( const idStr& a, const idStr& b ); friend bool operator==( const idStr& a, const char *b ); friend bool operator==( const char *a, const idStr& b ); friend bool operator!=( const idStr& a, const idStr& b ); friend bool operator!=( const idStr& a, const char *b ); friend bool operator!=( const char *a, const idStr& b ); operator const char * () const; operator const char * (); int icmpn( const char *text, int n ) const; int icmpn( const idStr& text, int n ) const; int icmp( const char *text ) const; int icmp( const idStr& text ) const; int cmpn( const char *text, int n ) const; int cmpn( const idStr& text, int n ) const; int cmp( const char *text ) const; int cmp( const idStr& text ) const; void tolower( void ); void toupper( void ); static char *tolower( char *s1 ); static char *toupper( char *s1 ); static int icmpn( const char *s1, const char *s2, int n ); static int icmp( const char *s1, const char *s2 ); static int cmpn( const char *s1, const char *s2, int n ); static int cmp( const char *s1, const char *s2 ); static void snprintf ( char *dst, int size, const char *fmt, ... ); static bool isNumeric( const char *str ); bool isNumeric( void ) const; void CapLength ( int ); void BackSlashesToSlashes (); }; inline idStr::~idStr() { if ( m_data ) { m_data->DelRef (); m_data = NULL; } } inline idStr::idStr() : m_data ( NULL ) { EnsureAlloced ( 1 ); m_data->data[ 0 ] = 0; } inline idStr::idStr ( const char *text ) : m_data ( NULL ) { int len; assert( text ); if ( text ) { len = strlen( text ); EnsureAlloced ( len + 1 ); strcpy( m_data->data, text ); m_data->len = len; } else { EnsureAlloced ( 1 ); m_data->data[ 0 ] = 0; m_data->len = 0; } } inline idStr::idStr ( const idStr& text ) : m_data ( NULL ) { m_data = text.m_data; m_data->AddRef (); } inline idStr::idStr ( const idStr text, int start, int end ) : m_data ( NULL ) { int i; int len; if ( end > text.length() ) { end = text.length(); } if ( start > text.length() ) { start = text.length(); } len = end - start; if ( len < 0 ) { len = 0; } EnsureAlloced ( len + 1 ); for( i = 0; i < len; i++ ) { m_data->data[ i ] = text[ start + i ]; } m_data->data[ len ] = 0; m_data->len = len; } inline idStr::idStr ( const char ch ) : m_data ( NULL ) { EnsureAlloced ( 2 ); m_data->data[ 0 ] = ch; m_data->data[ 1 ] = 0; m_data->len = 1; } inline idStr::idStr ( const float num ) : m_data ( NULL ) { char text[ 32 ]; int len; sprintf( text, "%.3f", num ); len = strlen( text ); EnsureAlloced( len + 1 ); strcpy( m_data->data, text ); m_data->len = len; } inline idStr::idStr ( const int num ) : m_data ( NULL ) { char text[ 32 ]; int len; sprintf( text, "%d", num ); len = strlen( text ); EnsureAlloced( len + 1 ); strcpy( m_data->data, text ); m_data->len = len; } inline idStr::idStr ( const unsigned num ) : m_data ( NULL ) { char text[ 32 ]; int len; sprintf( text, "%u", num ); len = strlen( text ); EnsureAlloced( len + 1 ); strcpy( m_data->data, text ); m_data->len = len; } inline int idStr::length( void ) const { return ( m_data != NULL ) ? m_data->len : 0; } inline int idStr::allocated( void ) const { return ( m_data != NULL ) ? m_data->alloced + sizeof( *m_data ) : 0; } inline const char *idStr::c_str( void ) const { assert( m_data ); return m_data->data; } inline void idStr::append ( const char *text ) { int len; assert( text ); if ( text ) { len = length() + strlen( text ); EnsureAlloced( len + 1 ); strcat( m_data->data, text ); m_data->len = len; } } inline void idStr::append ( const idStr& text ) { int len; len = length() + text.length(); EnsureAlloced ( len + 1 ); strcat ( m_data->data, text.c_str () ); m_data->len = len; } inline char idStr::operator[]( int index ) const { assert ( m_data ); if ( !m_data ) return 0; // don't include the '/0' in the test, because technically, it's out of bounds assert( ( index >= 0 ) && ( index < m_data->len ) ); // In release mode, give them a null character // don't include the '/0' in the test, because technically, it's out of bounds if ( ( index < 0 ) || ( index >= m_data->len ) ) { return 0; } return m_data->data[ index ]; } inline char& idStr::operator[] ( int index ) { // Used for result for invalid indices static char dummy = 0; assert ( m_data ); // We don't know if they'll write to it or not // if it's not a const object EnsureDataWritable (); if ( !m_data ) return dummy; // don't include the '/0' in the test, because technically, it's out of bounds assert( ( index >= 0 ) && ( index < m_data->len ) ); // In release mode, let them change a safe variable // don't include the '/0' in the test, because technically, it's out of bounds if ( ( index < 0 ) || ( index >= m_data->len ) ) { return dummy; } return m_data->data[ index ]; } inline void idStr::operator= ( const idStr& text ) { // adding the reference before deleting our current reference prevents // us from deleting our string if we are copying from ourself text.m_data->AddRef(); m_data->DelRef(); m_data = text.m_data; } inline void idStr::operator= ( const char *text ) { int len; assert( text ); if ( !text ) { // safe behaviour if NULL EnsureAlloced ( 1, false ); m_data->data[0] = 0; m_data->len = 0; return; } if ( !m_data ) { len = strlen ( text ); EnsureAlloced( len + 1, false ); strcpy ( m_data->data, text ); m_data->len = len; return; } if ( text == m_data->data ) return; // Copying same thing. Punt. // If we alias and I don't do this, I could corrupt other strings... This // will get called with EnsureAlloced anyway EnsureDataWritable (); // Now we need to check if we're aliasing.. if ( text >= m_data->data && text <= m_data->data + m_data->len ) { // Great, we're aliasing. We're copying from inside ourselves. // This means that I don't have to ensure that anything is alloced, // though I'll assert just in case. int diff = text - m_data->data; int i; assert ( strlen ( text ) < (unsigned) m_data->len ); for ( i = 0; text[i]; i++ ) { m_data->data[i] = text[i]; } m_data->data[i] = 0; m_data->len -= diff; return; } len = strlen( text ); EnsureAlloced ( len + 1, false ); strcpy( m_data->data, text ); m_data->len = len; } inline idStr operator+ ( const idStr& a, const idStr& b ) { idStr result( a ); result.append( b ); return result; } inline idStr operator+ ( const idStr& a, const char *b ) { idStr result( a ); result.append( b ); return result; } inline idStr operator+ ( const char *a, const idStr& b ) { idStr result( a ); result.append( b ); return result; } inline idStr operator+ ( const idStr& a, const bool b ) { idStr result( a ); result.append( b ? "true" : "false" ); return result; } inline idStr operator+ ( const idStr& a, const char b ) { char text[ 2 ]; text[ 0 ] = b; text[ 1 ] = 0; return a + text; } inline idStr& idStr::operator+= ( const idStr& a ) { append( a ); return *this; } inline idStr& idStr::operator+= ( const char *a ) { append( a ); return *this; } inline idStr& idStr::operator+= ( const char a ) { char text[ 2 ]; text[ 0 ] = a; text[ 1 ] = 0; append( text ); return *this; } inline idStr& idStr::operator+= ( const bool a ) { append( a ? "true" : "false" ); return *this; } inline bool operator== ( const idStr& a, const idStr& b ) { return ( !strcmp( a.c_str(), b.c_str() ) ); } inline bool operator== ( const idStr& a, const char *b ) { assert( b ); if ( !b ) { return false; } return ( !strcmp( a.c_str(), b ) ); } inline bool operator== ( const char *a, const idStr& b ) { assert( a ); if ( !a ) { return false; } return ( !strcmp( a, b.c_str() ) ); } inline bool operator!= ( const idStr& a, const idStr& b ) { return !( a == b ); } inline bool operator!= ( const idStr& a, const char *b ) { return !( a == b ); } inline bool operator!= ( const char *a, const idStr& b ) { return !( a == b ); } inline int idStr::icmpn ( const char *text, int n ) const { assert( m_data ); assert( text ); return idStr::icmpn( m_data->data, text, n ); } inline int idStr::icmpn ( const idStr& text, int n ) const { assert( m_data ); assert( text.m_data ); return idStr::icmpn( m_data->data, text.m_data->data, n ); } inline int idStr::icmp ( const char *text ) const { assert( m_data ); assert( text ); return idStr::icmp( m_data->data, text ); } inline int idStr::icmp ( const idStr& text ) const { assert( c_str () ); assert( text.c_str () ); return idStr::icmp( c_str () , text.c_str () ); } inline int idStr::cmp ( const char *text ) const { assert( m_data ); assert( text ); return idStr::cmp( m_data->data, text ); } inline int idStr::cmp ( const idStr& text ) const { assert( c_str () ); assert( text.c_str () ); return idStr::cmp( c_str () , text.c_str () ); } inline int idStr::cmpn ( const char *text, int n ) const { assert( c_str () ); assert( text ); return idStr::cmpn( c_str () , text, n ); } inline int idStr::cmpn ( const idStr& text, int n ) const { assert( c_str () ); assert( text.c_str () ); return idStr::cmpn( c_str () , text.c_str () , n ); } inline void idStr::tolower ( void ) { assert( m_data ); EnsureDataWritable (); idStr::tolower( m_data->data ); } inline void idStr::toupper ( void ) { assert( m_data ); EnsureDataWritable (); idStr::toupper( m_data->data ); } inline bool idStr::isNumeric ( void ) const { assert( m_data ); return idStr::isNumeric( m_data->data ); } inline idStr::operator const char *() { return c_str(); } inline idStr::operator const char * ( void ) const { return c_str (); } #endif DarkRadiant-2.5.0/libs/stream/000077500000000000000000000000001321750546400161355ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/stream/BinaryToTextInputStream.h000066400000000000000000000031771321750546400231060ustar00rootroot00000000000000#pragma once #include "itextstream.h" namespace stream { /// \brief A single-byte-reader wrapper around an InputStream. /// Optimised for reading one byte at a time. /// Uses a buffer to reduce the number of times the wrapped stream must be read. template class SingleByteInputStream { private: typedef typename InputStreamType::byte_type byte_type; static const std::size_t SIZE = 1024; InputStreamType& _inputStream; byte_type _buffer[SIZE]; byte_type* _cur; byte_type* _end; public: SingleByteInputStream(InputStreamType& inputStream) : _inputStream(inputStream), _cur(_buffer + SIZE), _end(_cur) {} bool readByte(byte_type& b) { if (_cur == _end) { if (_end != _buffer + SIZE) { return false; } _end = _buffer + _inputStream.read(_buffer, SIZE); _cur = _buffer; if (_end == _buffer) { return false; } } b = *_cur++; return true; } }; /// \brief A binary-to-text wrapper around an InputStream. /// Converts CRLF or LFCR line-endings to LF line-endings. template class BinaryToTextInputStream : public TextInputStream { private: SingleByteInputStream _inputStream; public: BinaryToTextInputStream(BinaryInputStreamType& inputStream) : _inputStream(inputStream) {} std::size_t read(char* buffer, std::size_t length) override { char* p = buffer; for (;;) { if (length != 0 && _inputStream.readByte(*reinterpret_cast(p))) { if (*p != '\r') { ++p; --length; } } else { return p - buffer; } } } }; } DarkRadiant-2.5.0/libs/stream/BufferInputStream.h000066400000000000000000000034671321750546400217250ustar00rootroot00000000000000#pragma once #include "itextstream.h" #include namespace stream { class BufferInputStream : public TextInputStream { private: const char* _begin; const char* _read; const char* _end; public: BufferInputStream(const char* buffer, std::size_t length) : _begin(buffer), _read(buffer), _end(buffer + length) {} std::size_t read(char* buffer, std::size_t length) override { std::size_t count = std::min(std::size_t(_end - _read), length); const char* end = _read + count; while (_read != end) { *buffer++ = *_read++; } return count; } // greebo: Override default std::streambuf::seekoff() method to provide buffer positioning capabilities virtual std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override { if (way == std::ios_base::beg) { const char* newPos = _begin + off; // Force streambuf underflow setg(_buffer, _buffer, _buffer); if (newPos > _end || newPos < _begin) { return std::streampos(-1); // error } _read = newPos; } else if (way == std::ios_base::cur) { const char* newPos = _read + off; if (newPos > _end || newPos < _begin) { return std::streampos(-1); // error } else { // success, check if we need to invalidate our controlled input sequence if (gptr() + off > egptr() || gptr() + off < eback()) { setg(_buffer, _buffer, _buffer); } } _read = newPos; } else if (way == std::ios_base::end) { const char* newPos = _end + off; // Force streambuf underflow setg(_buffer, _buffer, _buffer); if (newPos > _end || newPos < _begin) { return std::streampos(-1); // error } _read = newPos; } return std::streampos(_read - _begin); } }; }DarkRadiant-2.5.0/libs/stream/FileInputStream.h000066400000000000000000000040441321750546400213630ustar00rootroot00000000000000#pragma once #include "idatastream.h" #include #include namespace stream { namespace detail { inline int whence_for_seekdir(SeekableStream::seekdir direction) { switch (direction) { case SeekableStream::cur: return SEEK_CUR; case SeekableStream::end: return SEEK_END; default: break; } return SEEK_SET; } } /// \brief A wrapper around a file input stream opened for reading in binary mode. Similar to std::ifstream. /// /// - Maintains a valid file handle associated with a name passed to the constructor. /// - Implements SeekableInputStream. class FileInputStream : public SeekableInputStream { private: std::FILE* _file; public: FileInputStream(const std::string& name) : _file(!name.empty() ? fopen(name.c_str(), "rb") : nullptr) {} ~FileInputStream() { if (!failed()) { fclose(_file); } } bool failed() const { return _file == nullptr; } size_type read(byte_type* buffer, size_type length) override { return fread(buffer, 1, length, _file); } size_type seek(size_type position) override { return fseek(_file, static_cast(position), SEEK_SET); } size_type seek(offset_type offset, seekdir direction) override { return fseek(_file, offset, detail::whence_for_seekdir(direction)); } size_type tell() const override { return ftell(_file); } std::FILE* file() { return _file; } }; /// \brief A wrapper around a FileInputStream limiting access. /// /// - Maintains an input stream. /// - Provides input starting at an offset in the file for a limited range. class SubFileInputStream : public InputStream { private: FileInputStream& _istream; size_type _remaining; public: typedef FileInputStream::position_type position_type; SubFileInputStream(FileInputStream& istream, position_type offset, size_type size) : _istream(istream), _remaining(size) { _istream.seek(offset); } size_type read(byte_type* buffer, size_type length) override { size_type result = _istream.read(buffer, std::min(length, _remaining)); _remaining -= result; return result; } }; } DarkRadiant-2.5.0/libs/stream/PointerInputStream.h000066400000000000000000000012351321750546400221230ustar00rootroot00000000000000#pragma once #include "idatastream.h" namespace stream { /** * A class implementing the InputStream interface around a simple byte pointer. * No bounds or validity checking is performed on the given pointer. */ class PointerInputStream : public InputStream { private: const byte* _curPos; public: PointerInputStream(const byte* pointer) : _curPos(pointer) {} std::size_t read(byte* buffer, std::size_t length) override { const byte* end = _curPos + length; while (_curPos != end) { *buffer++ = *_curPos++; } return length; } void seek(std::size_t offset) { _curPos += offset; } const byte* get() { return _curPos; } }; } DarkRadiant-2.5.0/libs/stream/ScopedArchiveBuffer.h000066400000000000000000000013471321750546400221640ustar00rootroot00000000000000#pragma once #include "idatastream.h" #include "iarchive.h" #include namespace archive { /** * Scoped class reading all the data from the attached * ArchiveFile into a single memory chunk. Clients usually * refer to the buffer variable to access the data. */ class ScopedArchiveBuffer { private: // unique_ptr has a specialisation for arrays std::unique_ptr data; public: InputStream::byte_type* const buffer; // immutable pointer for convenience purposes std::size_t length; ScopedArchiveBuffer(ArchiveFile& file) : data(new InputStream::byte_type[file.size() + 1]), buffer(data.get()) { length = file.getInputStream().read(data.get(), file.size()); data[file.size()] = 0; } }; } DarkRadiant-2.5.0/libs/stream/TextFileInputStream.h000066400000000000000000000034051321750546400222300ustar00rootroot00000000000000#pragma once #include "itextstream.h" #include // A wrapper around a file input stream opened for reading in text mode. Similar to std::ifstream. class TextFileInputStream : public TextInputStream { private: FILE* _file; public: TextFileInputStream(const std::string& name) : _file(!name.empty() ? fopen(name.c_str(), "rt") : NULL) {} virtual ~TextFileInputStream() { if (!failed()) { fclose(_file); } } bool failed() const { return _file == 0; } std::size_t read(char* buffer, std::size_t length) override { return fread(buffer, 1, length, _file); } // greebo: Override default std::streambuf::seekoff() method to provide buffer positioning capabilities virtual std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override { if (way == std::ios_base::beg) { // Invalidate the buffer of our base class to force an underflow setg(_buffer, _buffer, _buffer); if (fseek(_file, static_cast(off), SEEK_SET)) { return std::streampos(-1); // error } } else if (way == std::ios_base::cur) { if (fseek(_file, static_cast(off), SEEK_CUR)) { return std::streampos(-1); // error } else { // success, check if we need to invalidate our controlled input sequence if (gptr() + off > egptr() || gptr() + off < eback()) { setg(_buffer, _buffer, _buffer); } } } else if (way == std::ios_base::end) { // Invalidate the buffer of our base class to force an underflow setg(_buffer, _buffer, _buffer); if (fseek(_file, static_cast(off), SEEK_END)) { return std::streampos(-1); // error } } return std::streampos(ftell(_file)); } }; DarkRadiant-2.5.0/libs/stream/utils.h000066400000000000000000000035471321750546400174570ustar00rootroot00000000000000#pragma once // Fixed width integer types are defined in the C++11 header #include #include "idatastream.h" #include #include namespace stream { /** * greebo: Writes the given number value to the given output stream in Big Endian byte order, * regardless of the calling platform's endianness. */ template void writeBigEndian(std::ostream& stream, ValueType value) { ValueType output = value; #ifndef __BIG_ENDIAN__ std::reverse(reinterpret_cast(&output), reinterpret_cast(&output) + sizeof(ValueType)); #endif stream.write(reinterpret_cast(&output), sizeof(ValueType)); } /** * greebo: Writes the given number value to the given output stream in Little Endian byte order, * regardless of the calling platform's endianness. */ template void writeLittleEndian(std::ostream& stream, ValueType value) { ValueType output = value; #ifdef __BIG_ENDIAN__ std::reverse(reinterpret_cast(&output), reinterpret_cast(&output) + sizeof(ValueType)); #endif stream.write(reinterpret_cast(&output), sizeof(ValueType)); } /** * greebo: Read an integer type stored in little endian format * from the given stream and returns its value. */ template inline ValueType readLittleEndian(InputStream& stream) { ValueType value; stream.read(reinterpret_cast(&value), sizeof(ValueType)); #ifdef __BIG_ENDIAN__ std::reverse(reinterpret_cast(&value), reinterpret_cast(&value) + sizeof(ValueType)); #endif return value; } inline void readByte(InputStream& stream, InputStream::byte_type& value) { stream.read(&value, 1); } inline InputStream::byte_type readByte(InputStream& stream) { InputStream::byte_type value; stream.read(&value, 1); return value; } } DarkRadiant-2.5.0/libs/string/000077500000000000000000000000001321750546400161505ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/string/case_conv.h000066400000000000000000000023201321750546400202560ustar00rootroot00000000000000#pragma once #include #include #include namespace string { /** * Converts the given input string to lowercase, using the * C-function tolower(). The string is modified in-place. */ inline void to_lower(std::string& input) { std::transform(input.begin(), input.end(), input.begin(), ::tolower); } /** * Converts the given input string to lowercase, using the * C-function tolower(), and returns a copy of the result. */ inline std::string to_lower_copy(const std::string& input) { std::string output; output.resize(input.size()); std::transform(input.begin(), input.end(), output.begin(), ::tolower); return output; } /** * Converts the given input string to uppercase, using the * C-function toupper(). The string is modified in-place. */ inline void to_upper(std::string& input) { std::transform(input.begin(), input.end(), input.begin(), ::toupper); } /** * Converts the given input string to uppercase, using the * C-function tolower(), and returns a copy of the result. */ inline std::string to_upper_copy(const std::string& input) { std::string output; output.resize(input.size()); std::transform(input.begin(), input.end(), output.begin(), ::toupper); return output; } } DarkRadiant-2.5.0/libs/string/convert.h000066400000000000000000000140061321750546400200020ustar00rootroot00000000000000#pragma once #include "math/Vector3.h" #include "math/Vector4.h" #include namespace string { // Converts a string to a different type, with a fallback value // A couple of template specialisations are defined below. template T convert(const Src& str, T defaultVal = T()); // Template specialisation to convert std::string => float template<> inline float convert(const std::string& str, float defaultVal) { try { return std::stof(str); } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to convert std::string => double template<> inline double convert(const std::string& str, double defaultVal) { try { return std::stod(str); } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to convert std::string => int template<> inline int convert(const std::string& str, int defaultVal) { try { return std::stoi(str); } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to convert std::string => unsigned int template<> inline unsigned int convert(const std::string& str, unsigned int defaultVal) { try { return static_cast(std::stoul(str)); } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to convert std::string => long template<> inline long convert(const std::string& str, long defaultVal) { try { return std::stol(str); } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to convert std::string => long long template<> inline long long convert(const std::string& str, long long defaultVal) { try { return std::stoll(str); } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to convert std::string => unsigned long template<> inline unsigned long convert(const std::string& str, unsigned long defaultVal) { try { return std::stoul(str); } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to convert std::string => unsigned long long template<> inline unsigned long long convert(const std::string& str, unsigned long long defaultVal) { try { return std::stoull(str); } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to convert std::string => bool // Returns the default value if the string is empty, everything else except "0" returns true template<> inline bool convert(const std::string& str, bool defaultVal) { return str.empty() ? defaultVal : (str != "0"); } // Template specialisation to extract a Vector3 from the given string template<> inline Vector3 convert(const std::string& str, Vector3 defaultVal) { try { Vector3 vec; std::istringstream stream(str); stream >> std::skipws >> vec.x() >> vec.y() >> vec.z(); if (stream.fail()) throw std::invalid_argument("Failed to parse Vector3"); return vec; } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to extract a Vector4 from the given string template<> inline Vector4 convert(const std::string& str, Vector4 defaultVal) { try { Vector4 vec; std::istringstream stream(str); stream >> std::skipws >> vec.x() >> vec.y() >> vec.z(); if (stream.bad()) throw std::invalid_argument("Failed to parse Vector4"); return vec; } catch (const std::logic_error&) // logic_error is base of invalid_argument out_of_range exceptions { return defaultVal; } } // Template specialisation to convert std::string => std::string, returning the input value template<> inline std::string convert(const std::string& str, std::string defaultVal) { return str; } #ifdef SPECIALISE_STR_TO_FLOAT /** * \brief * Convert a string to a float. * * If the SPECIALISE_STR_TO_FLOAT macro is defined, this will use the atof() C * function instead of convert() which may provide a speed benefit. * * \internal * This is a separate function rather than an actual specialisation of * convert<>() for two reasons: (1) I could not get the specialisation to work * (i.e. be chosen by the compiler), and (2) since when using atof() there is no * reliable way to use the fallback value, the default value argument would be * ignored and hence the user-visible function behaviour would change in the * specialised version, which is a bad thing. */ template double to_float(const Src& str) { return std::atof(str.c_str()); } #else template float to_float(const Src& src) { return convert(src, 0.0f); } #endif // Convert the given type to a std::string template inline std::string to_string(const Src& value) { return std::to_string(value); } // Specialisation for Vector3 template<> inline std::string to_string(const Vector3& value) { std::stringstream str; str << value; return str.str(); } // Specialisation for std::string template<> inline std::string to_string(const std::string& value) { return value; } // Special overload for converting const char* to string (this might actually // happen in other template functions calling to_string with T = const char* inline std::string to_string(const char* value) { return value; } } DarkRadiant-2.5.0/libs/string/join.h000066400000000000000000000010501321750546400172540ustar00rootroot00000000000000#pragma once #include namespace string { /** * Joins all the parts of the given container into a new string, separating * the parts by the given separator string. */ template inline std::string join(const ContainerType& parts, const std::string& separator) { std::string result; if (parts.empty()) return result; typename ContainerType::const_iterator part = parts.begin(); result.append(*part++); while (part != parts.end()) { result.append(separator); result.append(*part++); } return result; } } DarkRadiant-2.5.0/libs/string/predicate.h000066400000000000000000000133121321750546400202610ustar00rootroot00000000000000#pragma once #include #include namespace string { namespace detail { inline bool isEqual(const std::string::value_type& a, const std::string::value_type& b) { return a == b; } inline bool isEqualNoCase(const std::string::value_type& a, const std::string::value_type& b) { return ::tolower(a) == ::tolower(b); } } // detail /** * Compares the two strings for equality using the given comparator function. * Returns true if the two strings are considered equal. */ template inline bool equals(const std::string& str1, const std::string& str2, Comparator compare) { std::string::const_iterator str1It = str1.begin(); std::string::const_iterator str1End = str1.end(); std::string::const_iterator str2It = str2.begin(); std::string::const_iterator str2End = str2.end(); for (; str1It != str1End && str2It != str2End; ++str1It, ++str2It) { if (!compare(*str1It, *str2It)) { return false; } } return str1It == str1End && str2It == str2End; } /** * Compares the two strings for equality using the given comparator function. * Returns true if the two strings are considered equal. */ template inline bool equals(const std::string& str1, const char* str2, Comparator compare) { if (str2 == nullptr) return false; std::string::const_iterator str1It = str1.begin(); std::string::const_iterator str1End = str1.end(); const char* str2It = str2; for (; str1It != str1End && *str2It != '\0'; ++str1It, ++str2It) { if (!compare(*str1It, *str2It)) { return false; } } return str1It == str1End && *str2It == '\0'; } /** * Returns true if the given input string is equal with the given test string, * compared case-sensitively. */ template inline bool equals(const std::string& input, const TestStringType& test) { return equals(input, test, detail::isEqual); } /** * Returns true if the given input string is equal with the given test string, * compared case-insensitively. */ template inline bool iequals(const std::string& input, const TestStringType& test) { return equals(input, test, detail::isEqualNoCase); } /** * Returns true if the given input string starts with the given test string, * compared using the given Comparator functor. */ template inline bool starts_with(const std::string& input, const std::string& test, Comparator compare) { std::string::const_iterator inputEnd = input.end(); std::string::const_iterator testEnd = test.end(); std::string::const_iterator testIt = test.begin(); for (std::string::const_iterator inpIt = input.begin(); inpIt != inputEnd && testIt != testEnd; ++inpIt, ++testIt) { if (!compare(*inpIt, *testIt)) { return false; } } return testIt == testEnd; } /** * Returns true if the given input string starts with the given test string sequence, * compared using the given Comparator functor. */ template inline bool starts_with(const std::string& input, const char* test, Comparator compare) { if (test == nullptr) return false; std::string::const_iterator inputEnd = input.end(); const char* testIt = test; for (std::string::const_iterator inpIt = input.begin(); inpIt != inputEnd && *testIt != '\0'; ++inpIt, ++testIt) { if (!compare(*inpIt, *testIt)) { return false; } } return *testIt == '\0'; } /** * Returns true if the given input string starts with the given test string, * compared case-sensitively. */ template inline bool starts_with(const std::string& input, const TestStringType& test) { return starts_with(input, test, detail::isEqual); } /** * Returns true if the given input string starts with the given test string, * compared case-insensitively. */ template inline bool istarts_with(const std::string& input, const TestStringType& test) { return starts_with(input, test, detail::isEqualNoCase); } /** * Returns true if the given input string ends with the given test string, * compared using the given Comparator functor. */ template inline bool ends_with(const std::string& input, const std::string& test, Comparator compare) { std::string::const_reverse_iterator inputEnd = input.rend(); std::string::const_reverse_iterator testEnd = test.rend(); std::string::const_reverse_iterator testIt = test.rbegin(); for (std::string::const_reverse_iterator inpIt = input.rbegin(); inpIt != inputEnd && testIt != testEnd; ++inpIt, ++testIt) { if (!compare(*inpIt, *testIt)) { return false; } } return testIt == testEnd; } /** * Returns true if the given input string ends with the given test string sequence, * compared using the given Comparator functor. */ template inline bool ends_with(const std::string& input, const char* test, Comparator compare) { if (test == nullptr) return false; std::string::const_reverse_iterator inputEnd = input.rend(); const char* testIt = test + std::strlen(test) - 1; for (std::string::const_reverse_iterator inpIt = input.rbegin(); inpIt != inputEnd && testIt >= test; ++inpIt, --testIt) { if (!compare(*inpIt, *testIt)) { return false; } if (testIt == test) { return true; } } return false; } /** * Returns true if the given input string ends with the given test string, * compared case-sensitively. */ template inline bool ends_with(const std::string& input, const TestStringType& test) { return ends_with(input, test, detail::isEqual); } /** * Returns true if the given input string ends with the given test string, * compared case-insensitively. */ template inline bool iends_with(const std::string& input, const TestStringType& test) { return ends_with(input, test, detail::isEqualNoCase); } } DarkRadiant-2.5.0/libs/string/replace.h000066400000000000000000000036431321750546400177420ustar00rootroot00000000000000#pragma once #include namespace string { /** * Replaces all occurrences of the given search string in the subject * with the given replacement, in-place. */ inline void replace_all(std::string& subject, const std::string& search, const std::string& replacement) { if (search.empty()) return; // nothing to do std::size_t pos = 0; while ((pos = subject.find(search, pos)) != std::string::npos) { subject.replace(pos, search.length(), replacement); pos += replacement.length(); } } /** * Replaces all occurrences of of the given search string with * the given replacement and returns a new string instance * containing the result. The incoming subject is passed by value such * that the original string is not altered. */ inline std::string replace_all_copy(std::string subject, const std::string& search, const std::string& replacement) { if (search.empty()) return subject; // nothing to do std::size_t pos = 0; while ((pos = subject.find(search, pos)) != std::string::npos) { subject.replace(pos, search.length(), replacement); pos += replacement.length(); } return subject; } /** * Replaces the first occurrence of the given search string in the subject * with the given replacement, in-place. */ inline void replace_first(std::string& subject, const std::string& search, const std::string& replacement) { if (search.empty()) return; // nothing to do std::size_t pos = subject.find(search); if (pos != std::string::npos) { subject.replace(pos, search.length(), replacement); } } /** * Replaces the last occurrence of the given search string in the subject * with the given replacement, in-place. */ inline void replace_last(std::string& subject, const std::string& search, const std::string& replacement) { if (search.empty()) return; // nothing to do std::size_t pos = subject.rfind(search); if (pos != std::string::npos) { subject.replace(pos, search.length(), replacement); } } } DarkRadiant-2.5.0/libs/string/split.h000066400000000000000000000030301321750546400174500ustar00rootroot00000000000000#pragma once #include #include namespace string { /** * Splits the given string into pieces based on the given delimiters, storing * the tokens at the end the given token container (which might be any container * supporting insert iterators (std::vector, std::list, std::set, etc.) */ template inline void split(ContainerType& tokens, const std::string& subject, const std::string& delimiters, bool trimEmpty = true) { std::string::size_type lastPos = 0; std::string::size_type length = subject.length(); // Construct an output iterator which inserts elements at the end of the container std::insert_iterator output = std::inserter(tokens, tokens.end()); while (lastPos < length + 1) { // Find the next occurrence of any of the delimiters std::string::size_type pos = subject.find_first_of(delimiters, lastPos); // Nothing found, set the pos to trigger an exit if (pos == std::string::npos) { pos = length; } // If the found position is different from the last occurrence, we have a non-empty token // If the position is the same, this is an empty token, in which case we need to check the flag if (pos != lastPos || !trimEmpty) { // Copy the range [lastPos..pos] from the subject and insert it into our token container output = typename ContainerType::value_type( subject.data() + lastPos, static_cast(pos - lastPos)); } // Advance the cursor by one to skip the found delimiter lastPos = pos + 1; } } } DarkRadiant-2.5.0/libs/string/string.h000066400000000000000000000061111321750546400176260ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once /// \file /// C-style null-terminated-character-array string library. #include /// \brief Returns true if [\p string, \p string + \p n) is lexicographically equal to [\p other, \p other + \p n). /// O(n) inline bool string_equal_n(const char* string, const char* other, std::size_t n) { return std::strncmp(string, other, n) == 0; } /// \brief Returns <0 if \p string is lexicographically less than \p other after converting both to lower-case. /// Returns >0 if \p string is lexicographically greater than \p other after converting both to lower-case. /// Returns 0 if \p string is lexicographically equal to \p other after converting both to lower-case. /// O(n) inline int string_compare_nocase(const char* string, const char* other) { #ifdef WIN32 return _stricmp(string, other); #else return strcasecmp(string, other); #endif } /// \brief Returns <0 if [\p string, \p string + \p n) is lexicographically less than [\p other, \p other + \p n). /// Returns >0 if [\p string, \p string + \p n) is lexicographically greater than [\p other, \p other + \p n). /// Returns 0 if [\p string, \p string + \p n) is lexicographically equal to [\p other, \p other + \p n). /// Treats all ascii characters as lower-case during comparisons. /// O(n) inline int string_compare_nocase_n(const char* string, const char* other, std::size_t n) { #ifdef WIN32 return _strnicmp(string, other, n); #else return strncasecmp(string, other, n); #endif } /// \brief Returns true if \p string is lexicographically equal to \p other. /// Treats all ascii characters as lower-case during comparisons. /// O(n) inline bool string_equal_nocase(const char* string, const char* other) { return string_compare_nocase(string, other) == 0; } /// \brief Returns true if [\p string, \p string + \p n) is lexicographically equal to [\p other, \p other + \p n). /// Treats all ascii characters as lower-case during comparisons. /// O(n) inline bool string_equal_nocase_n(const char* string, const char* other, std::size_t n) { return string_compare_nocase_n(string, other, n) == 0; } /// \brief Returns true if \p string is lexicographically less than \p other. /// Treats all ascii characters as lower-case during comparisons. /// O(n) inline bool string_less_nocase(const char* string, const char* other) { return string_compare_nocase(string, other) < 0; } DarkRadiant-2.5.0/libs/string/tokeniser.h000066400000000000000000000112321321750546400203230ustar00rootroot00000000000000#pragma once #include #include namespace string { /** * Tokeniser class similar to the one shipping with boost. It needs a TokeniserFunc type * as template argument which it uses to split the target string into pieces. * Use the getIterator() method to retrieve an Iterator object which can be used to * retrieve the resulting tokens and checking for input exhaustion. * * The TokeniserFunc is a function object matching the following signature: * * template * bool (InputIterator& next, const InputIterator& end, TokenType& tok); * * TokeniserFunc returns true if a non-empty token could be stored into "tok". * "next" is non-const is advanced by the TokeniserFunc as it parses the characters. * "end" indicates the end of the input sequence. * The InputIterator type usually refers to a std::string::const_iterator or similar, * dereferencing it will retrieve a char value. * * See CharTokeniserFunc in string/tokeniser.h for an example. */ template class Tokeniser { protected: // The iterators defining the range to tokenise InputIterator _begin; InputIterator _end; TokeniserFunc _func; public: // Public type for use by client code, to retrieve tokens class Iterator { private: TokeniserFunc _tokeniserFunc; InputIterator _cur; InputIterator _end; TokenType _token; bool _hasValidToken; // true if we're not exhausted yet public: Iterator(const TokeniserFunc& tokeniserFunc, const InputIterator& begin, const InputIterator& end) : _tokeniserFunc(tokeniserFunc), _cur(begin), _end(end), _hasValidToken(false) { // Initialise our local token by the first call to the tokenisation function advance(); } // Call this to check whether the iterator can deliver at least one more token. // Returns true if no more tokens are available. bool isExhausted() const { return !_hasValidToken; } // Retrieves the current token - only call this if isExhausted == false const TokenType& operator*() const { assert(!isExhausted()); return _token; } // Advances to the next token. Only call this when not exhausted Iterator& operator++() { assert(!isExhausted()); advance(); return *this; } // Advances to the next token. Only call this when not exhausted Iterator operator++(int) // postfix-increment { assert(!isExhausted()); Iterator prev(*this); advance(); return prev; } private: void advance() { // Call the tokenisation function and let it store the characters // into our local _token member. Store the func result bool _hasValidToken = _tokeniserFunc(_cur, _end, _token); } }; // Construct this tokeniser on top of the given iterator range [begin..end), // using the given TokeniserFunc to split it into tokens. Tokeniser(const InputIterator& begin, const InputIterator& end, const TokeniserFunc& func = TokeniserFunc()) : _begin(begin), _end(end), _func(func) {} // Construct this tokeniser on top of the given string, using the given TokeniserFunc // to split it into tokens. Tokeniser(const std::string& string, const TokeniserFunc& func = TokeniserFunc()) : Tokeniser(string.begin(), string.end(), func) {} // Returns an Iterator for use by client code, pointing // to the first token extacted from the target range Iterator getIterator() { return Iterator(_func, _begin, _end); } }; class CharTokeniserFunc { // List of delimiters const char* _delims; // Test if a character is a delimiter bool isDelim(char c) { const char* curDelim = _delims; while (*curDelim != 0) { if (*(curDelim++) == c) { return true; } } return false; } public: // Constructor CharTokeniserFunc(const char* delims) : _delims(delims) {} /* REQUIRED. Operator() is called by the string::tokeniser. This function * must search for a token between the two iterators next and end, and if * a token is found, set tok to the token, set next to position to start * parsing on the next call, and return true. */ template bool operator() (InputIterator& next, const InputIterator& end, std::string& tok) { // Clear out the token, no guarantee that it is empty tok.clear(); while (next != end) { if (isDelim(*next)) { // Are we still searching for a non-delimiter? if (tok.empty()) { ++next; continue; } // hit a delim, token filled => success return true; } // Next is pointing at a non-delimiter, add to token tok += *next++; } // Return true if we have added anything to the token return !tok.empty(); } }; } DarkRadiant-2.5.0/libs/string/trim.h000066400000000000000000000075621321750546400173060ustar00rootroot00000000000000#pragma once #include #include namespace string { /** * Removes all characters from the beginning of the string matching the given predicate, * modifying the given string in-place. */ template static inline void trim_left_if(std::string& subject, Predicate predicate) { // Erase everything from the beginning up to the first character // that is not matching the predicate (e.g. is not a space) subject.erase(subject.begin(), std::find_if(subject.begin(), subject.end(), [&](int ch) { return !predicate(ch); })); } /** * Removes all characters from the end of the string matching the given predicate, * modifying the given string in-place. */ template static inline void trim_right_if(std::string& subject, Predicate predicate) { // Erase everything from the end up to the nearest-to-last character // that is not matching the predicate (e.g. is not a space) subject.erase( std::find_if(subject.rbegin(), subject.rend(), [&](int ch) { return !predicate(ch); }).base(), subject.end()); } /** * Removes all characters from the beginning and the end of the string * matching the given predicate, returning a new string containing of the result. */ template static inline std::string trim_copy_if(std::string subject, Predicate predicate) { trim_left_if(subject, predicate); trim_right_if(subject, predicate); return subject; } /** * Removes all space characters from the beginning and the end of the string, in-place. */ inline void trim(std::string& subject) { trim_left_if(subject, ::isspace); trim_right_if(subject, ::isspace); } /** * Removes all space characters from the beginning and the end of the string * and returns a new string containing of the result. */ inline std::string trim_copy(std::string subject) { trim_left_if(subject, ::isspace); trim_right_if(subject, ::isspace); return subject; } /** * Removes all of the given characters from the beginning and the end of the subject, in-place. */ inline void trim(std::string& subject, const std::string& charsToBeRemoved) { trim_left_if(subject, [&](int ch) { return charsToBeRemoved.find(ch) != std::string::npos; }); trim_right_if(subject, [&](int ch) { return charsToBeRemoved.find(ch) != std::string::npos; }); } /** * Removes all of the given characters from the beginning and the end of the subject, * returning a new string containing the result. */ inline std::string trim_copy(std::string subject, const std::string& charsToBeRemoved) { trim_left_if(subject, [&](int ch) { return charsToBeRemoved.find(ch) != std::string::npos; }); trim_right_if(subject, [&](int ch) { return charsToBeRemoved.find(ch) != std::string::npos; }); return subject; } /** * Removes all of the given characters from the beginning of the subject, in-place */ inline void trim_left(std::string& subject, const std::string& charsToBeRemoved) { trim_left_if(subject, [&](int ch) { return charsToBeRemoved.find(ch) != std::string::npos; }); } /** * Removes all of the given characters from the beginning of the subject, * returning a new string containing the result. */ inline std::string trim_left_copy(std::string subject, const std::string& charsToBeRemoved) { trim_left_if(subject, [&](int ch) { return charsToBeRemoved.find(ch) != std::string::npos; }); return subject; } /** * Removes all of the given characters from the end of the subject, in-place */ inline void trim_right(std::string& subject, const std::string& charsToBeRemoved) { trim_right_if(subject, [&](int ch) { return charsToBeRemoved.find(ch) != std::string::npos; }); } /** * Removes all of the given characters from the end of the subject, * returning a new string containing the result. */ inline std::string trim_right_copy(std::string subject, const std::string& charsToBeRemoved) { trim_right_if(subject, [&](int ch) { return charsToBeRemoved.find(ch) != std::string::npos; }); return subject; } } DarkRadiant-2.5.0/libs/texturelib.h000066400000000000000000000212071321750546400172040ustar00rootroot00000000000000/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (INCLUDED_TEXTURELIB_H) #define INCLUDED_TEXTURELIB_H #include "debugging/debugging.h" #include "math/Vector3.h" #include "math/Vector2.h" #include "math/Matrix4.h" #include "math/Plane3.h" #include "igl.h" #include #include #include "iimage.h" #include "ishaders.h" typedef unsigned int GLuint; enum ProjectionAxis { eProjectionAxisX = 0, eProjectionAxisY = 1, eProjectionAxisZ = 2, }; inline Matrix4 matrix4_rotation_for_vector3(const Vector3& x, const Vector3& y, const Vector3& z) { return Matrix4::byColumns( x.x(), x.y(), x.z(), 0, y.x(), y.y(), y.z(), 0, z.x(), z.y(), z.z(), 0, 0, 0, 0, 1 ); } inline Matrix4 matrix4_swap_axes(const Vector3& from, const Vector3& to) { if(from.x() != 0 && to.y() != 0) { return matrix4_rotation_for_vector3(to, from, g_vector3_axis_z); } if(from.x() != 0 && to.z() != 0) { return matrix4_rotation_for_vector3(to, g_vector3_axis_y, from); } if(from.y() != 0 && to.z() != 0) { return matrix4_rotation_for_vector3(g_vector3_axis_x, to, from); } if(from.y() != 0 && to.x() != 0) { return matrix4_rotation_for_vector3(from, to, g_vector3_axis_z); } if(from.z() != 0 && to.x() != 0) { return matrix4_rotation_for_vector3(from, g_vector3_axis_y, to); } if(from.z() != 0 && to.y() != 0) { return matrix4_rotation_for_vector3(g_vector3_axis_x, from, to); } ERROR_MESSAGE("unhandled axis swap case"); return Matrix4::getIdentity(); } inline Matrix4 matrix4_reflection_for_plane(const Plane3& plane) { return Matrix4::byColumns( 1 - (2 * plane.normal().x() * plane.normal().x()), -2 * plane.normal().x() * plane.normal().y(), -2 * plane.normal().x() * plane.normal().z(), 0, -2 * plane.normal().y() * plane.normal().x(), 1 - (2 * plane.normal().y() * plane.normal().y()), -2 * plane.normal().y() * plane.normal().z(), 0, -2 * plane.normal().z() * plane.normal().x(), -2 * plane.normal().z() * plane.normal().y(), 1 - (2 * plane.normal().z() * plane.normal().z()), 0, -2 * plane.dist() * plane.normal().x(), -2 * plane.dist() * plane.normal().y(), -2 * plane.dist() * plane.normal().z(), 1 ); } inline Matrix4 matrix4_reflection_for_plane45(const Plane3& plane, const Vector3& from, const Vector3& to) { Vector3 first = from; Vector3 second = to; if ((from.dot(plane.normal()) > 0) == (to.dot(plane.normal()) > 0)) { first = -first; second = -second; } #if 0 rMessage() << "normal: "; print_vector3(plane.normal()); rMessage() << "from: "; print_vector3(first); rMessage() << "to: "; print_vector3(second); #endif Matrix4 swap = matrix4_swap_axes(first, second); //Matrix4 tmp = matrix4_reflection_for_plane(plane); swap.tx() = -(-2 * plane.normal().x() * plane.dist()); swap.ty() = -(-2 * plane.normal().y() * plane.dist()); swap.tz() = -(-2 * plane.normal().z() * plane.dist()); return swap; } const double ProjectionAxisEpsilon = 0.0001; inline bool projectionaxis_better(double axis, double other) { return fabs(axis) > fabs(other) + ProjectionAxisEpsilon; } /// \brief Texture axis precedence: Z > X > Y inline ProjectionAxis projectionaxis_for_normal(const Vector3& normal) { return (projectionaxis_better(normal[eProjectionAxisY], normal[eProjectionAxisX])) ? (projectionaxis_better(normal[eProjectionAxisY], normal[eProjectionAxisZ])) ? eProjectionAxisY : eProjectionAxisZ : (projectionaxis_better(normal[eProjectionAxisX], normal[eProjectionAxisZ])) ? eProjectionAxisX : eProjectionAxisZ; } /*! \brief Construct a transform from XYZ space to ST space (3d to 2d). This will be one of three axis-aligned spaces, depending on the surface normal. NOTE: could also be done by swapping values. */ inline void Normal_GetTransform(const Vector3& normal, Matrix4& transform) { switch (projectionaxis_for_normal(normal)) { case eProjectionAxisZ: transform[0] = 1; transform[1] = 0; transform[2] = 0; transform[4] = 0; transform[5] = 1; transform[6] = 0; transform[8] = 0; transform[9] = 0; transform[10] = 1; break; case eProjectionAxisY: transform[0] = 1; transform[1] = 0; transform[2] = 0; transform[4] = 0; transform[5] = 0; transform[6] = -1; transform[8] = 0; transform[9] = 1; transform[10] = 0; break; case eProjectionAxisX: transform[0] = 0; transform[1] = 0; transform[2] = 1; transform[4] = 1; transform[5] = 0; transform[6] = 0; transform[8] = 0; transform[9] = 1; transform[10] = 0; break; } transform[3] = transform[7] = transform[11] = transform[12] = transform[13] = transform[14] = 0; transform[15] = 1; } /* greebo: This method calculates the normalised basis vectors of the texture plane as defined by * * If the normal vector points to the z-direction, the basis vectors are part * of the xy-plane: texS = <0,1,0> and texT = <1,0,0> * * If normal vector points to the negative z-direction, the above case applies, but with * the x-direction inversed: texS = <0,1,0> and texT = <-1,0,0> (note the minus) * * If none of the two above cases apply, the basis is calculated via cross products * that result in vectors perpendicular to . These lie within the plane * that is defined by the normal vector itself. * * Note: the vector MUST be normalised for this to function correctly. */ inline void ComputeAxisBase(const Vector3& normal, Vector3& texS, Vector3& texT) { const Vector3 up(0, 0, 1); const Vector3 down(0, 0, -1); if (normal.isEqual(up, 1e-6)) { texS = Vector3(0, 1, 0); texT = Vector3(1, 0, 0); } else if (normal.isEqual(down, 1e-6)) { texS = Vector3(0, 1, 0); texT = Vector3(-1, 0, 0); } else { texS = normal.crossProduct(up).getNormalised(); texT = normal.crossProduct(texS).getNormalised(); texS = -texS; } } /* greebo: this is used to calculate the directions the patch is "flattened" in. * If one of the patch bases is parallel or anti-parallel to the it cannot * be projected onto the facePlane, so a new orthogonal vector is taken as direction instead. * * This prevents the patch from disappearing and the texture from being infinetly stretched in such cases. * * @returns: This returns two normalised vectors that are orthogonal to the face plane normal and point * into the direction of the patch orientation. */ inline void getVirtualPatchBase(const Vector3& widthVector, const Vector3& heightVector, const Vector3& faceNormal, Vector3& widthBase, Vector3& heightBase) { bool widthVectorIsParallel = widthVector.isParallel(faceNormal); bool heightVectorIsParallel = heightVector.isParallel(faceNormal); if (widthVectorIsParallel) { // Calculate a orthogonal width vector widthBase = faceNormal.crossProduct(heightVector).getNormalised(); } else { // Project the vector onto the faceplane (this is the width direction) widthBase = (widthVector - faceNormal*(faceNormal*widthVector)).getNormalised(); } if (heightVectorIsParallel) { // Calculate a orthogonal height vector heightBase = faceNormal.crossProduct(widthVector).getNormalised(); } else { // Project the vector onto the faceplane (this is the height direction) heightBase = (heightVector - faceNormal*(faceNormal*heightVector)).getNormalised(); } } // handles degenerate cases, just in case library atan2 doesn't inline double arctangent_yx(double y, double x) { if (fabs(x) > 1.0E-6) { return atan2(y, x); } else if (y > 0) { return c_half_pi; } else { return -c_half_pi; } } // Returns the index of the one edge which points "most" into the given direction, should be normalised inline std::size_t findBestEdgeForDirection(const Vector2& direction, const std::vector& edges) { double best = -LONG_MAX; std::size_t bestIndex = 0; for (std::size_t i = 0; i < edges.size(); ++i) { double dot = direction.dot(edges[i]); if (dot <= best) continue; // Found a new best edge bestIndex = i; best = dot; } return bestIndex; } #endif DarkRadiant-2.5.0/libs/transformlib.h000066400000000000000000000022571321750546400175230ustar00rootroot00000000000000#pragma once #include "Transformable.h" #include "itransformnode.h" #include "math/Matrix4.h" /// \brief A transform node which has no effect. class IdentityTransform : public ITransformNode { public: /// \brief Returns the identity matrix. const Matrix4& localToParent() const { return Matrix4::getIdentity(); } }; /// \brief A transform node which stores a generic transformation matrix. class MatrixTransform : public ITransformNode { Matrix4 _localToParent; public: MatrixTransform() : _localToParent(Matrix4::getIdentity()) {} Matrix4& localToParent() { return _localToParent; } /// \brief Returns the stored local->parent transform. const Matrix4& localToParent() const { return _localToParent; } }; namespace scene { /** * Visit each transformable that is child of the given node with the given functor. */ inline void foreachTransformable(const scene::INodePtr& node, const std::function& functor) { if (!node) return; node->foreachNode([&] (const scene::INodePtr& child)->bool { ITransformablePtr transformable = Node_getTransformable(child); if (transformable) { functor(*transformable); } return true; }); } } DarkRadiant-2.5.0/libs/util/000077500000000000000000000000001321750546400156175ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/util/Noncopyable.h000066400000000000000000000006131321750546400202410ustar00rootroot00000000000000#pragma once namespace util { /** * Classes deriving from this one will end up as noncopyable * since the copy-constructor and assignment operators are marked * as deleted. Modeled after boost::noncopyable. */ class Noncopyable { public: constexpr Noncopyable() = default; protected: Noncopyable(const Noncopyable&) = delete; Noncopyable& operator=(const Noncopyable&) = delete; }; } DarkRadiant-2.5.0/libs/util/ScopedBoolLock.h000066400000000000000000000006431321750546400206350ustar00rootroot00000000000000#pragma once /// Miscellaneous utility classes namespace util { /// RAII object which sets a bool to true for its lifetime class ScopedBoolLock { bool& _target; public: /// Construct and set target to true ScopedBoolLock(bool& target) : _target(target) { _target = true; } /// Destroy and set target to false ~ScopedBoolLock() { _target = false; } }; } DarkRadiant-2.5.0/libs/wxutil/000077500000000000000000000000001321750546400161765ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/wxutil/ChoiceHelper.h000066400000000000000000000052721321750546400207070ustar00rootroot00000000000000#pragma once #include #include #include "string/convert.h" namespace wxutil { // Various wxChoice helper methods class ChoiceHelper { public: // Returns the numeric ID (that has been attached to the choice items) of the current selection. // Returns wxNOT_FOUND if no valid selection could be determined static int GetSelectionId(wxChoice* choice) { if (choice->GetSelection() != wxNOT_FOUND) { wxClientData* data = choice->GetClientObject(choice->GetSelection()); if (data == NULL) return wxNOT_FOUND; wxStringClientData* idStr = dynamic_cast(data); if (idStr == NULL) return wxNOT_FOUND; return string::convert(idStr->GetData().ToStdString(), wxNOT_FOUND); } else { return -1; } } // Selects the item whose numeric attached id is equal to static void SelectItemByStoredId(wxChoice* choice, int id) { for (unsigned int i = 0; i < choice->GetCount(); ++i) { wxStringClientData* idStr = static_cast(choice->GetClientObject(i)); int foundId = string::convert(idStr->GetData().ToStdString(), wxNOT_FOUND); if (foundId == id) { choice->SetSelection(i); return; } } choice->SetSelection(wxNOT_FOUND); } // Returns the string that has been attached as string client data to the currently selected choice item static std::string GetSelectedStoredString(wxChoice* choice) { int selectionIdx = choice->GetSelection(); if (selectionIdx == wxNOT_FOUND) { return std::string(); } wxStringClientData* data = static_cast(choice->GetClientObject(selectionIdx)); return data->GetData().ToStdString(); } // Selects the item whose attached string is equal to static void SelectItemByStoredString(wxChoice* choice, const wxString& str) { choice->SetSelection(wxNOT_FOUND); // Get the iter into the liststore pointing at the correct STIM_YYYY type for (unsigned int i = 0; i < choice->GetCount(); ++i) { wxStringClientData* data = static_cast(choice->GetClientObject(i)); if (data->GetData().ToStdString() == str) { choice->SetSelection(i); return; } } } // Selects the item whose attached string is equal to static void SelectComboItemByStoredString(wxComboBox* combo, const wxString& str) { combo->SetSelection(wxNOT_FOUND); // Get the iter into the liststore pointing at the correct STIM_YYYY type for (unsigned int i = 0; i < combo->GetCount(); ++i) { wxStringClientData* data = static_cast(combo->GetClientObject(i)); if (data->GetData().ToStdString() == str) { combo->SetSelection(i); return; } } } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/ConsoleView.cpp000066400000000000000000000044021321750546400211370ustar00rootroot00000000000000#include "ConsoleView.h" #include "imodule.h" #include "string/replace.h" namespace wxutil { ConsoleView::ConsoleView(wxWindow* parent) : wxTextCtrl(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_RICH2|wxTE_READONLY), _errorAttr(*wxRED), _warningAttr(wxColour(128, 128, 0)), _standardAttr(*wxBLACK) { _lineBuffer.reserve(512); } void ConsoleView::appendText(const std::string& text, TextMode mode) { // The text usually arrives in single characters at a time // Directly writing to the wxTextCtrl is awfully slow, so let's do some buffering // In case the textmode changes, we need to flush the line if (_bufferMode != mode) { flushLine(); } // Write to the buffer first _bufferMode = mode; _buffer.append(text); // Once we hit a single newline, flush the line if (text == "\n") { flushLine(); } // Request an idle callback on the GUI thread requestIdleCallback(); } void ConsoleView::flushLine() { if (!_buffer.empty()) { std::lock_guard lock(_lineBufferMutex); _lineBuffer.push_back(std::make_pair(_bufferMode, std::string())); _lineBuffer.back().second.swap(_buffer); } } void ConsoleView::onIdle() { // Idle events occur in the main thread - prevent interrupting // threads in the middle of a line std::lock_guard idleLock( module::GlobalModuleRegistry().getApplicationContext().getStreamLock()); flushLine(); std::lock_guard lock(_lineBufferMutex); if (_lineBuffer.empty()) return; for (LineBuffer::value_type& pair : _lineBuffer) { switch (pair.first) { case ModeStandard: SetDefaultStyle(_standardAttr); break; case ModeWarning: SetDefaultStyle(_warningAttr); break; case ModeError: SetDefaultStyle(_errorAttr); break; default: SetDefaultStyle(_standardAttr); }; // Replace NULL characters string::replace_all(pair.second, std::string(1, '\0'), "NULL"); // Insert at the end of the text buffer AppendText(pair.second); } _lineBuffer.clear(); // Scroll to bottom ShowPosition(GetLastPosition()); } } // namespace DarkRadiant-2.5.0/libs/wxutil/ConsoleView.h000066400000000000000000000015121321750546400206030ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "event/SingleIdleCallback.h" class wxWindow; namespace wxutil { class ConsoleView : public wxTextCtrl, private SingleIdleCallback { public: // The text modes determining the colour enum TextMode { ModeStandard, ModeWarning, ModeError, }; private: wxTextAttr _errorAttr; wxTextAttr _warningAttr; wxTextAttr _standardAttr; TextMode _bufferMode; std::string _buffer; typedef std::vector > LineBuffer; LineBuffer _lineBuffer; std::mutex _lineBufferMutex; public: ConsoleView(wxWindow* parent); // Appends new text to the end of the buffer void appendText(const std::string& text, TextMode mode); protected: void onIdle(); void flushLine(); }; } DarkRadiant-2.5.0/libs/wxutil/ControlButton.h000066400000000000000000000037011321750546400211640ustar00rootroot00000000000000#pragma once #include #include namespace wxutil { namespace { // The delay between the first "click" and the second "click" event const int DELAY_INITIAL = 200; // The delay between all following "click" events const int DELAY_PERIODIC = 20; } /** * A button containing a single icon that keeps periodically emitting the * "clicked" event as long as the user keeps the mouse button pressed. * Used for Surface Inspector controls, for example. */ class ControlButton : public wxBitmapButton { private: wxTimer _timer; public: ControlButton(wxWindow* parent, const wxBitmap& bitmap) : wxBitmapButton(parent, wxID_ANY, bitmap), _timer(this) { // Connect the pressed/released signals Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(ControlButton::onPress), NULL, this); Connect(wxEVT_LEFT_UP, wxMouseEventHandler(ControlButton::onRelease), NULL, this); Connect(wxEVT_TIMER, wxTimerEventHandler(ControlButton::onIntervalReached), NULL, this); _timer.Stop(); } void onIntervalReached(wxTimerEvent& ev) { // Safety check to see whether the mouse is still pointing on our control wxPoint mousePos = wxGetMousePosition(); wxWindow* windowAtPoint = wxFindWindowAtPointer(mousePos); if (windowAtPoint != this) { // Disconnect the timing event _timer.Stop(); return; } // Fire the "clicked" signal wxCommandEvent event(wxEVT_BUTTON, GetId()); event.SetEventObject(this); ProcessEvent(event); // Set the interval to a smaller value _timer.Stop(); _timer.Start(DELAY_PERIODIC); } void onPress(wxMouseEvent& ev) { // Trigger a first click wxCommandEvent event(wxEVT_BUTTON, GetId()); event.SetEventObject(this); ProcessEvent(event); // Start the timer using the initial value _timer.Start(DELAY_INITIAL); } void onRelease(wxMouseEvent& ev) { // Disconnect the timing event _timer.Stop(); } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/Cursor.cpp000066400000000000000000000016431321750546400201630ustar00rootroot00000000000000#include "Cursor.h" #ifdef WIN32 #include #else #include #endif namespace gtkutil { #ifdef WIN32 void Cursor::ReadPosition(const Glib::RefPtr& window, int& x, int& y) { POINT pos; GetCursorPos(&pos); ScreenToClient((HWND)GDK_WINDOW_HWND(window->get_window()->gobj()), &pos); x = pos.x; y = pos.y; } void Cursor::SetPosition(const Glib::RefPtr& window, int x, int y) { POINT pos; pos.x = x; pos.y = y; ClientToScreen((HWND)GDK_WINDOW_HWND(window->get_window()->gobj()), &pos); SetCursorPos(pos.x, pos.y); } #else void Cursor::ReadPosition(const Glib::RefPtr& window, int& x, int& y) { gdk_display_get_pointer(gdk_display_get_default(), 0, &x, &y, 0); } void Cursor::SetPosition(const Glib::RefPtr& window, int x, int y) { XWarpPointer(GDK_DISPLAY(), None, GDK_ROOT_WINDOW(), 0, 0, 0, 0, x, y); } #endif } // namespace DarkRadiant-2.5.0/libs/wxutil/Cursor.h000066400000000000000000000007051321750546400176260ustar00rootroot00000000000000#pragma once #include #include namespace gtkutil { class Cursor { public: /** * Reads the cursor position of the given window and writes the result to x,y. */ static void ReadPosition(const Glib::RefPtr& window, int& x, int& y); /** * Sets the cursor position of the given window to x,y. */ static void SetPosition(const Glib::RefPtr& window, int x, int y); }; } // namespace DarkRadiant-2.5.0/libs/wxutil/DeferredMotionDelta.h000066400000000000000000000015331321750546400222310ustar00rootroot00000000000000#pragma once #include #include "event/SingleIdleCallback.h" namespace wxutil { /** * A class accumulating mouse motion delta calls. When GTK is idle, * the attached function object is called with the stored x,y delta values. */ class DeferredMotionDelta : private wxutil::SingleIdleCallback { public: typedef std::function MotionDeltaFunction; private: int _deltaX; int _deltaY; MotionDeltaFunction _function; public: DeferredMotionDelta(const MotionDeltaFunction& function) : _deltaX(0), _deltaY(0), _function(function) {} void flush() { flushIdleCallback(); } void onMouseMotionDelta(int x, int y, unsigned int state) { _deltaX += x; _deltaY += y; requestIdleCallback(); } private: void onIdle() { _function(_deltaX, _deltaY); _deltaX = 0; _deltaY = 0; } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/DirChooser.cpp000066400000000000000000000020511321750546400207410ustar00rootroot00000000000000#include "DirChooser.h" #include #include #include #include #include "imainframe.h" namespace wxutil { DirChooser::DirChooser(wxWindow* parent, const std::string& title) : _dialog(new wxDirDialog(parent != NULL ? parent : GlobalMainFrame().getWxTopLevelWindow(), title)), _title(title) {} DirChooser::~DirChooser() { delete _dialog; } void DirChooser::setCurrentPath(const std::string& path) { _dialog->SetPath(path); } std::string DirChooser::getSelectedFolderName() { return _dialog->GetPath().ToStdString(); } std::string DirChooser::display() { int curDisplayIdx = wxDisplay::GetFromWindow(wxTheApp->GetTopWindow()); wxDisplay curDisplay(curDisplayIdx); wxRect rect = curDisplay.GetGeometry(); int newWidth = static_cast(rect.GetWidth() * 0.5f); int newHeight = static_cast(rect.GetHeight() * 0.66f); _dialog->SetSize(newWidth, newHeight); _dialog->CenterOnScreen(); if (_dialog->ShowModal() == wxID_OK) { return getSelectedFolderName(); } return ""; } } // namespace DarkRadiant-2.5.0/libs/wxutil/DirChooser.h000066400000000000000000000014171321750546400204130ustar00rootroot00000000000000#pragma once #include "ifilechooser.h" #include #include class wxWindow; namespace wxutil { class DirChooser : public ui::IDirChooser { private: wxDirDialog* _dialog; // Window title std::string _title; public: DirChooser(wxWindow* parent, const std::string& title); virtual ~DirChooser(); // Lets the dialog start at a certain path virtual void setCurrentPath(const std::string& path); /** * Returns the selected folder name. */ virtual std::string getSelectedFolderName(); /** * greebo: Displays the dialog and enters the main loop. * Returns the filename or "" if the user hit cancel. * * The returned file name is normalised using the os::standardPath() method. */ virtual std::string display(); }; } // namespace DarkRadiant-2.5.0/libs/wxutil/EntryAbortedException.h000066400000000000000000000005011321750546400226240ustar00rootroot00000000000000#pragma once #include #include namespace wxutil { /* Exception class raised when a text entry dialog is cancelled or destroyed. */ class EntryAbortedException: public std::runtime_error { public: EntryAbortedException(const std::string& what): std::runtime_error(what) { } }; } DarkRadiant-2.5.0/libs/wxutil/FileChooser.cpp000066400000000000000000000122441321750546400211070ustar00rootroot00000000000000#include "FileChooser.h" #include "ifiletypes.h" #include "i18n.h" #include "imapformat.h" #include "igame.h" #include "os/path.h" #include "os/file.h" #include "MultiMonitor.h" #include "dialog/MessageBox.h" #include "string/predicate.h" #include "string/case_conv.h" #include namespace wxutil { FileChooser::FileChooser(const std::string& title, bool open, const std::string& fileType, const std::string& defaultExt) : FileChooser(GlobalMainFrame().getWxTopLevelWindow(), title, open, fileType, defaultExt) {} FileChooser::FileChooser(wxWindow* parentWindow, const std::string& title, bool open, const std::string& fileType, const std::string& defaultExt) : _dialog(new wxFileDialog(parentWindow, title, wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, getStyle(open))), _title(title), _fileType(fileType), _defaultExt(defaultExt), _open(open) { construct(); } FileChooser::~FileChooser() { _dialog->Destroy(); } void FileChooser::construct() { // Sanity-check the filetype if (_fileType.empty()) { _fileType = "*"; } // Set a meaningful title if empty if (_title.empty()) { _title = _open ? _("Open File") : _("Save File"); } // Make default extension lowercase string::to_lower(_defaultExt); int defaultFormatIdx = 0; int curFormatIdx = 0; // Add the filetype FileTypePatterns patterns = GlobalFiletypes().getPatternsForType(_fileType); for (FileTypePatterns::const_iterator i = patterns.begin(); i != patterns.end(); ++i) { if (!_open && _fileType == filetype::TYPE_MAP) { std::set formats = GlobalMapFormatManager().getMapFormatList(i->extension); // Pre-select take the default map format for this game type map::MapFormatPtr defaultFormat = GlobalMapFormatManager().getMapFormatForGameType( GlobalGameManager().currentGame()->getKeyValue("type"), i->extension ); std::for_each(formats.begin(), formats.end(), [&] (const map::MapFormatPtr& format) { FileFilter filter; filter.caption = format->getMapFormatName() + " " + i->name + " (" + i->pattern + ")"; filter.filter = i->pattern; filter.mapFormatName = format->getMapFormatName(); _fileFilters.push_back(filter); if (format == defaultFormat) { defaultFormatIdx = curFormatIdx; } ++curFormatIdx; }); } else { FileFilter filter; filter.caption = i->name + " (" + i->pattern + ")"; filter.filter = i->pattern; // Pre-select the filter matching the default extension if (i->extension == _defaultExt) { defaultFormatIdx = curFormatIdx; } _fileFilters.push_back(filter); ++curFormatIdx; } } // Add a final mask for All Files (*.*) FileFilter filter; filter.caption = _("All Files (*.*)"); filter.filter = "*.*"; _fileFilters.push_back(filter); std::string wildcard = ""; std::for_each(_fileFilters.begin(), _fileFilters.end(), [&] (const FileFilter& filter) { wildcard += wildcard.empty() ? "" : "|"; wildcard += filter.caption + "|" + filter.filter; }); _dialog->SetWildcard(wildcard); _dialog->SetFilterIndex(defaultFormatIdx); } long FileChooser::getStyle(bool open) { if (open) { return wxFD_OPEN; } else // !open { return wxFD_SAVE | wxFD_OVERWRITE_PROMPT; } } void FileChooser::setCurrentPath(const std::string& path) { _path = os::standardPathWithSlash(path); // Convert path to standard and set the folder in the dialog _dialog->SetPath(_path); // SetPath() overwrites the filename, so set it again if (!_file.empty()) { _dialog->SetFilename(_file); } } void FileChooser::setCurrentFile(const std::string& file) { _file = os::getFilename(file); if (!_open) { _dialog->SetFilename(_file); } } std::string FileChooser::getSelectedFileName() { // Load the filename from the dialog std::string fileName = os::standardPath(_dialog->GetPath().ToStdString()); // Append the default extension for save operations before checking overwrites if (!_open // save operation && !fileName.empty() // valid filename && !_defaultExt.empty() // non-empty default extension && !string::iends_with(fileName, _defaultExt)) // no default extension { fileName.append(_defaultExt); } return fileName; } std::string FileChooser::getSelectedMapFormat() { int index = _dialog->GetFilterIndex(); if (index >=0 && index < static_cast(_fileFilters.size())) { return _fileFilters[index].mapFormatName; } return ""; } void FileChooser::askForOverwrite(bool ask) { if (ask) { _dialog->SetWindowStyleFlag(_dialog->GetWindowStyleFlag() | wxFD_OVERWRITE_PROMPT); } else { _dialog->SetWindowStyleFlag(_dialog->GetWindowStyleFlag() & ~wxFD_OVERWRITE_PROMPT); } } std::string FileChooser::display() { int curDisplayIdx = wxDisplay::GetFromWindow(wxTheApp->GetTopWindow()); wxDisplay curDisplay(curDisplayIdx); wxRect rect = curDisplay.GetGeometry(); int newWidth = static_cast(rect.GetWidth() * 0.5f); int newHeight = static_cast(rect.GetHeight() * 0.66f); _dialog->SetSize(newWidth, newHeight); _dialog->CenterOnScreen(); if (_dialog->ShowModal() == wxID_OK) { return getSelectedFileName(); } return ""; } } // namespace wxutil DarkRadiant-2.5.0/libs/wxutil/FileChooser.h000066400000000000000000000055041321750546400205550ustar00rootroot00000000000000#pragma once #include "ifilechooser.h" #include #include #include #include namespace wxutil { class FileChooser : public ui::IFileChooser { private: wxFileDialog* _dialog; // Window title std::string _title; std::string _path; std::string _file; std::string _fileType; std::string _defaultExt; // Open or save dialog bool _open; struct FileFilter { std::string caption; // "Doom 3 Map (*.map)" std::string filter; // "*.map" std::string mapFormatName; }; std::vector _fileFilters; public: /** * Construct a new filechooser with the given parameters. * * @title: The dialog title. * @open: if TRUE this is asking for "Open" files, FALSE generates a "Save" dialog. * @browseFolders: if TRUE the dialog is asking the user for directories only. * @fileType: the type "map", "prefab", this determines the file extensions. * @defaultExt: The default extension appended when the user enters * filenames without extension. */ FileChooser(const std::string& title, bool open, const std::string& fileType = "", const std::string& defaultExt = ""); /** * Construct a new filechooser with the given parameters. * * @parentWindow: The parent window, must not be NULL. * @title: The dialog title. * @open: if TRUE this is asking for "Open" files, FALSE generates a "Save" dialog. * @browseFolders: if TRUE the dialog is asking the user for directories only. * @pattern: the type "map", "prefab", this determines the file extensions. * @defaultExt: The default extension appended when the user enters * filenames without extension. */ FileChooser(wxWindow* parentWindow, const std::string& title, bool open, const std::string& fileType = "", const std::string& defaultExt = ""); virtual ~FileChooser(); // Lets the dialog start at a certain path void setCurrentPath(const std::string& path); // Pre-fills the currently selected file void setCurrentFile(const std::string& file); /** * Returns the selected filename (default extension * will be added if appropriate). */ virtual std::string getSelectedFileName(); /** * Get the selected map format by name (corresponds to the * string as returned by MapFormat::getMapFormatName()). */ virtual std::string getSelectedMapFormat(); /** * It's possible to inihibit the "File exists - replace" question when * selecting filenames for saving. */ void askForOverwrite(bool ask); /** * greebo: Displays the dialog and enters a blocking loop. * Returns the filename or "" if the user hit cancel. * * The returned file name is normalised using the os::standardPath() method. */ virtual std::string display(); private: static long getStyle(bool open); void construct(); // shared constructor stuff }; } // namespace wxutil DarkRadiant-2.5.0/libs/wxutil/FreezePointer.cpp000066400000000000000000000155631321750546400214750ustar00rootroot00000000000000#include "FreezePointer.h" #include #include "debugging/debugging.h" #include "MouseButton.h" namespace wxutil { FreezePointer::FreezePointer() : _freezePosX(0), _freezePosY(0), _freezePointer(true), _hidePointer(true), _motionReceivesDeltas(true), _capturedWindow(nullptr) {} void FreezePointer::startCapture(wxWindow* window, const MotionFunction& motionDelta, const CaptureLostFunction& endMove) { startCapture(window, motionDelta, endMove, true, true, true); } void FreezePointer::startCapture(wxWindow* window, const MotionFunction& motionDelta, const CaptureLostFunction& endMove, bool freezePointer, bool hidePointer, bool motionReceivesDeltas) { assert(window); ASSERT_MESSAGE(motionDelta, "can't capture pointer"); ASSERT_MESSAGE(endMove, "can't capture pointer"); // Pass the flags before going ahead setFreezePointer(freezePointer); setHidePointer(hidePointer); setSendMotionDeltas(motionReceivesDeltas); // Find the toplevel window wxWindow* topLevel = wxGetTopLevelParent(window); if (_hidePointer) { topLevel->SetCursor(wxCursor(wxCURSOR_BLANK)); } // We capture the mouse on the toplevel app, coordinates // are measured relative to the child window if (!topLevel->HasCapture()) { topLevel->CaptureMouse(); } _capturedWindow = window; wxPoint windowMousePos = _capturedWindow->ScreenToClient(wxGetMousePosition()); _freezePosX = windowMousePos.x; _freezePosY = windowMousePos.y; if (_freezePointer) { _capturedWindow->WarpPointer(_freezePosX, _freezePosY); } _motionFunction = motionDelta; _captureLostFunction = endMove; topLevel->Connect(wxEVT_MOTION, wxMouseEventHandler(FreezePointer::onMouseMotion), NULL, this); topLevel->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(FreezePointer::onMouseUp), NULL, this); topLevel->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(FreezePointer::onMouseUp), NULL, this); topLevel->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(FreezePointer::onMouseUp), NULL, this); topLevel->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(FreezePointer::onMouseDown), NULL, this); topLevel->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(FreezePointer::onMouseDown), NULL, this); topLevel->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(FreezePointer::onMouseDown), NULL, this); topLevel->Connect(wxEVT_MOUSE_CAPTURE_LOST, wxMouseCaptureLostEventHandler(FreezePointer::onMouseCaptureLost), NULL, this); } bool FreezePointer::isCapturing(wxWindow* window) { return _capturedWindow != nullptr; } void FreezePointer::endCapture() { if (!_capturedWindow) { return; // safeguard against duplicate unfreeze() calls } wxWindow* window = _capturedWindow; wxWindow* topLevel = wxGetTopLevelParent(window); _capturedWindow = nullptr; _motionFunction = MotionFunction(); _captureLostFunction = CaptureLostFunction(); if (_freezePointer) { window->WarpPointer(_freezePosX, _freezePosY); } if (_hidePointer) { topLevel->SetCursor(wxCursor(wxCURSOR_DEFAULT)); } if (topLevel->HasCapture()) { topLevel->ReleaseMouse(); } topLevel->Disconnect(wxEVT_MOUSE_CAPTURE_LOST, wxMouseCaptureLostEventHandler(FreezePointer::onMouseCaptureLost), NULL, this); topLevel->Disconnect(wxEVT_MOTION, wxMouseEventHandler(FreezePointer::onMouseMotion), NULL, this); topLevel->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(FreezePointer::onMouseUp), NULL, this); topLevel->Disconnect(wxEVT_RIGHT_UP, wxMouseEventHandler(FreezePointer::onMouseUp), NULL, this); topLevel->Disconnect(wxEVT_MIDDLE_UP, wxMouseEventHandler(FreezePointer::onMouseUp), NULL, this); topLevel->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(FreezePointer::onMouseDown), NULL, this); topLevel->Disconnect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(FreezePointer::onMouseDown), NULL, this); topLevel->Disconnect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(FreezePointer::onMouseDown), NULL, this); } void FreezePointer::setFreezePointer(bool shouldFreeze) { _freezePointer = shouldFreeze; } void FreezePointer::setHidePointer(bool shouldHide) { _hidePointer = shouldHide; } void FreezePointer::setSendMotionDeltas(bool shouldSendDeltasOnly) { _motionReceivesDeltas = shouldSendDeltasOnly; } void FreezePointer::connectMouseEvents(const MouseEventFunction& onMouseDown, const MouseEventFunction& onMouseUp) { _onMouseUp = onMouseUp; _onMouseDown = onMouseDown; } void FreezePointer::disconnectMouseEvents() { _onMouseUp = MouseEventFunction(); _onMouseDown = MouseEventFunction(); } void FreezePointer::onMouseDown(wxMouseEvent& ev) { if (_onMouseDown && _capturedWindow) { // The connected mouse up event expects window coordinates, // not coordinates relative to the captured window wxMouseEvent copy(ev); wxPoint windowMousePos = _capturedWindow->ScreenToClient(wxGetMousePosition()); copy.SetX(windowMousePos.x); copy.SetY(windowMousePos.y); _onMouseDown(copy); } } void FreezePointer::onMouseUp(wxMouseEvent& ev) { if (_onMouseUp && _capturedWindow) { // The connected mouse up event expects window coordinates, // not coordinates relative to the captured window wxMouseEvent copy(ev); wxPoint windowMousePos = _capturedWindow->ScreenToClient(wxGetMousePosition()); copy.SetX(windowMousePos.x); copy.SetY(windowMousePos.y); _onMouseUp(copy); } } void FreezePointer::onMouseMotion(wxMouseEvent& ev) { if (!_capturedWindow) return; wxPoint windowMousePos = _capturedWindow->ScreenToClient(wxGetMousePosition()); int dx = windowMousePos.x - _freezePosX; int dy = windowMousePos.y - _freezePosY; if (dx != 0 || dy != 0) { if (_freezePointer) { // Force the mouse cursor to stay where it is _capturedWindow->WarpPointer(_freezePosX, _freezePosY); } else { // Non-freezing, update the reference point for the next delta _freezePosX = windowMousePos.x; _freezePosY = windowMousePos.y; } if (_motionFunction) { if (_motionReceivesDeltas) { _motionFunction(dx, dy, MouseButton::GetStateForMouseEvent(ev)); } else { _motionFunction(windowMousePos.x, windowMousePos.y, MouseButton::GetStateForMouseEvent(ev)); } } } ev.Skip(); } void FreezePointer::onMouseCaptureLost(wxMouseCaptureLostEvent& ev) { if (_captureLostFunction) { _captureLostFunction(); } // Regardless of what the client does, we need to end capture now endCapture(); } } // namespace DarkRadiant-2.5.0/libs/wxutil/FreezePointer.h000066400000000000000000000063161321750546400211360ustar00rootroot00000000000000#pragma once #include #include #include namespace wxutil { class FreezePointer : public wxEvtHandler { public: typedef std::function MotionFunction; typedef std::function CaptureLostFunction; typedef std::function MouseEventFunction; private: // Freeze position relative to captured window int _freezePosX; int _freezePosY; // Whether to lock the cursor in its position bool _freezePointer; // Whether to hide the cursor during capture bool _hidePointer; // Whether the motion callback receives deltas or absolute coords bool _motionReceivesDeltas; MotionFunction _motionFunction; CaptureLostFunction _captureLostFunction; wxWindow* _capturedWindow; MouseEventFunction _onMouseUp; MouseEventFunction _onMouseDown; public: FreezePointer(); /** * Catch any mouse pointer movements. * Any mouse movement will be reported to the given MotionFunction. * The EndMoveFunction will be invoked as soon as the cursor capture is lost. * Defaults to: freeze and hide cursor, send motion deltas only. */ void startCapture(wxWindow* window, const MotionFunction& function, const CaptureLostFunction& endMove); /** * Catch any mouse pointer movements and redirect them to the given window. * Any mouse movement will be reported to the given MotionFunction. * The CaptureLostFunction will be invoked as soon as the cursor capture is lost. * Define flags to control behaviour (hide cursor, etc.) */ void startCapture(wxWindow* window, const MotionFunction& function, const CaptureLostFunction& endMove, bool freezePointer, bool hidePointer, bool motionReceivesDeltas); // Returns true if the mouse is currently captured by this class. bool isCapturing(wxWindow* window); /** * Un-capture the cursor again. If the cursor was frozen, this moves * it back to where it was before. */ void endCapture(); // Activate or deactivate the freeze pointer behaviour // when activated, the cursor will be forced to stay at the current position void setFreezePointer(bool shouldFreeze); // Set this to true to hide the cursor while the capture is active void setHidePointer(bool shouldHide); // Controls whether (during capture) the MotionFunction should receive // deltas (relative to start point) or absolute coordinates. void setSendMotionDeltas(bool shouldSendDeltasOnly); /** * During freeze mouse button events might be eaten by the window. * Use these to enable event propagation. */ void connectMouseEvents(const MouseEventFunction& onMouseDown, const MouseEventFunction& onMouseUp); void disconnectMouseEvents(); private: // During capture we might need to propagate the mouseup and // mousedown events to the client void onMouseUp(wxMouseEvent& ev); void onMouseDown(wxMouseEvent& ev); // The callback to connect to the motion-notify-event void onMouseMotion(wxMouseEvent& ev); void onMouseCaptureLost(wxMouseCaptureLostEvent& ev); }; } // namespace DarkRadiant-2.5.0/libs/wxutil/GLFont.cpp000066400000000000000000000016021321750546400200320ustar00rootroot00000000000000#include "GLFont.h" #include "itextstream.h" #include "igl.h" #include "imodule.h" #include namespace wxutil { GLFont::GLFont(Style style, unsigned int size) : _pixelHeight(0), _ftglFont(NULL) { // Load the locally-provided TTF font file std::string fontpath = module::GlobalModuleRegistry() .getApplicationContext() .getRuntimeDataPath() + "ui/fonts/"; fontpath += style == FONT_SANS ? "FreeSans.ttf" : "FreeMono.ttf"; _ftglFont = FTGL::ftglCreatePixmapFont(fontpath.c_str()); if (_ftglFont) { ftglSetFontFaceSize(_ftglFont,size,0); _pixelHeight = FTGL::ftglGetFontLineHeight(_ftglFont); } else { rError() << "Failed to create FTGLPixmapFont" << std::endl; } } GLFont::~GLFont() { if (_ftglFont) { FTGL::ftglDestroyFont(_ftglFont); _ftglFont = NULL; } } } // namespace DarkRadiant-2.5.0/libs/wxutil/GLFont.h000066400000000000000000000010501321750546400174740ustar00rootroot00000000000000#pragma once #include #include namespace wxutil { class GLFont { private: int _pixelHeight; FTGL::FTGLfont* _ftglFont; public: enum Style { FONT_SANS, // free sans FONT_MONO, // free mono }; // the constructor will allocate the FTGL font GLFont(Style style, unsigned int size); // Destructor frees the FTGL object again ~GLFont(); FTGL::FTGLfont* getFtglFont() { return _ftglFont; } int getPixelHeight() const { return _pixelHeight; } }; typedef std::shared_ptr GLFontPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/GLWidget.cpp000066400000000000000000000037401321750546400203540ustar00rootroot00000000000000#include "GLWidget.h" #include "igl.h" #include "itextstream.h" #include namespace wxutil { const int ATTRIBS [] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0 }; GLWidget::GLWidget(wxWindow *parent, const std::function& renderCallback, const std::string& name) : wxGLCanvas(parent, wxID_ANY, ATTRIBS, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS, wxString(name.c_str(), *wxConvCurrent)), _registered(false), _renderCallback(renderCallback), _privateContext(NULL) { Connect(wxEVT_PAINT, wxPaintEventHandler(GLWidget::OnPaint), NULL, this); } void GLWidget::SetHasPrivateContext(bool hasPrivateContext) { if (hasPrivateContext) { _privateContext = new wxGLContext(this); } else { DestroyPrivateContext(); } } void GLWidget::DestroyPrivateContext() { if (_privateContext != NULL) { _privateContext->UnRef(); _privateContext = NULL; } } GLWidget::~GLWidget() { DestroyPrivateContext(); if (_registered) { GlobalOpenGL().unregisterGLCanvas(this); } } void GLWidget::OnPaint(wxPaintEvent& WXUNUSED(event)) { // Got this check from the wxWidgets sources, they assert the widget to be shown // "although on MSW it works even if the window is still hidden, it doesn't // work in other ports (notably X11-based ones) and documentation mentions // that SetCurrent() can only be called for a shown window, so check for it" if (!IsShownOnScreen()) return; // Make sure this widget is registered if (!_registered) { _registered = true; GlobalOpenGL().registerGLCanvas(this); } // This is required even though dc is not used otherwise. wxPaintDC dc(this); // Grab the contex for this widget if (_privateContext != NULL) { // Use the private context for this widget SetCurrent(*_privateContext); } else { // Use the globally shared context SetCurrent(GlobalOpenGL().getwxGLContext()); } _renderCallback(); SwapBuffers(); } } // namespace DarkRadiant-2.5.0/libs/wxutil/GLWidget.h000066400000000000000000000017241321750546400200210ustar00rootroot00000000000000#pragma once #include #include #include #include #include // greebo: Undo the min max macro definitions coming from a windows header #undef min #undef max namespace wxutil { class GLWidget : public wxGLCanvas { // TRUE, if this GL widget has been registered bool _registered; // The attached client method to invoke to render this view std::function _renderCallback; // Some widgets have their own openGL context, // If it is non-NULL _privateContext will be used. wxGLContext* _privateContext; public: GLWidget(wxWindow *parent, const std::function& renderCallback, const std::string& name); // Call this to enable/disable the private GL context of this widget void SetHasPrivateContext(bool hasPrivateContext); virtual ~GLWidget(); private: void DestroyPrivateContext(); void OnPaint(wxPaintEvent& event); }; typedef std::shared_ptr GLWidgetPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/IConv.h000066400000000000000000000016251321750546400173710ustar00rootroot00000000000000#pragma once #include #include #include namespace wxutil { /** * greebo: This helper class is supposed to help converting * locale strings containing foreign characters like the * French accented chars to UTF-8 encoding. * * I'm not an expert on this, so let's hope I got it right. * Be welcome to suggest any code improvements. */ class IConv { public: /** * greebo: Converts the given string to UTF-8 encoding. * Returns an empty string if the conversion fails. */ static std::string localeToUTF8(const std::string& input) { wxString inp(input); return inp.ToUTF8().data(); } /** * greebo: Converts the given string from UTF-8 to the current locale. * Returns an empty string if the conversion fails. */ static std::string localeFromUTF8(const std::string& input) { return wxString::FromUTF8(input.c_str()).ToStdString(); } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/KeyValueTable.cpp000066400000000000000000000025651321750546400214070ustar00rootroot00000000000000#include "KeyValueTable.h" #include "i18n.h" namespace wxutil { namespace { // Column setup for the list store struct Columns : public wxutil::TreeModel::ColumnRecord { Columns() : key(add(wxutil::TreeModel::Column::String)), value(add(wxutil::TreeModel::Column::String)) {} wxutil::TreeModel::Column key; wxutil::TreeModel::Column value; }; const Columns& COLUMNS() { static const Columns _instance; return _instance; } } KeyValueTable::KeyValueTable(wxWindow* parent) : wxutil::TreeView(parent, TreeModel::Ptr(), wxDV_NO_HEADER | wxDV_SINGLE), _store(new wxutil::TreeModel(COLUMNS(), true)) // list model { AssociateModel(_store.get()); EnableAutoColumnWidthFix(false); // we don't need this // Single visible column, containing the directory/shader name and the icon AppendTextColumn(_("Key"), COLUMNS().key.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE); AppendTextColumn(_("Value"), COLUMNS().value.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE); } void KeyValueTable::Clear() { _store->Clear(); } void KeyValueTable::Append(const std::string& key, const std::string& value) { wxutil::TreeModel::Row row = _store->AddItem(); wxDataViewItemAttr bold; bold.SetBold(true); row[COLUMNS().key] = key; row[COLUMNS().key] = bold; row[COLUMNS().value] = value; row.SendItemAdded(); } } // namespace DarkRadiant-2.5.0/libs/wxutil/KeyValueTable.h000066400000000000000000000012351321750546400210450ustar00rootroot00000000000000#pragma once #include "TreeModel.h" #include "TreeView.h" namespace wxutil { /** * \brief * A treeview widget that shows simple key/value pairs * * This is a convenience widget that allows the quick construction of a table * listing keys and values, without having to create and populate a list store * manually. */ class KeyValueTable : public TreeView { // Our data store wxutil::TreeModel::Ptr _store; public: /// Construct a KeyValueTable KeyValueTable(wxWindow* parent); /// Clear all entries in the table void Clear(); /// Append a key/value pair void Append(const std::string& key, const std::string& value); }; } DarkRadiant-2.5.0/libs/wxutil/Makefile.am000066400000000000000000000040731321750546400202360ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include \ -I$(top_srcdir)/libs \ -DPKGLIBDIR='"$(pkglibdir)"' \ -DPKGDATADIR='"$(pkgdatadir)"' \ $(XML_CFLAGS) \ $(LIBSIGC_CFLAGS) \ $(FTGL_CFLAGS) AM_CXXFLAGS = -fPIC pkglib_LTLIBRARIES = libwxutil.la libwxutil_la_LDFLAGS = -release @PACKAGE_VERSION@ \ $(XML_LIBS) \ $(GLEW_LIBS) \ $(LIBSIGC_LIBS) \ $(WX_LIBS) \ $(GL_LIBS) \ $(FILESYSTEM_LIBS) \ $(GLU_LIBS) \ $(FTGL_LIBS) libwxutil_la_LIBADD = $(top_builddir)/libs/xmlutil/libxmlutil.la \ $(top_builddir)/libs/math/libmath.la \ $(top_builddir)/libs/scene/libscenegraph.la libwxutil_la_SOURCES = ConsoleView.cpp \ FreezePointer.cpp \ GLWidget.cpp \ KeyValueTable.cpp \ PanedPosition.cpp \ VFSTreePopulator.cpp \ WindowPosition.cpp \ window/TransientWindow.cpp \ dialog/Dialog.cpp \ dialog/MessageBox.cpp \ preview/ModelPreview.cpp \ preview/ParticlePreview.cpp \ preview/RenderPreview.cpp \ preview/GuiRenderer.cpp \ preview/GuiView.cpp \ GLFont.cpp \ menu/PopupMenu.cpp \ PathEntry.cpp \ TreeModel.cpp \ TreeView.cpp \ TreeModelFilter.cpp \ ModalProgressDialog.cpp \ MouseToolHandler.cpp \ SerialisableWidgets.cpp \ SourceView.cpp \ clipboard.cpp \ DirChooser.cpp \ FileChooser.cpp DarkRadiant-2.5.0/libs/wxutil/MenuItemAccelerator.cpp000066400000000000000000000036771321750546400226070ustar00rootroot00000000000000#if 0 #include "MenuItemAccelerator.h" #include #include #include #include namespace gtkutil { TextMenuItemBase::TextMenuItemBase(const std::string& label, const std::string& accelLabel, const Glib::RefPtr& icon) : _label(NULL), _accel(NULL), _iconImage(NULL) { // Create the text. This consists of the icon, the label string (left- // aligned) and the accelerator string (right-aligned). _hbox = Gtk::manage(new Gtk::HBox(false, 4)); // Try to pack in icon ONLY if it is valid if (icon) { _iconImage = Gtk::manage(new Gtk::Image(icon)); _hbox->pack_start(*_iconImage, false, false, 0); } _label = Gtk::manage(new Gtk::Label(label, true)); _accel = Gtk::manage(new Gtk::Label(accelLabel, true)); _hbox->pack_start(*_label, false, false, 0); _hbox->pack_start(*Gtk::manage(new Gtk::Label(" ")), false, false, 12); _hbox->pack_end(*_accel, false, false, 0); } void TextMenuItemBase::setLabel(const std::string& newLabel) { if (_label != NULL) { _label->set_markup_with_mnemonic(newLabel); } } void TextMenuItemBase::setAccelerator(const std::string& newAccel) { if (_accel != NULL) { _accel->set_markup_with_mnemonic(newAccel); } } void TextMenuItemBase::setIcon(const Glib::RefPtr& icon) { if (_iconImage != NULL) { _iconImage->set(icon); } } TextMenuItemAccelerator::TextMenuItemAccelerator(const std::string& label, const std::string& accelLabel, const Glib::RefPtr& icon) : TextMenuItemBase(label, accelLabel, icon), Gtk::MenuItem() { // Pack the label structure into the MenuItem add(*_hbox); } TextToggleMenuItemAccelerator::TextToggleMenuItemAccelerator(const std::string& label, const std::string& accelLabel, const Glib::RefPtr& icon) : TextMenuItemBase(label, accelLabel, icon), Gtk::CheckMenuItem() { // Pack the label structure into the MenuItem add(*_hbox); } } // namespace gtkutil #endif DarkRadiant-2.5.0/libs/wxutil/MenuItemAccelerator.h000066400000000000000000000034331321750546400222420ustar00rootroot00000000000000#ifndef MENUITEMACCELERATOR_H_ #define MENUITEMACCELERATOR_H_ #include #include #include namespace Gtk { class Label; class Image; class HBox; } namespace gtkutil { class TextMenuItemBase { protected: Gtk::HBox* _hbox; // The corresponding widget Gtk::Label* _label; // The corresponding widget Gtk::Label* _accel; // The corresponding image widget Gtk::Image* _iconImage; TextMenuItemBase(const std::string& label, const std::string& accelLabel, const Glib::RefPtr& icon); public: // destructor virtual ~TextMenuItemBase() {} // Changes the label text of the given menu item void setLabel(const std::string& newLabel); // Changes the accelerator text of this menutem void setAccelerator(const std::string& newAccel); // Change the icon void setIcon(const Glib::RefPtr& icon); }; class TextMenuItemAccelerator : public TextMenuItemBase, public Gtk::MenuItem { public: /** * Construct a menu item with the given label, accelerator and icon. The * icon may be the empty string if no icon is required. */ TextMenuItemAccelerator(const std::string& label, const std::string& accelLabel, const Glib::RefPtr& icon); // destructor virtual ~TextMenuItemAccelerator() {} }; class TextToggleMenuItemAccelerator : public TextMenuItemBase, public Gtk::CheckMenuItem { public: /** * Construct a menu item with the given label, accelerator and icon. The * icon may be the empty string if no icon is required. */ TextToggleMenuItemAccelerator(const std::string& label, const std::string& accelLabel, const Glib::RefPtr& icon); // destructor virtual ~TextToggleMenuItemAccelerator() {} }; } #endif /*MENUITEMACCELERATOR_H_*/ DarkRadiant-2.5.0/libs/wxutil/ModalProgressDialog.cpp000066400000000000000000000017431321750546400226100ustar00rootroot00000000000000#include "ModalProgressDialog.h" #include "i18n.h" #include "imainframe.h" #include namespace wxutil { ModalProgressDialog::ModalProgressDialog(const std::string& title, wxWindow* parent) : wxProgressDialog(title, "", 100, parent != nullptr ? parent : GlobalMainFrame().getWxTopLevelWindow(), wxPD_CAN_ABORT | wxPD_APP_MODAL | wxPD_AUTO_HIDE) {} void ModalProgressDialog::setText(const std::string& text) { // If the aborted flag is set, throw an exception here if (WasCancelled()) { throw OperationAbortedException(_("Operation cancelled by user")); } Pulse(text); } void ModalProgressDialog::setTextAndFraction(const std::string& text, double fraction) { if (WasCancelled()) { throw OperationAbortedException(_("Operation cancelled by user")); } if (fraction < 0) { fraction = 0.0; } else if (fraction > 1.0) { fraction = 1.0; } int newValue = static_cast(fraction * 100); Update(newValue, text); } } // namespace DarkRadiant-2.5.0/libs/wxutil/ModalProgressDialog.h000066400000000000000000000022541321750546400222530ustar00rootroot00000000000000#pragma once #include #include #include namespace wxutil { class ModalProgressDialog : public wxProgressDialog { public: /** * Constructor accepts window to be modal for and the dialog title. */ ModalProgressDialog(const std::string& title, wxWindow* parent = nullptr); /** * Exception thrown when cancel button is pressed. */ struct OperationAbortedException : public std::runtime_error { OperationAbortedException(const std::string& what) : std::runtime_error(what) {} }; /** * Set the text to display in the label, and pulse the progress bar. If the * user has clicked the Cancel button since the last update, this method * will throw an exception to indicate an aborted operation. */ void setText(const std::string& text); /** * Set the text to display in the label, and the completed fraction of the progress bar. * If the user has clicked the Cancel button since the last update, this method * will throw an exception to indicate an aborted operation. */ void setTextAndFraction(const std::string& text, double fraction); }; typedef std::shared_ptr ModalProgressDialogPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/Modifier.h000066400000000000000000000070041321750546400201060ustar00rootroot00000000000000#pragma once #include "i18n.h" #include #include #include #include "xmlutil/Node.h" #include "string/split.h" namespace wxutil { class Modifier { #define ATTR_MODIFIER ("modifiers") #define MODIFIERSTR_SHIFT "SHIFT" #define MODIFIERSTR_ALT "ALT" #define MODIFIERSTR_CONTROL "CONTROL" public: enum Flags { NONE = 0, /* Used by wxutil::MouseButton LEFT = 1 << 1, RIGHT = 1 << 2, MIDDLE = 1 << 3, AUX1 = 1 << 4, AUX2 = 1 << 5, */ SHIFT = 1 << 6, CONTROL = 1 << 7, ALT = 1 << 8 }; // Translates the given wxMouseEvent to modifier flags. static unsigned int GetStateForMouseEvent(wxMouseEvent& ev) { unsigned int state = NONE; if (ev.ShiftDown()) { state |= SHIFT; } else { state &= ~SHIFT; } if (ev.ControlDown()) { state |= CONTROL; } else { state &= ~CONTROL; } if (ev.AltDown()) { state |= ALT; } else { state &= ~ALT; } return state; } // Translates the given wxKeyEvent to modifier flags. static unsigned int GetStateForKeyEvent(wxKeyEvent& ev) { unsigned int state = NONE; if (ev.ShiftDown()) { state |= SHIFT; } else { state &= ~SHIFT; } if (ev.ControlDown()) { state |= CONTROL; } else { state &= ~CONTROL; } if (ev.AltDown()) { state |= ALT; } else { state &= ~ALT; } return state; } // Converts e.g. "SHIFT+ALT" to flags static unsigned int GetStateFromModifierString(const std::string& modifierStr) { unsigned int state = NONE; std::vector parts; string::split(parts, modifierStr, "+"); for (const std::string& mod : parts) { if (mod == MODIFIERSTR_SHIFT) { state |= SHIFT; continue; } if (mod == MODIFIERSTR_ALT) { state |= ALT; continue; } if (mod == MODIFIERSTR_CONTROL) { state |= CONTROL; continue; } } return state; } static unsigned int LoadFromNode(const xml::Node& node) { return GetStateFromModifierString(node.getAttributeValue(ATTR_MODIFIER)); } static std::string GetModifierString(unsigned int state) { std::string mod = ""; if ((state & ALT) != 0) mod += mod.empty() ? MODIFIERSTR_ALT : "+" MODIFIERSTR_ALT; if ((state & CONTROL) != 0) mod += mod.empty() ? MODIFIERSTR_CONTROL : "+" MODIFIERSTR_CONTROL; if ((state & SHIFT) != 0) mod += mod.empty() ? MODIFIERSTR_SHIFT : "+" MODIFIERSTR_SHIFT; return mod; } static std::string GetModifierStringForMenu(unsigned int state, const std::string& separator = "+") { std::string mod = ""; if ((state & ALT) != 0) mod += mod.empty() ? _("Alt") : separator + _("Alt"); if ((state & CONTROL) != 0) mod += mod.empty() ? _("Ctrl") : separator + _("Ctrl"); if ((state & SHIFT) != 0) mod += mod.empty() ? _("Shift") : separator + _("Shift"); return mod; } static void SaveToNode(unsigned int state, xml::Node& node) { node.setAttributeValue(ATTR_MODIFIER, GetModifierString(state)); } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/MouseButton.h000066400000000000000000000077141321750546400206440ustar00rootroot00000000000000#pragma once #include #include "Modifier.h" namespace wxutil { class MouseButton { #define ATTR_BUTTON ("button") #define BUTTONSTR_LMB "LMB" #define BUTTONSTR_MMB "MMB" #define BUTTONSTR_RMB "RMB" #define BUTTONSTR_AUX1 "AUX1" #define BUTTONSTR_AUX2 "AUX2" public: enum ButtonFlags { NONE = 0, LEFT = 1 << 1, RIGHT = 1 << 2, MIDDLE = 1 << 3, AUX1 = 1 << 4, AUX2 = 1 << 5, ALL_BUTTON_MASK = (LEFT | RIGHT | MIDDLE | AUX1 | AUX2), /* Used by wxutil::Modifier SHIFT = 1 << 6, CONTROL = 1 << 7, ALT = 1 << 8 */ }; // Returns a bit mask corresponding to a single mouse button CHANGE event // Only one mouse button will be marked in this bit mask since only one // button can be changed at a given time. The keyboard modifier state // is added to the bit mask in the usual way, the according bit flag will // be set if the modifier key is currently held. This also checks the flag // if a double-click event is generated by wxWidgets static unsigned int GetButtonStateChangeForMouseEvent(wxMouseEvent& ev) { unsigned int newState = NONE; if (ev.LeftDown() || ev.LeftUp() || ev.LeftDClick()) { newState |= LEFT; } else if (ev.RightDown() || ev.RightUp() || ev.RightDClick()) { newState |= RIGHT; } else if (ev.MiddleDown() || ev.MiddleUp() || ev.MiddleDClick()) { newState |= MIDDLE; } #if wxCHECK_VERSION(3,0,0) else if (ev.Aux1Down() || ev.Aux1Up() || ev.Aux1DClick()) { newState |= AUX1; } else if (ev.Aux2Down() || ev.Aux2Up() || ev.Aux2DClick()) { newState |= AUX2; } #endif // Add the modifier key state as usual newState |= Modifier::GetStateForMouseEvent(ev); return newState; } // Returns a bit mask representing the current mouse and modifier state // Since it represents the current state it's possible to find multiple // mouse buttons being held at the same time. static unsigned int GetStateForMouseEvent(wxMouseEvent& ev) { unsigned int newState = NONE; if (ev.LeftIsDown()) { newState |= LEFT; } else { newState &= ~LEFT; } if (ev.RightIsDown()) { newState |= RIGHT; } else { newState &= ~RIGHT; } if (ev.MiddleIsDown()) { newState |= MIDDLE; } else { newState &= ~MIDDLE; } #if wxCHECK_VERSION(3,0,0) if (ev.Aux1IsDown()) { newState |= AUX1; } else { newState &= ~AUX1; } if (ev.Aux2IsDown()) { newState |= AUX2; } else { newState &= ~AUX2; } #endif newState |= Modifier::GetStateForMouseEvent(ev); return newState; } // Parses the node's attributes to the corresponding flag static unsigned int LoadFromNode(const xml::Node& node) { return GetStateFromString(node.getAttributeValue(ATTR_BUTTON)); } // Converts "LMB" to the corresponding flag static unsigned int GetStateFromString(const std::string& str) { if (str == BUTTONSTR_LMB) return LEFT; if (str == BUTTONSTR_RMB) return RIGHT; if (str == BUTTONSTR_MMB) return MIDDLE; if (str == BUTTONSTR_AUX1) return AUX1; if (str == BUTTONSTR_AUX2) return AUX2; return NONE; } static std::string GetButtonString(unsigned int state) { if ((state & LEFT) != 0) return BUTTONSTR_LMB; if ((state & RIGHT) != 0) return BUTTONSTR_RMB; if ((state & MIDDLE) != 0) return BUTTONSTR_MMB; if ((state & AUX1) != 0) return BUTTONSTR_AUX1; if ((state & AUX2) != 0) return BUTTONSTR_AUX2; return ""; } // Saves the button flags to the given node static void SaveToNode(unsigned int state, xml::Node& node) { node.setAttributeValue(ATTR_BUTTON, GetButtonString(state)); } static void ForeachButton(const std::function& func) { func(LEFT); func(MIDDLE); func(RIGHT); func(AUX1); func(AUX2); } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/MouseToolHandler.cpp000066400000000000000000000250521321750546400221320ustar00rootroot00000000000000#include "MouseToolHandler.h" #include "itextstream.h" #include "MouseButton.h" #include "imainframe.h" namespace wxutil { MouseToolHandler::MouseToolHandler(ui::IMouseToolGroup::Type type) : _type(type) {} void MouseToolHandler::onGLMouseButtonPress(wxMouseEvent& ev) { // Filter out the button that was triggering this event unsigned int state = wxutil::MouseButton::GetButtonStateChangeForMouseEvent(ev); // For the active tool mapping, we need just the mouse button without modifiers unsigned int button = wxutil::MouseButton::GetButtonStateChangeForMouseEvent(ev) & wxutil::MouseButton::ALL_BUTTON_MASK; ui::MouseToolPtr activeToolToBeCleared; if (_activeMouseTools.find(button) != _activeMouseTools.end()) { // Send the click event to the currently active tool. Some tools stay active after // mouse up and might choose to end their action along with the mouse down event // The FreeMoveTool with toggle mode activated is such an example. ui::MouseToolPtr tool = _activeMouseTools[button]; switch (processMouseDownEvent(tool, Vector2(ev.GetX(), ev.GetY()))) { case ui::MouseTool::Result::Finished: // Tool is done activeToolToBeCleared = tool; break; case ui::MouseTool::Result::Activated: case ui::MouseTool::Result::Continued: handleViewRefresh(tool->getRefreshMode()); break; case ui::MouseTool::Result::Ignored: break; }; } // Now consider all the available tools and send the event // Currently active tools are handled above, so don't send the event again // Get all mouse tools mapped to this button/modifier combination ui::MouseToolStack toolStack = GlobalMouseToolManager().getMouseToolsForEvent(_type, state); // Remove all active mouse tools from this stack toolStack.remove_if(std::bind(&MouseToolHandler::toolIsActive, this, std::placeholders::_1)); // The candidates have been trimmed, so let's clear out any pending tools if (activeToolToBeCleared) { clearActiveMouseTool(activeToolToBeCleared); activeToolToBeCleared.reset(); } // Check which one of the candidates responds to the mousedown event ui::MouseToolPtr activeTool; for (ui::MouseToolPtr tool : toolStack) { // Ask each tool to handle the event ui::MouseTool::Result result = processMouseDownEvent(tool, Vector2(ev.GetX(), ev.GetY())); if (result != ui::MouseTool::Result::Ignored && result != ui::MouseTool::Result::Finished) { // This tool is now activated activeTool = tool; break; } } if (!activeTool) { return; } // Store this tool in our map _activeMouseTools[button] = activeTool; unsigned int pointerMode = activeTool->getPointerMode(); // Check if the mousetool requires pointer freeze if ((pointerMode & ui::MouseTool::PointerMode::Capture) != 0) { startCapture(activeTool); } if (!_escapeListener) { // Register a hook to capture the ESC key during the active phase _escapeListener.reset(new KeyEventFilter(WXK_ESCAPE, std::bind(&MouseToolHandler::handleEscapeKeyPress, this))); } } void MouseToolHandler::onGLMouseMove(wxMouseEvent& ev) { // Skip this event if any of the active mouse tools is in capture mode // the call here still arrives on OSX even during capture for (const ActiveMouseTools::value_type& pair : _activeMouseTools) { if (pair.second->getPointerMode() & ui::MouseTool::PointerMode::Capture) { return; // skip } } Vector2 position(ev.GetX(), ev.GetY()); sendMoveEventToInactiveTools(ev.GetX(), ev.GetY()); // Pass the move event to all active tools and clear the ones that are done for (ActiveMouseTools::const_iterator i = _activeMouseTools.begin(); i != _activeMouseTools.end();) { ui::MouseToolPtr tool = (i++)->second; // Ask the active mousetool to handle this event switch (processMouseMoveEvent(tool, ev.GetX(), ev.GetY())) { case ui::MouseTool::Result::Finished: // Tool is done clearActiveMouseTool(tool); handleViewRefresh(tool->getRefreshMode()); break; case ui::MouseTool::Result::Activated: case ui::MouseTool::Result::Continued: handleViewRefresh(tool->getRefreshMode()); break; case ui::MouseTool::Result::Ignored: break; }; } } void MouseToolHandler::onGLCapturedMouseMove(int x, int y, unsigned int mouseState) { sendMoveEventToInactiveTools(x, y); for (ActiveMouseTools::const_iterator i = _activeMouseTools.begin(); i != _activeMouseTools.end();) { ui::MouseToolPtr tool = (i++)->second; switch (processMouseMoveEvent(tool, x, y)) { case ui::MouseTool::Result::Finished: // Tool is done clearActiveMouseTool(tool); handleViewRefresh(tool->getRefreshMode()); break; case ui::MouseTool::Result::Activated: case ui::MouseTool::Result::Continued: handleViewRefresh(tool->getRefreshMode()); break; case ui::MouseTool::Result::Ignored: break; }; } } bool MouseToolHandler::toolIsActive(const ui::MouseToolPtr& tool) { // The active tools don't count for (const ActiveMouseTools::value_type& i : _activeMouseTools) { if (i.second == tool) return true; } return false; } void MouseToolHandler::sendMoveEventToInactiveTools(int x, int y) { // Send mouse move events to all tools that want them GlobalMouseToolManager().getGroup(_type).foreachMouseTool([&] (const ui::MouseToolPtr& tool) { if (!tool->alwaysReceivesMoveEvents()) return; // The active tools don't receive this event a second time if (toolIsActive(tool)) return; processMouseMoveEvent(tool, x, y); }); } void MouseToolHandler::onGLMouseButtonRelease(wxMouseEvent& ev) { if (_activeMouseTools.empty()) return; // Determine the button that has been released unsigned int state = wxutil::MouseButton::GetButtonStateChangeForMouseEvent(ev) & wxutil::MouseButton::ALL_BUTTON_MASK; ActiveMouseTools::const_iterator i = _activeMouseTools.find(state); if (i != _activeMouseTools.end()) { // Ask the active mousetool to handle this event ui::MouseTool::Result result = processMouseUpEvent(i->second, Vector2(ev.GetX(), ev.GetY())); if (result == ui::MouseTool::Result::Finished) { clearActiveMouseTool(i->second); } } } void MouseToolHandler::handleCaptureLost(const ui::MouseToolPtr& tool) { if (!tool) return; if (tool->getPointerMode() & ui::MouseTool::PointerMode::Capture) { // Send the capture lost event, which should make the tool cancel the operation tool->onMouseCaptureLost(getInteractiveView()); handleViewRefresh(tool->getRefreshMode()); // Clear the tool when the capture is lost clearActiveMouseTool(tool); } } void MouseToolHandler::clearActiveMouseTool(const ui::MouseToolPtr& tool) { unsigned int previousPointerMode = tool->getPointerMode(); for (ActiveMouseTools::const_iterator i = _activeMouseTools.begin(); i != _activeMouseTools.end(); ++i) { if (i->second == tool) { _activeMouseTools.erase(i); break; } } // Check if any mouse tools still require capture mode unsigned int remainingPointerMode = ui::MouseTool::PointerMode::Normal; for (const ActiveMouseTools::value_type& pair : _activeMouseTools) { remainingPointerMode |= pair.second->getPointerMode(); } // If no more freezing mouse tools: release the mouse cursor again if (previousPointerMode & ui::MouseTool::PointerMode::Capture && !(remainingPointerMode & ui::MouseTool::PointerMode::Capture)) { endCapture(); } // Reset the escape listener if this was the last active tool if (_activeMouseTools.empty()) { _escapeListener.reset(); } } void MouseToolHandler::clearActiveMouseTool(unsigned int button) { if (_activeMouseTools.find(button) != _activeMouseTools.end()) { clearActiveMouseTool(_activeMouseTools[button]); } } void MouseToolHandler::clearActiveMouseTools() { // Reset the escape listener _escapeListener.reset(); if (_activeMouseTools.empty()) { return; } // Check the capture mode unsigned int pointerMode = ui::MouseTool::PointerMode::Normal; // Freezing mouse tools: release the mouse cursor again for (ActiveMouseTools::const_iterator i = _activeMouseTools.begin(); i != _activeMouseTools.end();) { pointerMode |= i->second->getPointerMode(); // Tool is done _activeMouseTools.erase(i++); } // If any of the active tools was capturing, end this now if (pointerMode & ui::MouseTool::PointerMode::Capture) { endCapture(); } } KeyEventFilter::Result MouseToolHandler::handleEscapeKeyPress() { // Key will slip through unless one tool reports having it processed KeyEventFilter::Result result = KeyEventFilter::Result::KeyIgnored; for (ActiveMouseTools::const_iterator i = _activeMouseTools.begin(); i != _activeMouseTools.end();) { ui::MouseToolPtr tool = (i++)->second; switch (tool->onCancel(getInteractiveView())) { // Any MouseTool returning the Finished signal will be removed case ui::MouseTool::Result::Finished: // Tool is done clearActiveMouseTool(tool); handleViewRefresh(tool->getRefreshMode()); result = KeyEventFilter::Result::KeyProcessed; break; case ui::MouseTool::Result::Activated: case ui::MouseTool::Result::Continued: case ui::MouseTool::Result::Ignored: break; }; } return result; } void MouseToolHandler::handleViewRefresh(unsigned int flags) { if (flags & ui::MouseTool::RefreshMode::AllViews) { // Pass the signal to the mainframe GlobalMainFrame().updateAllWindows((flags & ui::MouseTool::RefreshMode::Force) != 0); } else if (flags & ui::MouseTool::RefreshMode::ActiveView) { if (flags & ui::MouseTool::RefreshMode::Force) { getInteractiveView().forceRedraw(); } else { getInteractiveView().queueDraw(); } } } } DarkRadiant-2.5.0/libs/wxutil/MouseToolHandler.h000066400000000000000000000036651321750546400216050ustar00rootroot00000000000000#pragma once #include "imousetool.h" #include "imousetoolmanager.h" #include #include #include "event/KeyEventFilter.h" namespace wxutil { /** * A helper class containing the mousetool handling logic. * Interactive Views can have several active mouse tools * at the same time, need support for ESC/cancelling the operations * as well as capturing the mouse pointer. */ class MouseToolHandler { private: ui::IMouseToolGroup::Type _type; protected: // One active tool is possible for each button typedef std::map ActiveMouseTools; ActiveMouseTools _activeMouseTools; private: // During active phases we listen for ESC keys to cancel the operation KeyEventFilterPtr _escapeListener; public: MouseToolHandler(ui::IMouseToolGroup::Type type); void onGLMouseButtonPress(wxMouseEvent& ev); void onGLMouseButtonRelease(wxMouseEvent& ev); void onGLMouseMove(wxMouseEvent& ev); void onGLCapturedMouseMove(int x, int y, unsigned int mouseState); void handleCaptureLost(const ui::MouseToolPtr& tool); protected: virtual ui::MouseTool::Result processMouseDownEvent(const ui::MouseToolPtr& tool, const Vector2& point) = 0; virtual ui::MouseTool::Result processMouseUpEvent(const ui::MouseToolPtr& tool, const Vector2& point) = 0; virtual ui::MouseTool::Result processMouseMoveEvent(const ui::MouseToolPtr& tool, int x, int y) = 0; virtual void startCapture(const ui::MouseToolPtr& tool) = 0; virtual void endCapture() = 0; virtual IInteractiveView& getInteractiveView() = 0; void clearActiveMouseTool(const ui::MouseToolPtr& tool); void clearActiveMouseTool(unsigned int button); void clearActiveMouseTools(); private: void sendMoveEventToInactiveTools(int x, int y); void handleViewRefresh(unsigned int flags); bool toolIsActive(const ui::MouseToolPtr& tool); KeyEventFilter::Result handleEscapeKeyPress(); }; } DarkRadiant-2.5.0/libs/wxutil/MultiMonitor.h000066400000000000000000000026741321750546400210220ustar00rootroot00000000000000#pragma once #include #include "itextstream.h" namespace wxutil { /** * greebo: This class acts as container for several * multi-monitor-related functions. Use the getMonitor() method * to acquire the screen dimensions of the given screen. */ class MultiMonitor { public: /** * Returns the number of monitors of the default screen. */ static unsigned int getNumMonitors() { // Get and return the number of monitors return wxDisplay::GetCount(); } /** * Returns the screen rectangle of the screen with the given index. * The first screen is always present and has the index 0. */ static wxRect getMonitor(int monitorNum) { wxDisplay display(monitorNum); return display.GetGeometry(); } /** * greebo: Returns the rectangle (width/height) for the monitor * which the given window is displayed on. */ static wxRect getMonitorForWindow(wxWindow* window) { // Retrieve the screen wxDisplay display(wxDisplay::GetFromWindow(window)); return display.GetGeometry(); } static void printMonitorInfo() { rMessage() << "Default screen has " << getNumMonitors() << " monitors." << std::endl; // detect multiple monitors for (unsigned int j = 0; j < getNumMonitors(); j++) { wxRect geom = getMonitor(j); rMessage() << "Monitor " << j << " geometry: " << geom.GetWidth() << "x" << geom.GetHeight() << " at " << geom.GetX() << ", " << geom.GetY() << std::endl; } } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/NonModalEntry.h000066400000000000000000000045661321750546400211130ustar00rootroot00000000000000#pragma once #include #include namespace wxutil { /** * A special entry field keeping track of focus events and editing status. * The attached callbacks ("apply" and "cancel") are automatically invoked * where appropriate (losing focus, pressing enter or escape, etc.) */ class NonModalEntry : public wxTextCtrl { public: typedef std::function ApplyCallback; typedef std::function CancelCallback; typedef std::function ChangedCallback; private: bool _editing; ApplyCallback _apply; CancelCallback _cancel; ChangedCallback _changed; bool _giveUpFocusOnApplyOrCancel; public: NonModalEntry(wxWindow* parent, const ApplyCallback& apply, const CancelCallback& cancel, const ChangedCallback& changed = 0, bool giveUpFocusOnApplyOrCancel = true) : wxTextCtrl(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER), _editing(false), _apply(apply), _cancel(cancel), _changed(changed), _giveUpFocusOnApplyOrCancel(giveUpFocusOnApplyOrCancel) { Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(NonModalEntry::onGetFocus), NULL, this); Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(NonModalEntry::onLoseFocus), NULL, this); Connect(wxEVT_TEXT, wxCommandEventHandler(NonModalEntry::onTextChanged), NULL, this); Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(NonModalEntry::onTextEntryKeyPressed), NULL, this); } protected: void onGetFocus(wxFocusEvent& ev) { _editing = false; ev.Skip(); } void onLoseFocus(wxFocusEvent& ev) { if (_editing && IsShown()) { if (_apply) { _apply(); } } _editing = false; ev.Skip(); } void onTextChanged(wxCommandEvent& ev) { _editing = true; if (IsShown()) { if (_changed) { _changed(); } } ev.Skip(); } void onTextEntryKeyPressed(wxKeyEvent& ev) { if (ev.GetKeyCode() == WXK_RETURN || ev.GetKeyCode() == WXK_ESCAPE) { if (ev.GetKeyCode() == WXK_RETURN) { if (_apply) { _apply(); } } else { if (_cancel) { _cancel(); } } _editing = false; if (_giveUpFocusOnApplyOrCancel) { wxWindow* parent = GetParent(); while (parent != NULL && parent->GetParent() != NULL) { parent = parent->GetParent(); } if (parent != NULL) { parent->SetFocus(); } } return; } ev.Skip(); } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/PanedPosition.cpp000066400000000000000000000025331321750546400214610ustar00rootroot00000000000000#include "PanedPosition.h" #include "string/convert.h" #include "iregistry.h" #include namespace { const int DEFAULT_POSITION = 200; } namespace wxutil { PanedPosition::PanedPosition() : _position(DEFAULT_POSITION) {} PanedPosition::~PanedPosition() { disconnect(); } void PanedPosition::connect(wxSplitterWindow* paned) { wxASSERT(_paned == NULL); // detect weirdness _paned = paned; _paned->Bind(wxEVT_SPLITTER_SASH_POS_CHANGED, &PanedPosition::onPositionChange, this); } void PanedPosition::disconnect() { if (_paned) { _paned->Unbind(wxEVT_SPLITTER_SASH_POS_CHANGED, &PanedPosition::onPositionChange, this); _paned.Release(); } } void PanedPosition::setPosition(int position) { _position = position; if (_paned) { _paned->SetSashPosition(_position, true); } } void PanedPosition::saveToPath(const std::string& path) { GlobalRegistry().setAttribute(path, "position", string::to_string(_position)); } void PanedPosition::loadFromPath(const std::string& path) { setPosition( string::convert(GlobalRegistry().getAttribute(path, "position")) ); } void PanedPosition::onPositionChange(wxSplitterEvent& ev) { if (_paned) { _position = _paned->GetSashPosition(); } } } // namespace DarkRadiant-2.5.0/libs/wxutil/PanedPosition.h000066400000000000000000000021731321750546400211260ustar00rootroot00000000000000#pragma once #include #include class wxSplitterWindow; class wxSplitterEvent; namespace wxutil { /** * greebo: A PanedPosition object keeps track of the divider position of * a wxSplitterWindow object. * * Use the connect() method to connect a wxSplitterWindow to this object. * * Use the loadFromNode() and saveToNode() methods to save the internal * size info into the given xml::Node * * This is used in various places (mainframe, entity inspector) to store * the size/position of the paned views on shutdown. */ class PanedPosition : public wxEvtHandler { private: // The position of this object int _position; // The connected paned container wxWeakRef _paned; public: PanedPosition(); ~PanedPosition(); // Connect the passed splitter window to this object void connect(wxSplitterWindow* paned); void disconnect(); void setPosition(int position); void saveToPath(const std::string& path); void loadFromPath(const std::string& path); private: // The callback that gets invoked on position change void onPositionChange(wxSplitterEvent& ev); }; } // namespace DarkRadiant-2.5.0/libs/wxutil/PathEntry.cpp000066400000000000000000000067021321750546400206250ustar00rootroot00000000000000#include "PathEntry.h" #include "iregistry.h" #include "i18n.h" #include #include #include #include #include "FileChooser.h" #include "DirChooser.h" #include "os/path.h" namespace wxutil { PathEntry::PathEntry(wxWindow* parent, const std::string& fileType, bool open) : PathEntry(parent, false, open, fileType, std::string()) {} PathEntry::PathEntry(wxWindow* parent, bool foldersOnly) : PathEntry(parent, foldersOnly, true, std::string(), std::string()) {} PathEntry::PathEntry(wxWindow* parent, const char* fileType, bool open) : PathEntry(parent, std::string(fileType), open, std::string()) {} PathEntry::PathEntry(wxWindow* parent, const std::string& fileType, bool open, const std::string& defaultExt) : PathEntry(parent, false, open, fileType, defaultExt) {} PathEntry::PathEntry(wxWindow* parent, bool foldersOnly, bool open, const std::string& fileType, const std::string& defaultExt) : wxPanel(parent, wxID_ANY), _fileType(fileType), _defaultExt(defaultExt), _open(open) { SetSizer(new wxBoxSizer(wxHORIZONTAL)); // path entry _entry = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); _entry->Bind(wxEVT_TEXT_ENTER, [&](wxCommandEvent& ev) { // Fire the PathEntryChanged event on enter wxQueueEvent(_entry->GetEventHandler(), new wxCommandEvent(EV_PATH_ENTRY_CHANGED, _entry->GetId())); }); // Generate browse button image std::string fullFileName = GlobalRegistry().get(RKEY_BITMAPS_PATH) + "ellipsis.png"; wxImage image(fullFileName); // browse button _button = new wxBitmapButton(this, wxID_ANY, wxBitmap(image)); // Connect the button if (foldersOnly) { _button->Connect(wxEVT_BUTTON, wxCommandEventHandler(PathEntry::onBrowseFolders), NULL, this); } else { _button->Connect(wxEVT_BUTTON, wxCommandEventHandler(PathEntry::onBrowseFiles), NULL, this); } GetSizer()->Add(_entry, 1, wxEXPAND | wxRIGHT, 6); GetSizer()->Add(_button, 0, wxEXPAND); } void PathEntry::setValue(const std::string& val) { _entry->SetValue(val); } std::string PathEntry::getValue() const { return _entry->GetValue().ToStdString(); } wxTextCtrl* PathEntry::getEntryWidget() { return _entry; } void PathEntry::setDefaultExtension(const std::string& defaultExt) { _defaultExt = defaultExt; } void PathEntry::onBrowseFiles(wxCommandEvent& ev) { wxWindow* topLevel = wxGetTopLevelParent(this); FileChooser fileChooser(topLevel, _("Choose File"), _open, _fileType, _defaultExt); fileChooser.setCurrentPath(getValue()); std::string filename = fileChooser.display(); topLevel->Show(); if (!filename.empty()) { setValue(filename); // Fire the PathEntryChanged event wxQueueEvent(GetEventHandler(), new wxCommandEvent(EV_PATH_ENTRY_CHANGED, _entry->GetId())); } } void PathEntry::onBrowseFolders(wxCommandEvent& ev) { wxWindow* topLevel = wxGetTopLevelParent(this); DirChooser dirChooser(topLevel, _("Choose Directory")); std::string curEntry = getValue(); if (!path_is_absolute(curEntry.c_str())) { curEntry.clear(); } dirChooser.setCurrentPath(curEntry); std::string filename = dirChooser.display(); topLevel->Show(); if (!filename.empty()) { setValue(filename); // Fire the PathEntryChanged event wxQueueEvent(GetEventHandler(), new wxCommandEvent(EV_PATH_ENTRY_CHANGED, _entry->GetId())); } } wxDEFINE_EVENT(EV_PATH_ENTRY_CHANGED, wxCommandEvent); } // namespace wxutil DarkRadiant-2.5.0/libs/wxutil/PathEntry.h000066400000000000000000000053321321750546400202700ustar00rootroot00000000000000#pragma once #include class wxButton; class wxTextCtrl; namespace wxutil { /** * greebo: A PathEntry can be used to edit a path to a file or a directory. * It behaves like an ordinary text entry box, featuring a button to the right * which opens a FileChooser dialog when clicked. */ class PathEntry : public wxPanel { protected: // Browse button wxButton* _button; // The text entry box wxTextCtrl* _entry; // The filetype determining the available filters in the FileChooser std::string _fileType; // The extension to use as default (preselects the corresponding filter // in the FileChooser dialog std::string _defaultExt; // Whether we're opening the FileChooser in open or save mode bool _open; private: // Private shared constructor PathEntry(wxWindow* parent, bool foldersOnly, bool open, const std::string& fileType, const std::string& defaultExt); public: /** * Construct a new Path Entry. Use the boolean * to specify whether this widget should be used to * browser for folders or files. */ explicit PathEntry(wxWindow* parent, bool foldersOnly); /** * Construct a new Path Entry which browses for files * matching the given filetype. The filetype is used to populate * the filter dropdown with the corresponding options registered * in the FileTypeRegistry. * The open flag indicates whether the FileChooser should be in * open or save mode. */ explicit PathEntry(wxWindow* parent, const std::string& fileType, bool open); // Same constructor as above but accepting const char* to avoid // the char* being converted to bool even though it's marked explicit explicit PathEntry(wxWindow* parent, const char* fileType, bool open); /** * Construct a new Path Entry which browses for files * matching the given filetype. The filetype is used to populate * the filter dropdown with the corresponding options registered * in the FileTypeRegistry. The dropdown matching the default extension * is preselected by default. The open flag indicates whether the * FileChooser should be in open or save mode. */ explicit PathEntry(wxWindow* parent, const std::string& fileType, bool open, const std::string& defaultExt); // Set the selected path, this does not fire the EV_PATH_ENTRY_CHANGED event void setValue(const std::string& val); // Get the currently selected path std::string getValue() const; // Returns the text entry widget wxTextCtrl* getEntryWidget(); // Set the default extension to use in the FileChooser variant void setDefaultExtension(const std::string& defaultExt); private: // callbacks void onBrowseFiles(wxCommandEvent& ev); void onBrowseFolders(wxCommandEvent& ev); }; wxDECLARE_EVENT(EV_PATH_ENTRY_CHANGED, wxCommandEvent); } // namespace wxutil DarkRadiant-2.5.0/libs/wxutil/ScopeTimer.h000066400000000000000000000010261321750546400204200ustar00rootroot00000000000000#pragma once #include "itextstream.h" #include #include namespace wxutil { class ScopeTimer { private: wxStopWatch _timer; std::string _message; public: ScopeTimer(const std::string& message) : _message(message) { _timer.Start(); } ~ScopeTimer() { double elapsed_time = _timer.Time() / 1000.f; rMessage() << _message << " timer: " << fmt::format("{0:5.2f}", elapsed_time) << " second(s) elapsed" << std::endl; } }; } DarkRadiant-2.5.0/libs/wxutil/SerialisableWidgets.cpp000066400000000000000000000121601321750546400226300ustar00rootroot00000000000000#include "SerialisableWidgets.h" #include "string/convert.h" #include "itextstream.h" namespace wxutil { // Text entry SerialisableTextEntry::SerialisableTextEntry(wxWindow* parent) : wxTextCtrl(parent, wxID_ANY) {} void SerialisableTextEntry::importFromString(const std::string& str) { SetValue(str); } std::string SerialisableTextEntry::exportToString() const { return GetValue().ToStdString(); } SerialisableTextEntryWrapper::SerialisableTextEntryWrapper(wxTextCtrl* entry) : _entry(entry) {} void SerialisableTextEntryWrapper::importFromString(const std::string& str) { _entry->SetValue(str); } std::string SerialisableTextEntryWrapper::exportToString() const { return _entry->GetValue().ToStdString(); } // Spin button SerialisableSpinButton::SerialisableSpinButton(wxWindow* parent, double value, double min, double max, double step, unsigned int digits) : wxSpinCtrlDouble(parent, wxID_ANY) { SetRange(min, max); SetValue(value); SetIncrement(step); SetDigits(digits); } void SerialisableSpinButton::importFromString(const std::string& str) { SetValue(string::convert(str)); } std::string SerialisableSpinButton::exportToString() const { return string::to_string(GetValue()); } SerialisableSpinButtonWrapper::SerialisableSpinButtonWrapper(wxSpinCtrlDouble* spin) : _spin(spin) {} void SerialisableSpinButtonWrapper::importFromString(const std::string& str) { _spin->SetValue(string::convert(str)); } std::string SerialisableSpinButtonWrapper::exportToString() const { return string::to_string(_spin->GetValue()); } // Toggle button SerialisableToggleButton::SerialisableToggleButton(wxWindow* parent) : wxToggleButton(parent, wxID_ANY, "") {} SerialisableToggleButton::SerialisableToggleButton(wxWindow* parent, const std::string& label) : wxToggleButton(parent, wxID_ANY, label) {} void SerialisableToggleButton::importFromString(const std::string& str) { SetValue(str == "1"); } std::string SerialisableToggleButton::exportToString() const { return GetValue() ? "1" : "0"; } SerialisableToggleButtonWrapper::SerialisableToggleButtonWrapper(wxToggleButton* button) : _button(button) {} void SerialisableToggleButtonWrapper::importFromString(const std::string& str) { _button->SetValue(str == "1"); } std::string SerialisableToggleButtonWrapper::exportToString() const { return _button->GetValue() ? "1" : "0"; } // Check Button SerialisableCheckButton::SerialisableCheckButton(wxWindow* parent) : wxCheckBox(parent, wxID_ANY, "") {} SerialisableCheckButton::SerialisableCheckButton(wxWindow* parent, const std::string& label) : wxCheckBox(parent, wxID_ANY, label) {} void SerialisableCheckButton::importFromString(const std::string& str) { SetValue(str == "1"); } std::string SerialisableCheckButton::exportToString() const { return GetValue() ? "1" : "0"; } SerialisableCheckButtonWrapper::SerialisableCheckButtonWrapper(wxCheckBox* button) : _button(button) {} void SerialisableCheckButtonWrapper::importFromString(const std::string& str) { _button->SetValue(str == "1"); } std::string SerialisableCheckButtonWrapper::exportToString() const { return _button->GetValue() ? "1" : "0"; } // SerialisableComboBox_Index SerialisableComboBox_Index::SerialisableComboBox_Index(wxWindow* parent) : SerialisableComboBox(parent) {} void SerialisableComboBox_Index::importFromString(const std::string& str) { int activeId = string::convert(str); SetSelection(activeId); int newId = GetSelection(); if (activeId != newId) { rConsoleError() << "SerialisableComboBox_Index::importFromString(): " << "warning: requested index " << activeId << " was not set, current index is " << newId << std::endl; } } std::string SerialisableComboBox_Index::exportToString() const { return string::to_string(GetSelection()); } SerialisableComboBox_IndexWrapper::SerialisableComboBox_IndexWrapper(wxChoice* combo) : _combo(combo) {} void SerialisableComboBox_IndexWrapper::importFromString(const std::string& str) { int activeId = string::convert(str); _combo->SetSelection(activeId); int newId = _combo->GetSelection(); if (activeId != newId) { rConsoleError() << "SerialisableComboBox_Index::importFromString(): " << "warning: requested index " << activeId << " was not set, current index is " << newId << std::endl; } } std::string SerialisableComboBox_IndexWrapper::exportToString() const { return string::to_string(_combo->GetSelection()); } // SerialisableComboBox_Text SerialisableComboBox_Text::SerialisableComboBox_Text(wxWindow* parent) : SerialisableComboBox(parent) {} void SerialisableComboBox_Text::importFromString(const std::string& str) { SetSelection(FindString(str)); } std::string SerialisableComboBox_Text::exportToString() const { return GetString(GetSelection()).ToStdString(); } SerialisableComboBox_TextWrapper::SerialisableComboBox_TextWrapper(wxChoice* combo) : _combo(combo) {} void SerialisableComboBox_TextWrapper::importFromString(const std::string& str) { _combo->SetSelection(_combo->FindString(str)); } std::string SerialisableComboBox_TextWrapper::exportToString() const { return _combo->GetString(_combo->GetSelection()).ToStdString(); } } // namespace DarkRadiant-2.5.0/libs/wxutil/SerialisableWidgets.h000066400000000000000000000144021321750546400222760ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include namespace wxutil { /* * wxTextCtrl object which implements StringSerialisable. */ class SerialisableTextEntry : public wxTextCtrl, public StringSerialisable { public: // Main constructor SerialisableTextEntry(wxWindow* parent); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableTextEntryPtr; // Wrapper class to make existing wxTextCtrls serialisable class SerialisableTextEntryWrapper : public StringSerialisable { private: wxTextCtrl* _entry; public: // Main constructor, wrapping around an existing widget SerialisableTextEntryWrapper(wxTextCtrl* entry); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableTextEntryWrapperPtr; /** * wxSpinCtrlDouble object which implements StringSerialisable. */ class SerialisableSpinButton : public wxSpinCtrlDouble, public StringSerialisable { public: // Main constructor SerialisableSpinButton(wxWindow* parent, double value, double min, double max, double step, unsigned int digits); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableSpinButtonPtr; // Wrapper class to make an existing wxSpinCtrlDouble serialisable class SerialisableSpinButtonWrapper : public StringSerialisable { private: wxSpinCtrlDouble* _spin; public: // Main constructor, wrapping around an existing widget SerialisableSpinButtonWrapper(wxSpinCtrlDouble* spin); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableSpinButtonWrapperPtr; /** * wxToggleButton object which implements StringSerialisable. */ class SerialisableToggleButton : public wxToggleButton, public StringSerialisable { public: // Main constructors SerialisableToggleButton(wxWindow* parent); SerialisableToggleButton(wxWindow* parent, const std::string& label); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableToggleButtonPtr; // Wrapper class to make existing wxToggleButton serialisable class SerialisableToggleButtonWrapper : public StringSerialisable { private: wxToggleButton* _button; public: // Main constructor, wrapping around an existing widget SerialisableToggleButtonWrapper(wxToggleButton* button); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableToggleButtonWrapperPtr; /** * wxCheckBox object which implements StringSerialisable. */ class SerialisableCheckButton : public wxCheckBox, public StringSerialisable { public: // Main constructors SerialisableCheckButton(wxWindow* parent); SerialisableCheckButton(wxWindow* parent, const std::string& label); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableCheckButtonPtr; // Wrapper class to make existing wxCheckBox serialisable class SerialisableCheckButtonWrapper : public StringSerialisable { private: wxCheckBox* _button; public: // Main constructor, wrapping around an existing widget SerialisableCheckButtonWrapper(wxCheckBox* button); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableCheckButtonWrapperPtr; // Base class for serialisable combo boxes (text or index) class SerialisableComboBox : public wxChoice, public StringSerialisable { public: SerialisableComboBox(wxWindow* parent) : wxChoice(parent, wxID_ANY) {} }; typedef std::shared_ptr SerialisableComboBoxPtr; /** * \brief * StringSerialisable combo box (text values) which saves the * selected value as a numeric index. */ class SerialisableComboBox_Index : public SerialisableComboBox { public: /* Main constructor */ SerialisableComboBox_Index(wxWindow* parent); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableComboBox_IndexPtr; // Wrapper class to make existing Gtk::ComboBoxText serialisable class SerialisableComboBox_IndexWrapper : public StringSerialisable { private: wxChoice* _combo; public: // Main constructor, wrapping around an existing widget SerialisableComboBox_IndexWrapper(wxChoice* combo); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableComboBox_IndexWrapperPtr; /** * \brief * Serialisable combo box which saves the selected value as a text string. */ class SerialisableComboBox_Text : public SerialisableComboBox { public: /* Main constructor */ SerialisableComboBox_Text(wxWindow* parent); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableComboBox_TextPtr; // Wrapper class to make existing Gtk::ComboBoxText serialisable class SerialisableComboBox_TextWrapper : public StringSerialisable { private: wxChoice* _combo; public: // Main constructor, wrapping around an existing widget SerialisableComboBox_TextWrapper(wxChoice* combo); /* StringSerialisable implementation */ void importFromString(const std::string& str); std::string exportToString() const; }; typedef std::shared_ptr SerialisableComboBox_TextWrapperPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/SourceView.cpp000066400000000000000000000137641321750546400210100ustar00rootroot00000000000000#include "SourceView.h" namespace wxutil { SourceViewCtrl::SourceViewCtrl(wxWindow* parent) : wxStyledTextCtrl(parent, wxID_ANY) { // Predefine a few styles for use in subclasses _predefinedStyles[Default] = Style("BLACK"); _predefinedStyles[Keyword1] = Style("BLUE", Bold); _predefinedStyles[Keyword2] = Style("MIDNIGHT BLUE"); _predefinedStyles[Keyword3] = Style("CORNFLOWER BLUE"); _predefinedStyles[Keyword4] = Style("CYAN"); _predefinedStyles[Keyword5] = Style("DARK GREY"); _predefinedStyles[Keyword6] = Style("GREY"); _predefinedStyles[Comment] = Style("FOREST GREEN"); _predefinedStyles[CommentDoc] = Style("FOREST GREEN"); _predefinedStyles[CommentLine] = Style("FOREST GREEN"); _predefinedStyles[SpecialComment] = Style("FOREST GREEN", Italic); _predefinedStyles[Character] = Style("KHAKI"); _predefinedStyles[CharacterEOL] = Style("KHAKI"); _predefinedStyles[String] = Style("BROWN"); _predefinedStyles[StringEOL] = Style("BROWN"); _predefinedStyles[Delimiter] = Style("ORANGE"); _predefinedStyles[Punctuation] = Style("ORANGE"); _predefinedStyles[Operator] = Style("BLACK"); _predefinedStyles[Brace] = Style("VIOLET"); _predefinedStyles[Command] = Style("BLUE"); _predefinedStyles[Identifier] = Style("VIOLET"); _predefinedStyles[Label] = Style("VIOLET"); _predefinedStyles[Number] = Style("SIENNA"); _predefinedStyles[Parameter] = Style("VIOLET", Italic); _predefinedStyles[RegEx] = Style("ORCHID"); _predefinedStyles[UUID] = Style("ORCHID"); _predefinedStyles[Value] = Style("ORCHID", Italic); _predefinedStyles[Preprocessor] = Style("GREY"); _predefinedStyles[Script] = Style("DARK GREY"); _predefinedStyles[Error] = Style("RED"); _predefinedStyles[Undefined] = Style("ORANGE"); // Ensure we have all styles defined assert(_predefinedStyles.size() == NumElements); } void SourceViewCtrl::SetStyleMapping(int elementIndex, Element elementType) { const Style& style = _predefinedStyles[elementType]; StyleSetForeground(elementIndex, wxColour(style.foreground)); wxFont font(style.fontsize, wxFONTFAMILY_MODERN, (style.fontstyle & Italic) > 0 ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL, (style.fontstyle & Bold) > 0 ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL, (style.fontstyle & Underline) > 0, style.fontname); StyleSetFont(elementIndex, font); StyleSetVisible(elementIndex, (style.fontstyle & Hidden) == 0); } // Python specific PythonSourceViewCtrl::PythonSourceViewCtrl(wxWindow* parent) : SourceViewCtrl(parent) { // Set up styling for Python SetLexer(wxSTC_LEX_PYTHON); // The Python Lexer can recognise 14 different types of source elements // We map these types to different styles/appearances SetStyleMapping(0, Default); SetStyleMapping(1, CommentLine); SetStyleMapping(2, Number); SetStyleMapping(3, String); SetStyleMapping(4, Character); SetStyleMapping(5, Keyword1); SetStyleMapping(6, Default); SetStyleMapping(7, Default); SetStyleMapping(8, Default); SetStyleMapping(9, Default); SetStyleMapping(10, Operator); SetStyleMapping(11, Identifier); SetStyleMapping(12, Default); SetStyleMapping(13, StringEOL); SetKeyWords(0, "and as assert break class continue def del elif else except exec " "finally for from global if import in is lambda not None or pass " "print raise return try while with yield"); }; // D3 materials D3MaterialSourceViewCtrl::D3MaterialSourceViewCtrl(wxWindow* parent) : SourceViewCtrl(parent) { // Set up styling for C++ SetLexer(wxSTC_LEX_CPP); // The C++ Lexer can recognise 19 different types of source elements // We map these types to different styles/appearances SetStyleMapping(0, Default); SetStyleMapping(1, Comment); SetStyleMapping(2, CommentLine); SetStyleMapping(3, CommentDoc); SetStyleMapping(4, Number); SetStyleMapping(5, Keyword1); SetStyleMapping(6, String); SetStyleMapping(7, Character); SetStyleMapping(8, UUID); SetStyleMapping(9, Preprocessor); SetStyleMapping(10, Operator); SetStyleMapping(11, Identifier); SetStyleMapping(12, StringEOL); SetStyleMapping(13, Default); SetStyleMapping(14, RegEx); SetStyleMapping(15, SpecialComment); SetStyleMapping(16, Keyword2); SetStyleMapping(17, Keyword1); SetStyleMapping(18, Error); SetKeyWords(0, "diffusemap qer_editorimage bumpmap specularmap map if description polygonOffset " "noshadows noselfshadow forceshadows nooverlays forceoverlays translucent clamp zeroclamp " "alphazeroclamp forceopaque twosided backsided mirror nofog unsmoothedTangents guisurf sort " "decal reflect spectrum deform decalInfo renderbump DECAL_MACRO sprite tube flare expand move " "turbulent eyeBall particle particle2 noportalfog fogLight blendLight ambientLight lightFallOffImage " "solid water playerclip monsterclip moveableclip ikclip blood trigger aassolid aasobstacle " "flashlight_trigger nonsolid nullNormal areaPortal qer_nocarve discrete nofragment slick " "collision noimpact nodamage ladder nosteps metal stone flesh wood cardboard liquid glass " "plastic ricochet surfType10 surfType11 surfType12 surfType13 surfType14 surfType15 blend " "remoteRenderMap mirrorRenderMap videomap soundmap cubemap cameracubemap ignorealphatest " "nearest linear noclamp uncompressed highQuality forceHighQuality nopicmip vertexColor " "inverseVertexColor privatePolygonOffset texGen scroll translate scale centerScale shear " "rotate maskRed maskGreen maskBlue maskAlpha maskColor maskDepth alphatest red green blue " "alpha rgb rgba color colored fragmentProgram vertexProgram program vertexParm fragmentMap megatexture"); SetKeyWords(1, "_white _flat _black gl_src_alpha gl_one_minus_src_alpha gl_one gl_dst_color " "gl_zero gl_one_minus_dst_color gl_dst_alpha gl_one_minus_dst_alpha gl_src_alpha_saturate " "gl_src_color gl_one_minus_src_color add filter modulate none heightmap addnormals smoothnormals " "add scale invertAlpha invertColor makeIntensity makeAlpha parm0 parm1 parm2 parm3 parm4 parm5 " "parm6 parm7 parm8 parm9 parm10 parm11 global0 global1 global2 global3 global4 global5 global6 global7 "); }; } // namespace DarkRadiant-2.5.0/libs/wxutil/SourceView.h000066400000000000000000000044021321750546400204420ustar00rootroot00000000000000#pragma once #include #include namespace wxutil { /** * greebo: This is a custom extension of the wxWidgets styles text control, * providing a few methods to make the actual code style mapping easier. * It's advisable to subclass this control and map the various lexer-recognised * elements to a specific appearance. */ class SourceViewCtrl : public wxStyledTextCtrl { public: enum FontStyle { Normal = 1, Italic = 2, Bold = 4, Underline = 8, Hidden = 16, }; // Describes a specific style (e.g. a code comment) struct Style { wxString foreground; wxString fontname; int fontsize; FontStyle fontstyle; Style() : foreground("BLACK"), fontname(""), fontsize(10), fontstyle(Normal) {}; Style(const char* foreground_, FontStyle fontStyle_ = Normal, int fontSize_ = 10, const char* fontname_ = "") : foreground(foreground_), fontname(fontname_), fontsize(fontSize_), fontstyle(fontStyle_) {} }; // Elements as recognised by the STC lexer enum Element { Default = 0, Keyword1, Keyword2, Keyword3, Keyword4, Keyword5, Keyword6, Comment, CommentDoc, CommentLine, SpecialComment, Character, CharacterEOL, String, StringEOL, Delimiter, Punctuation, Operator, Brace, Command, Identifier, Label, Number, Parameter, RegEx, UUID, Value, Preprocessor, Script, Error, Undefined, NumElements, }; protected: typedef std::map StyleMap; StyleMap _predefinedStyles; public: SourceViewCtrl(wxWindow* parent); virtual ~SourceViewCtrl() {} // Use this method to set a lexer-recognised element to a certain style // e.g. SetStyleMapping(0, Number), provided that the lexer is using // the index 0 to represent numbers in the source. virtual void SetStyleMapping(int elementIndex, Element elementType); }; /** * A special class providing syntax highlighting for the Python * scripting language. */ class PythonSourceViewCtrl : public SourceViewCtrl { public: PythonSourceViewCtrl(wxWindow* parent); }; /** * A special class providing highlighting for the Doom 3 * material syntax. */ class D3MaterialSourceViewCtrl : public SourceViewCtrl { public: D3MaterialSourceViewCtrl(wxWindow* parent); }; } // namespace DarkRadiant-2.5.0/libs/wxutil/TreeModel.cpp000066400000000000000000000572271321750546400205770ustar00rootroot00000000000000#include "TreeModel.h" #include #include namespace wxutil { wxString TreeModel::Column::getWxType() const { static std::vector types(NumTypes); if (types.empty()) { types[String] = "string"; // we store numbers as strings types[Integer] = "string"; types[Double] = "string"; types[Boolean] = "bool"; types[Icon] = "icon"; types[IconText] = "wxDataViewIconText"; types[Pointer] = "void*"; } return types[type]; } // TreeModel nodes form a directed acyclic graph class TreeModel::Node : public util::Noncopyable { public: Node* parent; // NULL for the root node // The item will use a Node* pointer as ID, which is possible // since the wxDataViewItem is using void* as ID type wxDataViewItem item; typedef std::vector Values; Values values; typedef std::vector Children; Children children; typedef std::vector Attributes; Attributes attributes; typedef std::vector EnabledFlags; // Each value can be flagged as enabled/disabled EnabledFlags enabledFlags; // Public constructor, does not accept NULL pointers Node(Node* parent_) : parent(parent_), item(reinterpret_cast(this)) { // Use CreateRoot() instead of passing NULL assert(parent_ != NULL); } private: // Private constructor creates a root node, has a wxDataViewItem ID == NULL Node() : parent(NULL), item(NULL) {} public: static NodePtr createRoot() { return NodePtr(new Node); } bool remove(TreeModel::Node* child) { for (Children::iterator i = children.begin(); i != children.end(); ++i) { if (i->get() == child) { children.erase(i); return true; } } return false; } }; // ------------------------------------------------------------------------------- wxDEFINE_EVENT(EV_TREEMODEL_POPULATION_FINISHED, TreeModel::PopulationFinishedEvent); wxDEFINE_EVENT(EV_TREEMODEL_POPULATION_PROGRESS, TreeModel::PopulationProgressEvent); TreeModel::PopulationFinishedEvent::PopulationFinishedEvent(int id) : wxEvent(id, EV_TREEMODEL_POPULATION_FINISHED), _treeModel(NULL) {} TreeModel::PopulationFinishedEvent::PopulationFinishedEvent(TreeModel::Ptr store, int id) : wxEvent(id, EV_TREEMODEL_POPULATION_FINISHED), _treeModel(store) {} // You *must* copy here the data to be transported TreeModel::PopulationFinishedEvent::PopulationFinishedEvent(const TreeModel::PopulationFinishedEvent& event) : wxEvent(event), _treeModel(event._treeModel) {} // Required for sending with wxPostEvent() wxEvent* TreeModel::PopulationFinishedEvent::Clone() const { return new PopulationFinishedEvent(*this); } TreeModel::Ptr TreeModel::PopulationFinishedEvent::GetTreeModel() const { return _treeModel; } void TreeModel::PopulationFinishedEvent::SetTreeModel(TreeModel::Ptr store) { _treeModel = store; } TreeModel::PopulationProgressEvent::PopulationProgressEvent(int id) : wxEvent(id, EV_TREEMODEL_POPULATION_PROGRESS) {} TreeModel::PopulationProgressEvent::PopulationProgressEvent(const wxString& message, int id) : wxEvent(id, EV_TREEMODEL_POPULATION_PROGRESS), _message(message) {} TreeModel::PopulationProgressEvent::PopulationProgressEvent(const TreeModel::PopulationProgressEvent& event) : wxEvent(event), _message(event._message) {} wxEvent* TreeModel::PopulationProgressEvent::Clone() const { return new PopulationProgressEvent(*this); } const wxString& TreeModel::PopulationProgressEvent::GetMessage() const { return _message; } void TreeModel::PopulationProgressEvent::SetMessage(const wxString& message) { _message = message; } // ------------------------------------------------------------------------------- TreeModel::TreeModel(const ColumnRecord& columns, bool isListModel) : _columns(columns), _rootNode(Node::createRoot()), _defaultStringSortColumn(-1), _hasDefaultCompare(false), _isListModel(isListModel) {} TreeModel::TreeModel(const TreeModel& existingModel) : _columns(existingModel._columns), _rootNode(existingModel._rootNode), _defaultStringSortColumn(existingModel._defaultStringSortColumn), _hasDefaultCompare(existingModel._hasDefaultCompare), _isListModel(existingModel._isListModel) {} TreeModel::~TreeModel() {} const TreeModel::NodePtr& TreeModel::getRootNode() const { return _rootNode; } const TreeModel::ColumnRecord& TreeModel::GetColumns() const { return _columns; } TreeModel::Row TreeModel::AddItem() { return AddItem(_rootNode->item); } TreeModel::Row TreeModel::AddItem(const wxDataViewItem& parent) { // Redirect to the root node for invalid items Node* parentNode = !parent.IsOk() ? _rootNode.get() : static_cast(parent.GetID()); NodePtr node(new Node(parentNode)); parentNode->children.push_back(node); return Row(node->item, *this); } bool TreeModel::RemoveItem(const wxDataViewItem& item) { if (item.IsOk()) { Node* node = static_cast(item.GetID()); Node* parent = node->parent; if (parent == NULL) return false; // cannot remove the root node if (parent->remove(node)) { ItemDeleted(parent->item, item); return true; } } return false; } int TreeModel::RemoveItems(const std::function& predicate) { return RemoveItemsRecursively(GetRoot(), predicate); } int TreeModel::RemoveItemsRecursively(const wxDataViewItem& parent, const std::function& predicate) { Node* parentNode = !parent.IsOk() ? _rootNode.get() : static_cast(parent.GetID()); int deleteCount = 0; wxDataViewItemArray itemsToDelete; for (Node::Children::const_iterator i = parentNode->children.begin(); i != parentNode->children.end(); ++i) { Row row((*i)->item, *this); if (predicate(row)) { itemsToDelete.push_back((*i)->item); } } if (!itemsToDelete.IsEmpty()) { // It seems that the wxDataViewCtrl has trouble in case a highlighted row is removed // and the actual nodes have already been deleted, so remove them afterwards. ItemsDeleted(parent, itemsToDelete); // Remove these items std::for_each(itemsToDelete.begin(), itemsToDelete.end(), [&] (const wxDataViewItem& item) { Node* nodeToDelete = static_cast(item.GetID()); parentNode->remove(nodeToDelete); deleteCount++; }); } for (Node::Children::const_iterator i = parentNode->children.begin(); i != parentNode->children.end(); ++i) { deleteCount += RemoveItemsRecursively((*i)->item, predicate); } return deleteCount; } TreeModel::Row TreeModel::GetRootItem() { return Row(GetRoot(), *this); } void TreeModel::Clear() { _rootNode->values.clear(); _rootNode->children.clear(); Cleared(); } void TreeModel::SetDefaultStringSortColumn(int index) { _defaultStringSortColumn = index; } void TreeModel::SetHasDefaultCompare(bool hasDefaultCompare) { _hasDefaultCompare = hasDefaultCompare; } void TreeModel::ForeachNode(const TreeModel::VisitFunction& visitFunction) { // Skip the root node and traverse its immediate children recursively std::for_each(_rootNode->children.begin(), _rootNode->children.end(), [&] (const NodePtr& node) { ForeachNodeRecursive(node, visitFunction); }); } void TreeModel::ForeachNodeRecursive(const TreeModel::NodePtr& node, const TreeModel::VisitFunction& visitFunction) { wxutil::TreeModel::Row row(node->item, *this); visitFunction(row); // Enter the recursion std::for_each(node->children.begin(), node->children.end(), [&] (const NodePtr& child) { ForeachNodeRecursive(child, visitFunction); }); } void TreeModel::ForeachNodeReverse(const TreeModel::VisitFunction& visitFunction) { // Skip the root node and traverse its immediate children recursively for (Node::Children::const_reverse_iterator i = _rootNode->children.rbegin(); i != _rootNode->children.rend(); ++i) { ForeachNodeRecursiveReverse(*i, visitFunction); } } void TreeModel::ForeachNodeRecursiveReverse(const TreeModel::NodePtr& node, const TreeModel::VisitFunction& visitFunction) { wxutil::TreeModel::Row row(node->item, *this); visitFunction(row); // Enter the recursion for (Node::Children::const_reverse_iterator i = node->children.rbegin(); i != node->children.rend(); ++i) { ForeachNodeRecursiveReverse(*i, visitFunction); } } void TreeModel::SortModel(const TreeModel::SortFunction& sortFunction) { SortModelRecursive(_rootNode, sortFunction); } void TreeModel::SortModelByColumn(const TreeModel::Column& column) { SortModelRecursive(_rootNode, [&] (const wxDataViewItem& a, const wxDataViewItem& b)->bool { Row rowA(a, *this); Row rowB(b, *this); if (column.type == Column::IconText) { wxDataViewIconText txtA = rowA[column]; wxDataViewIconText txtB = rowB[column]; return txtA.GetText() < txtB.GetText(); } else if (column.type == Column::String) { std::string txtA = rowA[column]; std::string txtB = rowB[column]; return txtA < txtB; } else if (column.type == Column::Integer) { int intA = rowA[column].getInteger(); int intB = rowA[column].getInteger(); return intA < intB; } else if (column.type == Column::Double) { double dblA = rowA[column].getDouble(); double dblB = rowA[column].getDouble(); return dblA < dblB; } return false; }); } void TreeModel::SortModelFoldersFirst(const TreeModel::Column& stringColumn, const TreeModel::Column& isFolderColumn) { SortModelRecursive(_rootNode, std::bind(&TreeModel::CompareFoldersFirst, this, std::placeholders::_1, std::placeholders::_2, stringColumn, stringColumn.type == Column::String ? CompareStringVariants : CompareIconTextVariants, isFolderColumn)); } void TreeModel::SortModelRecursive(const TreeModel::NodePtr& node, const TreeModel::SortFunction& sortFunction) { // Use std::sort algorithm and small lambda to only pass wxDataViewItems to the client sort function std::sort(node->children.begin(), node->children.end(), [&] (const NodePtr& a, const NodePtr& b)->bool { return sortFunction(a->item, b->item); }); // Enter recursion std::for_each(node->children.begin(), node->children.end(), [&] (const NodePtr& child) { SortModelRecursive(child, sortFunction); }); } wxDataViewItem TreeModel::FindString(const std::string& needle, const Column& column) { return FindRecursive(_rootNode, [&] (const Node& node)->bool { int colIndex = column.getColumnIndex(); if (column.type == Column::IconText) { if (static_cast(node.values.size()) > colIndex) { wxDataViewIconText iconText; iconText << node.values[colIndex]; return iconText.GetText() == needle; } } else if (column.type == Column::String) { return static_cast(node.values.size()) > colIndex && static_cast(node.values[colIndex]) == needle; } return false; }); } wxDataViewItem TreeModel::FindInteger(long needle, const Column& column) { return FindRecursive(_rootNode, [&] (const Node& node)->bool { int colIndex = column.getColumnIndex(); return static_cast(node.values.size()) > colIndex && static_cast(node.values[colIndex]) == needle; }); } wxDataViewItem TreeModel::FindRecursive(const TreeModel::NodePtr& node, const std::function& predicate) { // Test the node itself if (predicate(*node)) { return node->item; } // Then test all children, aborting on first success for (Node::Children::const_iterator i = node->children.begin(); i != node->children.end(); ++i) { wxDataViewItem item = FindRecursive(*i, predicate); if (item.IsOk()) { return item; } } // Return an empty data item, which is "not ok" return wxDataViewItem(); } wxDataViewItem TreeModel::FindRecursiveUsingRows(const TreeModel::NodePtr& node, const std::function& predicate) { if (node->item.IsOk()) { Row row(node->item, *this); // Test the node itself if (predicate(row)) { return node->item; } } // Then test all children, aborting on first success for (Node::Children::const_iterator i = node->children.begin(); i != node->children.end(); ++i) { wxDataViewItem item = FindRecursiveUsingRows(*i, predicate); if (item.IsOk()) { return item; } } // Return an empty data item, which is "not ok" return wxDataViewItem(); } bool TreeModel::HasDefaultCompare() const { return _hasDefaultCompare; } unsigned int TreeModel::GetColumnCount() const { return static_cast(_columns.size()); }; // return type as reported by wxVariant wxString TreeModel::GetColumnType(unsigned int col) const { return _columns[col].getWxType(); } // get value into a wxVariant void TreeModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const { Node* owningNode = item.IsOk() ? static_cast(item.GetID()) : _rootNode.get(); if (col < owningNode->values.size()) { variant = owningNode->values[col]; } } bool TreeModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) { Node* owningNode = item.IsOk() ? static_cast(item.GetID()) : _rootNode.get(); if (owningNode->values.size() < col + 1) { owningNode->values.resize(col + 1); } owningNode->values[col] = variant; return true; } bool TreeModel::GetAttr(const wxDataViewItem& item, unsigned int col, wxDataViewItemAttr& attr) const { if (!item.IsOk()) { return false; } Node* owningNode = static_cast(item.GetID()); if (col < owningNode->attributes.size()) { attr = owningNode->attributes[col]; return true; } return false; } void TreeModel::SetAttr(const wxDataViewItem& item, unsigned int col, const wxDataViewItemAttr& attr) const { if (!item.IsOk()) { return; } Node* owningNode = static_cast(item.GetID()); if (owningNode->attributes.size() < col + 1) { owningNode->attributes.resize(col + 1); } owningNode->attributes[col] = attr; } wxDataViewItem TreeModel::GetParent(const wxDataViewItem& item) const { // It's ok to ask for invisible root node's parent if (!item.IsOk()) { return wxDataViewItem(NULL); } Node* owningNode = static_cast(item.GetID()); if (owningNode->parent != NULL) { return owningNode->parent->item; } return wxDataViewItem(NULL); } bool TreeModel::IsContainer(const wxDataViewItem& item) const { // greebo: it appears that invalid items are treated as containers, they can have children. // The wxDataViewCtrl has such a root node with invalid items if (!item.IsOk()) { return true; } #ifdef __WXGTK__ // greebo: The GTK DataViewCtrl implementation treats nodes differently // based on whether they have children or not. If a tree model node has no children // now it's not possible to add any children later on, causing assertions. // wxGTK wants to know *in advance* whether a node has children, so let's assume true // unless this is a listmodel (in which case non-root nodes never have children) return !_isListModel ? true : false; #else // Regular implementation: return true if this node has child nodes Node* owningNode = static_cast(item.GetID()); return owningNode != NULL && !owningNode->children.empty(); #endif } unsigned int TreeModel::GetChildren(const wxDataViewItem& item, wxDataViewItemArray& children) const { // Requests for invalid items are asking for our root children, actually Node* owningNode = !item.IsOk() ? _rootNode.get() : static_cast(item.GetID()); for (Node::Children::const_iterator iter = owningNode->children.begin(); iter != owningNode->children.end(); ++iter) { children.Add((*iter)->item); } return static_cast(owningNode->children.size()); } wxDataViewItem TreeModel::GetRoot() { // The root item carries the NULL pointer, the other methods need to be able to deal with that return _rootNode->item; } bool TreeModel::IsListModel() const { return _isListModel; } void TreeModel::SetIsListModel(bool isListModel) { _isListModel = isListModel; } int TreeModel::Compare(const wxDataViewItem& item1, const wxDataViewItem& item2, unsigned int column, bool ascending) const { Node* node1 = static_cast(item1.GetID()); Node* node2 = static_cast(item2.GetID()); if (!node1 || !node2) return 0; #if 0 // Don't handle folders first sorting here if (!node1->children.empty() && node2->children.empty()) return ascending ? -1 : 1; if (!node2->children.empty() && node1->children.empty()) return ascending ? 1 : -1; #endif if (_defaultStringSortColumn >= 0) { return ascending ? node1->values[_defaultStringSortColumn].GetString().CmpNoCase( node2->values[_defaultStringSortColumn].GetString()) : node2->values[_defaultStringSortColumn].GetString().CmpNoCase( node1->values[_defaultStringSortColumn].GetString()); } // When clicking on the dataviewctrl headers, we need to support some default algorithm // implement a few comparison types switch (_columns[column].type) { case Column::String: { return ascending ? node1->values[column].GetString().CmpNoCase(node2->values[column].GetString()) : node2->values[column].GetString().CmpNoCase(node1->values[column].GetString()); } case Column::IconText: { wxDataViewIconText val1; val1 << node1->values[column]; wxDataViewIconText val2; val2 << node2->values[column]; return ascending ? val1.GetText().CmpNoCase(val2.GetText()) : val2.GetText().CmpNoCase(val1.GetText()); } case Column::Double: { double val1 = node1->values[column].GetDouble(); double val2 = node2->values[column].GetDouble(); if (val1 == val2) return 0; return ascending ? (val1 < val2 ? -1 : 1) : (val2 < val1 ? -1 : 1); } case Column::Integer: { long val1 = node1->values[column].GetInteger(); long val2 = node2->values[column].GetInteger(); if (val1 == val2) return 0; return ascending ? (val1 < val2 ? -1 : 1) : (val2 < val1 ? -1 : 1); } case Column::Boolean: { bool val1 = node1->values[column].GetBool(); bool val2 = node2->values[column].GetBool(); if (val1 == val2) return 0; return ascending ? (!val1 ? -1 : 1) : (val1 ? -1 : 1); } case Column::Pointer: { void* val1 = node1->values[column].GetVoidPtr(); void* val2 = node2->values[column].GetVoidPtr(); if (val1 == val2) return 0; return ascending ? (val1 < val2 ? -1 : 1) : (val2 < val1 ? -1 : 1); } case Column::Icon: { return 0; // no sense in comparing icons } default: return 0; // default case }; return 0; } int TreeModel::CompareStringVariants(const wxVariant& a, const wxVariant& b) { wxVariant aName, bName; return aName.GetString().CmpNoCase(bName.GetString()); } int TreeModel::CompareIconTextVariants(const wxVariant& a, const wxVariant& b) { wxDataViewIconText aValue; aValue << a; wxDataViewIconText bValue; bValue << b; return aValue.GetText().CmpNoCase(bValue.GetText()); } bool TreeModel::CompareFoldersFirst(const wxDataViewItem& a, const wxDataViewItem& b, const TreeModel::Column& stringColumn, const std::function& stringCompareFunc, const TreeModel::Column& isFolderCol) { // Check if A or B are folders wxVariant aIsFolder, bIsFolder; GetValue(aIsFolder, a, isFolderCol.getColumnIndex()); GetValue(bIsFolder, b, isFolderCol.getColumnIndex()); if (aIsFolder) { // A is a folder, check if B is as well if (bIsFolder) { // A and B are both folders // Compare folder names // greebo: We're not checking for equality here, shader names are unique wxVariant aName, bName; GetValue(aName, a, stringColumn.getColumnIndex()); GetValue(bName, b, stringColumn.getColumnIndex()); return stringCompareFunc(aName, bName) < 0; } else { // A is a folder, B is not, A sorts before return true; } } else { // A is not a folder, check if B is one if (bIsFolder) { // A is not a folder, B is, so B sorts before A return false; } else { // Neither A nor B are folders, compare names // greebo: We're not checking for equality here, names are unique wxVariant aName, bName; GetValue(aName, a, stringColumn.getColumnIndex()); GetValue(bName, b, stringColumn.getColumnIndex()); return stringCompareFunc(aName, bName) < 0; } } } // Search functor which tries to find the next match for the given search string // is agnostic of the search direction, it just gets invoked for each row. class TreeModel::SearchFunctor { private: const std::vector& _columns; wxDataViewItem _previousMatch; wxDataViewItem _match; enum SearchState { SearchingForLastMatch, Searching, Found, }; SearchState _state; wxString _searchString; public: SearchFunctor(const wxString& searchString, const std::vector& columns, const wxDataViewItem& previousMatch) : _columns(columns), _previousMatch(previousMatch), _state(previousMatch.IsOk() ? SearchingForLastMatch : Searching), _searchString(searchString.Lower()) {} const wxDataViewItem& getMatch() const { return _match; } void operator()(TreeModel::Row& row) { switch (_state) { case SearchingForLastMatch: if (row.getItem() == _previousMatch) { _state = Searching; return; } else { return; // skip until previousMatch is found } case Searching: std::for_each(_columns.begin(), _columns.end(), [&](const TreeModel::Column& col) { if (col.type == TreeModel::Column::String) { wxVariant variant = row[col].getVariant(); if (!variant.IsNull() && variant.GetString().Lower().Contains(_searchString)) { _match = row.getItem(); _state = Found; } } else if (col.type == TreeModel::Column::IconText) { wxDataViewIconText iconText = static_cast(row[col]); if (iconText.GetText().Lower().Contains(_searchString)) { _match = row.getItem(); _state = Found; } } }); break; case Found: return; // done here } } }; wxDataViewItem TreeModel::FindNextString(const wxString& needle, const std::vector& columns, const wxDataViewItem& previousMatch) { SearchFunctor functor(needle, columns, previousMatch); ForeachNode([&] (Row& row) { functor(row); }); return functor.getMatch(); } // Search for an item in the given columns (backwards), using previousMatch as reference point wxDataViewItem TreeModel::FindPrevString(const wxString& needle, const std::vector& columns, const wxDataViewItem& previousMatch) { SearchFunctor functor(needle, columns, previousMatch); ForeachNodeReverse([&] (Row& row) { functor(row); }); return functor.getMatch(); } bool TreeModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const { Node* owningNode = item.IsOk() ? static_cast(item.GetID()) : _rootNode.get(); if (col < owningNode->enabledFlags.size()) { return owningNode->enabledFlags[col]; } // Column values without flags render as enabled by default return true; } void TreeModel::SetEnabled(const wxDataViewItem& item, unsigned int col, bool enabled) { if (!item.IsOk()) { return; } Node* owningNode = static_cast(item.GetID()); if (owningNode->enabledFlags.size() < col + 1) { owningNode->enabledFlags.resize(col + 1, true); // fill with true by default } owningNode->enabledFlags[col] = enabled; } } // namespace DarkRadiant-2.5.0/libs/wxutil/TreeModel.h000066400000000000000000000337301321750546400202350ustar00rootroot00000000000000#pragma once #include #include #include #include #include "util/Noncopyable.h" namespace wxutil { /** * Implements a wxwidgets DataViewModel, similar to wxDataViewTreeStore * but with more versatility of the stored columns. */ class TreeModel : public wxDataViewModel { public: /// Reference-counted smart pointer type typedef wxObjectDataPtr Ptr; /** * Represents a column in the wxutil::TreeModel * Use the Type string to instantiate a Column and arrange * multiple columns into a ColumnRecord structure. */ class Column { public: enum Type { String = 0, Integer, Double, Boolean, Icon, IconText, Pointer, NumTypes }; Type type; std::string name; private: // column index in the treemodel - this member is assigned // by the TreeModel itself on attachment int _col; public: Column(Type type_, const std::string& name_ = "") : type(type_), name(name_), _col(-1) {} // Returns the index of this column int getColumnIndex() const { if (_col == -1) throw std::runtime_error("Cannot query column index of unattached column."); return _col; } // Only for internal use by the TreeModel - didn't want to use a friend declaration void _setColumnIndex(int index) { _col = index; } // Returns the wxWidgets type string of this column wxString getWxType() const; }; /** * Use this record to declare the column order of the TreeModel. * Subclasses should call Add() for each of their Column members. */ class ColumnRecord : public util::Noncopyable { public: // A list of column references, these point to members of subclasses typedef std::vector List; private: List _columns; protected: // Only to be instantiated by subclasses ColumnRecord() {} public: Column add(Column::Type type, const std::string& name = "") { _columns.push_back(Column(type, name)); _columns.back()._setColumnIndex(static_cast(_columns.size()) - 1); return _columns.back(); } List::iterator begin() { return _columns.begin(); } List::const_iterator begin() const { return _columns.begin(); } List::iterator end() { return _columns.end(); } List::const_iterator end() const { return _columns.end(); } List::size_type size() const { return _columns.size(); } const List::value_type& operator[](std::size_t index) const { return _columns[index]; } List::value_type& operator[](std::size_t index) { return _columns[index]; } }; // An assignment helper, for use in TreeModel::Row class ItemValueProxy { private: wxDataViewItem _item; const Column& _column; TreeModel& _model; public: ItemValueProxy(const wxDataViewItem& item, const Column& column, TreeModel& model) : _item(item), _column(column), _model(model) {} ItemValueProxy(const ItemValueProxy& other) : _item(other._item), _column(other._column), _model(other._model) {} // get/set operators ItemValueProxy& operator=(const wxVariant& data) { // wxGTK and wxWidgets 3.1.0+ doesn't like rendering number values as text // so let's store numbers as strings automatically if ((_column.type == Column::Integer || _column.type == Column::Double) && data.GetType() != "string") { wxVariant stringified(data.GetString()); _model.SetValue(stringified, _item, _column.getColumnIndex()); } else { _model.SetValue(data, _item, _column.getColumnIndex()); } // Newly assigned values are enabled by default _model.SetEnabled(_item, _column.getColumnIndex(), true); return *this; } // get/set operators for dataview attributes ItemValueProxy& operator=(const wxDataViewItemAttr& attr) { _model.SetAttr(_item, _column.getColumnIndex(), attr); return *this; } wxVariant getVariant() const { wxVariant variant; _model.GetValue(variant, _item, _column.getColumnIndex()); return variant; } operator wxVariant() const { return getVariant(); } // IconText columns are widely used, let's provide an operator cast operator wxDataViewIconText() const { wxDataViewIconText iconText; iconText << getVariant(); return iconText; } // Don't implement operator bool() directly, it can be converted to integer bool getBool() const { return getVariant().GetBool(); } int getInteger() const { return static_cast(getVariant().GetInteger()); } double getDouble() const { return getVariant().GetDouble(); } void* getPointer() const { return getVariant().GetVoidPtr(); } operator std::string() const { wxVariant variant = getVariant(); return variant.IsNull() ? "" : variant.GetString().ToStdString(); } bool isEnabled() { return _model.IsEnabled(_item, _column.getColumnIndex()); } // Enable/disable the column value, by default values are enabled after assignment // This is adhered by some cell renderers which draw the value greyed out or inactive void setEnabled(bool enabled) { _model.SetEnabled(_item, _column.getColumnIndex(), enabled); } }; /** * A convenience representation of a single wxDataViewItem, * allowing to set column values using the operator[]. */ class Row { private: wxDataViewItem _item; TreeModel& _model; public: Row(const wxDataViewItem& item, wxDataViewModel& model) : _item(item), _model(static_cast(model)) { assert(dynamic_cast(&_model) != NULL); } const wxDataViewItem& getItem() const { return _item; } const ItemValueProxy operator[](const Column& column) const { return ItemValueProxy(_item, column, _model); } ItemValueProxy operator[](const Column& column) { return ItemValueProxy(_item, column, _model); } // Sends the ItemAdded event to the associated treestore void SendItemAdded() { _model.ItemAdded(_model.GetParent(_item), _item); } void SendItemChanged() { _model.ItemChanged(_item); } }; // Visit function typedef std::function VisitFunction; // Sort function - should return true if a < b, false otherwise typedef std::function SortFunction; // Event to be emitted by threaded treemodel populators. Worker threads should use events // to communicate with the main GUI thread. class PopulationFinishedEvent : public wxEvent { private: TreeModel::Ptr _treeModel; public: PopulationFinishedEvent(int id = 0); PopulationFinishedEvent(TreeModel::Ptr store, int id = 0); PopulationFinishedEvent(const PopulationFinishedEvent& event); wxEvent* Clone() const; TreeModel::Ptr GetTreeModel() const; void SetTreeModel(TreeModel::Ptr store); }; typedef void (wxEvtHandler::*PopulationFinishedFunction)(PopulationFinishedEvent&); // During population some threads send progress information to their connected event handlers class PopulationProgressEvent : public wxEvent { private: wxString _message; public: PopulationProgressEvent(int id = 0); PopulationProgressEvent(const wxString& message, int id = 0); PopulationProgressEvent(const PopulationProgressEvent& event); wxEvent* Clone() const; const wxString& GetMessage() const; void SetMessage(const wxString& message); }; typedef void (wxEvtHandler::*PopulationProgressFunction)(PopulationProgressEvent&); protected: class Node; typedef std::shared_ptr NodePtr; class SearchFunctor; private: const ColumnRecord& _columns; NodePtr _rootNode; int _defaultStringSortColumn; bool _hasDefaultCompare; bool _isListModel; protected: // Constructor to be used by subclasses, allows an existing model to be referenced. // The root node of the existing model will be shared by this instance. // This is not a copy constructor btw. TreeModel(const TreeModel& existingModel); public: TreeModel(const ColumnRecord& columns, bool isListModel = false); virtual ~TreeModel(); // Return the column definition of this model virtual const ColumnRecord& GetColumns() const; // Add a new item below the root element virtual Row AddItem(); // Add a new item below the given element virtual Row AddItem(const wxDataViewItem& parent); // Removes the item, returns TRUE on success virtual bool RemoveItem(const wxDataViewItem& item); // Remove all items matching the predicate, returns the number of deleted items virtual int RemoveItems(const std::function& predicate); // Returns a Row reference to the topmost element virtual Row GetRootItem(); // Returns true if the given column value should render as "enabled" or not virtual bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override; // Removes all items - internally the root node will be kept, but cleared too // This also fires the "Cleared" event to any listeners virtual void Clear(); virtual void SetDefaultStringSortColumn(int index); virtual void SetHasDefaultCompare(bool hasDefaultCompare); // Visit each node in the model, excluding the internal root node virtual void ForeachNode(const VisitFunction& visitFunction); // Visit each node in the model, backwards direction, excluding the internal root node virtual void ForeachNodeReverse(const TreeModel::VisitFunction& visitFunction); // Sorts the entire tree using the given sort function virtual void SortModel(const SortFunction& sortFunction); // Sorts the entire tree by the given column (can also be a IconText column) virtual void SortModelByColumn(const TreeModel::Column& column); // Sort the model by a string-valued column, sorting folders on top. // Pass a boolean-valued "is-a-folder" column to indicate which items are actual folders. virtual void SortModelFoldersFirst(const Column& stringColumn, const Column& isFolderColumn); virtual wxDataViewItem FindString(const std::string& needle, const Column& column); virtual wxDataViewItem FindInteger(long needle, const Column& column); virtual void SetAttr(const wxDataViewItem& item, unsigned int col, const wxDataViewItemAttr& attr) const; virtual void SetIsListModel(bool isListModel); // Search for an item in the given columns (forward), using previousMatch as reference point // search is performed case-insensitively, partial matches are considered ("contains") virtual wxDataViewItem FindNextString(const wxString& needle, const std::vector& columns, const wxDataViewItem& previousMatch = wxDataViewItem()); // Search for an item in the given columns (backwards), using previousMatch as reference point // search is performed case-insensitively, partial matches are considered ("contains") virtual wxDataViewItem FindPrevString(const wxString& needle, const std::vector& columns, const wxDataViewItem& previousMatch = wxDataViewItem()); // Marks a specific column value as enabled or disabled. virtual void SetEnabled(const wxDataViewItem& item, unsigned int col, bool enabled); // Base class implementation / overrides virtual bool HasDefaultCompare() const override; virtual unsigned int GetColumnCount() const override; // return type as reported by wxVariant virtual wxString GetColumnType(unsigned int col) const override; // get value into a wxVariant virtual void GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const override; virtual bool SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned int col) override; virtual bool GetAttr(const wxDataViewItem& item, unsigned int col, wxDataViewItemAttr& attr) const override; virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override; virtual bool IsContainer(const wxDataViewItem& item) const override; virtual unsigned int GetChildren(const wxDataViewItem& item, wxDataViewItemArray& children) const override; virtual wxDataViewItem GetRoot(); virtual bool IsListModel() const override; virtual int Compare(const wxDataViewItem& item1, const wxDataViewItem& item2, unsigned int column, bool ascending) const override; protected: // Returns a reference to the actual rootnode, only allowed for use in subclasses virtual const NodePtr& getRootNode() const; protected: void ForeachNodeRecursive(const TreeModel::NodePtr& node, const VisitFunction& visitFunction); void ForeachNodeRecursiveReverse(const TreeModel::NodePtr& node, const TreeModel::VisitFunction& visitFunction); void SortModelRecursive(const TreeModel::NodePtr& node, const TreeModel::SortFunction& sortFunction); // Sort functor for the SortModelFoldersFirst() method, uses the stringCompare method to compare the actual text values // Pass CompareStringVariants or CompareIconTextVariants as stringCompare. bool CompareFoldersFirst(const wxDataViewItem& a, const wxDataViewItem& b, const Column& stringColumn, const std::function& stringCompareFunc, const Column& isFolderCol); static int CompareStringVariants(const wxVariant& a, const wxVariant& b); static int CompareIconTextVariants(const wxVariant& a, const wxVariant& b); wxDataViewItem FindRecursive(const TreeModel::NodePtr& node, const std::function& predicate); wxDataViewItem FindRecursiveUsingRows(const TreeModel::NodePtr& node, const std::function& predicate); int RemoveItemsRecursively(const wxDataViewItem& parent, const std::function& predicate); }; // wx event macros wxDECLARE_EVENT(EV_TREEMODEL_POPULATION_FINISHED, TreeModel::PopulationFinishedEvent); #define TreeModelPopulationFinishedHandler(func) wxEVENT_HANDLER_CAST(wxutil::TreeModel::PopulationFinishedFunction, func) wxDECLARE_EVENT(EV_TREEMODEL_POPULATION_PROGRESS, TreeModel::PopulationProgressEvent); #define TreeModelPopulationProgressHandler(func) wxEVENT_HANDLER_CAST(wxutil::TreeModel::PopulationProgressFunction, func) } // namespace DarkRadiant-2.5.0/libs/wxutil/TreeModelFilter.cpp000066400000000000000000000114601321750546400217320ustar00rootroot00000000000000#include "TreeModelFilter.h" #include namespace wxutil { class TreeModelFilter::ChildModelNotifier : public wxDataViewModelNotifier { private: TreeModelFilter* _owner; public: ChildModelNotifier(TreeModelFilter* owner) : _owner(owner) {} virtual bool ItemAdded(const wxDataViewItem& parent, const wxDataViewItem& item) { // Only pass the call if the item is relevant according to the filter criteria if (_owner->ItemIsVisible(parent) && _owner->ItemIsVisible(item)) { return _owner->ItemAdded(parent, item); } return true; } virtual bool ItemDeleted(const wxDataViewItem& parent, const wxDataViewItem& item) { if (_owner->ItemIsVisible(parent) && _owner->ItemIsVisible(item)) { return _owner->ItemDeleted(parent, item); } return true; } virtual bool ItemChanged(const wxDataViewItem& item) { if (_owner->ItemIsVisible(item)) { return _owner->ItemChanged(item); } return true; } virtual bool ValueChanged(const wxDataViewItem& item, unsigned int col) { if (_owner->ItemIsVisible(item)) { return _owner->ValueChanged(item, col); } return _owner->ValueChanged(item, col); } virtual bool Cleared() { return _owner->Cleared(); } virtual void Resort() { _owner->Resort(); } }; TreeModelFilter::TreeModelFilter(TreeModel::Ptr childModel, const Column* filterColumn) : TreeModel(*childModel), // reference the existing model _childModel(childModel), _notifier(NULL), _filterColumn(NULL) { _notifier = new ChildModelNotifier(this); _childModel->AddNotifier(_notifier); if (filterColumn != NULL) { SetFilterColumn(*filterColumn); } } TreeModelFilter::~TreeModelFilter() { _childModel->RemoveNotifier(_notifier); } TreeModel::Ptr TreeModelFilter::GetChildModel() { return _childModel; } void TreeModelFilter::SetFilterColumn(const Column& column) { assert(column.type == Column::Boolean); _filterColumn = &column; } void TreeModelFilter::SetVisibleFunc(const VisibleFunc& visibleFunc) { _customVisibleFunc = visibleFunc; } bool TreeModelFilter::ItemIsVisible(const wxDataViewItem& item) const { if (!item.IsOk()) return true; Row row(item, *const_cast(this)); return ItemIsVisible(row); } bool TreeModelFilter::ItemIsVisible(Row& row) const { // A custom filter logic always takes precedence over the filter column if (_customVisibleFunc) { return _customVisibleFunc(row); } if (_filterColumn != NULL) { return row[*_filterColumn].getBool(); } return true; // default fallback evaluates to true } void TreeModelFilter::ForeachNode(const VisitFunction& visitFunction) { _childModel->ForeachNode([&] (Row& row) { // Only visit unfiltered items if (ItemIsVisible(row)) { visitFunction(row); } }); } wxDataViewItem TreeModelFilter::FindString(const std::string& needle, int column) { return FindRecursiveUsingRows(getRootNode(), [&] (Row& row)->bool { if (!ItemIsVisible(row)) { return false; // skip filtered items } return static_cast(row[GetColumns()[column]]) == needle; }); } wxDataViewItem TreeModelFilter::FindInteger(long needle, int column) { return FindRecursiveUsingRows(getRootNode(), [&] (Row& row)->bool { if (!ItemIsVisible(row)) { return false; // skip filtered items } return row[GetColumns()[column]].getInteger() == needle; }); } bool TreeModelFilter::IsContainer(const wxDataViewItem& item) const { if (!item.IsOk()) { return true; } #ifdef __WXGTK__ // greebo: The GTK DataViewCtrl implementation treats nodes differently // based on whether they have children or not. If a tree model node has no children // now it's not possible to add any children later on, causing assertions. // wxGTK wants to know *in advance* whether a node has children, so let's assume true // unless this is a listmodel (in which case non-root nodes never have children) return !IsListModel() ? true : false; #else bool isContainer = _childModel->IsContainer(item); if (!isContainer) { return false; } // Check if the node actually has visible children wxDataViewItemArray children; return GetChildren(item, children) > 0; #endif } unsigned int TreeModelFilter::GetChildren(const wxDataViewItem& item, wxDataViewItemArray& children) const { if (_filterColumn == NULL && !_customVisibleFunc) { return _childModel->GetChildren(item, children); } // Get the raw child list wxDataViewItemArray unfilteredChildren; _childModel->GetChildren(item, unfilteredChildren); // Only add the visible ones to the result set std::for_each(unfilteredChildren.begin(), unfilteredChildren.end(), [&] (const wxDataViewItem& item) { if (ItemIsVisible(item)) { children.Add(item); } }); return static_cast(children.size()); } } // namespace DarkRadiant-2.5.0/libs/wxutil/TreeModelFilter.h000066400000000000000000000051031321750546400213740ustar00rootroot00000000000000#pragma once #include "TreeModel.h" namespace wxutil { /** * Filter class that can be created on top of an existing * TreeModel instance to filter its data and return only * those items that are matching the given criteria. * * Once a TreeModelFilter is constructede it will increase * the refcount of the underlying TreeModel. When the * TreeModelFilter is destroyed, the reference count of the * child TreeModel is decreased as well, so it's safe to * transfer the ownership of the child model to this filter. * * Upon constructor, the root node of the child model will be shared * by this instance, all settable properties like "hasDefaultCompare" * will be copied over, but can be changed independently for this instance. * * Note that the TreeModelFilter cannot provide a different ordering scheme * than the child model, any Sort*() calls are issued directly to the child. */ class TreeModelFilter : public TreeModel { public: typedef std::function VisibleFunc; protected: TreeModel::Ptr _childModel; // When the child model issues any events, we need to pass them to the // wxDataViewCtrl owning this TreeModelFilter. class ChildModelNotifier; ChildModelNotifier* _notifier; // The filter column const Column* _filterColumn; // Custom filter logic VisibleFunc _customVisibleFunc; public: typedef wxObjectDataPtr Ptr; TreeModelFilter(TreeModel::Ptr childModel, const Column* filterColumn = NULL); virtual ~TreeModelFilter(); // Methods only provided by TreeModelFilter to implement filtering TreeModel::Ptr GetChildModel(); // Set the boolean-valued column filtering the child model void SetFilterColumn(const Column& column); // Alternative to SetFilterColumn: use a custom function to evaluate // whether a given row is visible or not. // A non-empty VisibleFunc always takes precedence over a filter column. void SetVisibleFunc(const VisibleFunc& visibleFunc); bool ItemIsVisible(const wxDataViewItem& item) const; bool ItemIsVisible(Row& row) const; // We need to provide some TreeModel methods on our own, // to implement filtering // Visit each node in the model, excluding the internal root node virtual void ForeachNode(const VisitFunction& visitFunction); virtual wxDataViewItem FindString(const std::string& needle, int column); virtual wxDataViewItem FindInteger(long needle, int column); virtual bool IsContainer(const wxDataViewItem& item) const; virtual unsigned int GetChildren(const wxDataViewItem& item, wxDataViewItemArray& children) const; }; } // namespace DarkRadiant-2.5.0/libs/wxutil/TreeView.cpp000066400000000000000000000207721321750546400204440ustar00rootroot00000000000000#include "TreeView.h" #include #include #include #include namespace wxutil { TreeView::TreeView(wxWindow* parent, TreeModel::Ptr model, long style) : wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style), _searchPopup(NULL) { EnableAutoColumnWidthFix(); if (model) { AssociateModel(model.get()); } Connect(wxEVT_CHAR, wxKeyEventHandler(TreeView::_onChar), NULL, this); Connect(EV_TREEVIEW_SEARCH_EVENT, SearchEventHandler(TreeView::_onSearch), NULL, this); Connect(wxEVT_DATAVIEW_ITEM_ACTIVATED, wxDataViewEventHandler(TreeView::_onItemActivated), NULL, this); } TreeView* TreeView::Create(wxWindow* parent, long style) { return new TreeView(parent, TreeModel::Ptr(), style); } // Construct a TreeView using the given TreeModel, which will be associated // with this view (refcount is automatically decreased by one). TreeView* TreeView::CreateWithModel(wxWindow* parent, TreeModel::Ptr model, long style) { return new TreeView(parent, model, style); } TreeView::~TreeView() {} bool TreeView::AssociateModel(wxDataViewModel* model) { // We're changing models, so unselect everything first, // even if it's the same model again, the tree might have changed. UnselectAll(); // Pass the call to the regular routine return wxDataViewCtrl::AssociateModel(model); } // Enable the automatic recalculation of column widths void TreeView::EnableAutoColumnWidthFix(bool enable) { if (enable) { Connect(wxEVT_DATAVIEW_ITEM_EXPANDED, wxDataViewEventHandler(TreeView::_onItemExpanded), NULL, this); } else { Disconnect(wxEVT_DATAVIEW_ITEM_EXPANDED, wxDataViewEventHandler(TreeView::_onItemExpanded), NULL, this); } } void TreeView::TriggerColumnSizeEvent(const wxDataViewItem& item) { if (GetModel() == NULL) return; // Trigger a column size event on the first row wxDataViewItemArray children; GetModel()->GetChildren(item, children); std::for_each(children.begin(), children.end(), [&](wxDataViewItem& item) { GetModel()->ItemChanged(item); }); } void TreeView::ExpandTopLevelItems() { TreeModel* model = dynamic_cast(GetModel()); if (model == nullptr) return; // Expand the first layer wxDataViewItemArray children; model->GetChildren(model->GetRoot(), children); std::for_each(children.begin(), children.end(), [&](const wxDataViewItem& item) { Expand(item); }); } void TreeView::ResetSortingOnAllColumns() { #if wxCHECK_VERSION(3, 1, 0) && defined(__WXMSW__ ) ResetAllSortColumns(); #else // We don't have ResetAllSortColumns in wxWidgets 3.0.x wxDataViewColumn* col = GetSortingColumn(); if (col != nullptr) { col->UnsetAsSortKey(); } #endif } void TreeView::AddSearchColumn(const TreeModel::Column& column) { // Only text columns are supported right now assert(column.type == TreeModel::Column::String || column.type == TreeModel::Column::IconText); _colsToSearch.push_back(column); } bool TreeView::HasActiveSearchPopup() { return _searchPopup != NULL; } #if !defined(__linux__) void TreeView::Rebuild() { TreeModel* model = dynamic_cast(GetModel()); if (model == nullptr) return; // Trigger a rebuild of the tree wxDataViewItemArray children; wxDataViewItem root = model->GetRoot(); model->GetChildren(root, children); // By calling deleted and added, the internal dataview's nodes // are invalidated which effectively is a rebuild of everything. for (auto child : children) { model->ItemDeleted(root, child); model->ItemAdded(root, child); } } #endif void TreeView::_onItemExpanded(wxDataViewEvent& ev) { // This should force a recalculation of the column width if (GetModel() != NULL) { GetModel()->ItemChanged(ev.GetItem()); } ev.Skip(); } void TreeView::_onItemActivated(wxDataViewEvent& ev) { if (!IsExpanded(ev.GetItem())) { Expand(ev.GetItem()); } else { Collapse(ev.GetItem()); } } wxDEFINE_EVENT(EV_TREEVIEW_SEARCH_EVENT, TreeView::SearchEvent); TreeView::SearchEvent::SearchEvent(int id) : wxEvent(id, EV_TREEVIEW_SEARCH_EVENT) {} TreeView::SearchEvent::SearchEvent(const wxString& searchString, int id) : wxEvent(id, EV_TREEVIEW_SEARCH_EVENT), _searchString(searchString) {} // You *must* copy here the data to be transported TreeView::SearchEvent::SearchEvent(const TreeView::SearchEvent& ev) : wxEvent(ev), _searchString(ev._searchString) {} // Required for sending with wxPostEvent() wxEvent* TreeView::SearchEvent::Clone() const { return new SearchEvent(*this); } const wxString& TreeView::SearchEvent::GetSearchString() const { return _searchString; } // The custom popup window containing our search box class TreeView::SearchPopupWindow : public wxPopupTransientWindow { private: TreeView* _owner; wxTextCtrl* _entry; public: SearchPopupWindow(TreeView* owner) : wxPopupTransientWindow(owner), _owner(owner), _entry(NULL) { SetSizer(new wxBoxSizer(wxVERTICAL)); _entry = new wxTextCtrl(this, wxID_ANY); GetSizer()->Add(_entry, 1, wxEXPAND | wxALL, 6); Layout(); Fit(); // Position this control in the bottom right corner wxPoint popupPos = owner->GetScreenPosition() + owner->GetSize() - GetSize(); Position(popupPos, wxSize(0, 0)); Connect(wxEVT_CHAR, wxKeyEventHandler(SearchPopupWindow::OnChar), NULL, this); } void OnChar(wxKeyEvent& ev) { HandleKey(ev); } virtual void OnDismiss() { // Send an event to the parent TreeView SearchEvent searchEvent("", SearchEvent::POPUP_DISMISSED); _owner->HandleWindowEvent(searchEvent); wxPopupTransientWindow::OnDismiss(); } void HandleKey(wxKeyEvent& ev) { // Adapted this from the wxWidgets docs wxChar uc = ev.GetUnicodeKey(); if (uc != WXK_NONE) { // It's a "normal" character. Notice that this includes // control characters in 1..31 range, e.g. WXK_RETURN or // WXK_BACK, so check for them explicitly. if (uc >= 32) { _entry->SetValue(_entry->GetValue() + ev.GetUnicodeKey()); // Send an event to the parent TreeView SearchEvent searchEvent(_entry->GetValue(), SearchEvent::SEARCH); _owner->HandleWindowEvent(searchEvent); } else if (ev.GetKeyCode() == WXK_ESCAPE) { DismissAndNotify(); } else if (ev.GetKeyCode() == WXK_BACK) { _entry->SetValue(_entry->GetValue().RemoveLast(1)); // Send an event to the parent TreeView SearchEvent searchEvent(_entry->GetValue(), SearchEvent::SEARCH); _owner->HandleWindowEvent(searchEvent); } } else // No Unicode equivalent. { // Cursor events are special if (ev.GetKeyCode() == WXK_UP || ev.GetKeyCode() == WXK_DOWN) { SearchEvent searchEvent(_entry->GetValue(), ev.GetKeyCode() == WXK_UP ? SearchEvent::SEARCH_PREV_MATCH : SearchEvent::SEARCH_NEXT_MATCH); _owner->HandleWindowEvent(searchEvent); } } } }; void TreeView::_onChar(wxKeyEvent& ev) { if (GetModel() == NULL || _colsToSearch.empty()) { ev.Skip(); return; } // Adapted this from the wxWidgets docs wxChar uc = ev.GetUnicodeKey(); if (uc != WXK_NONE && uc >= 32) { // It's a "normal" character, start the search if (_searchPopup == NULL) { _searchPopup = new SearchPopupWindow(this); _searchPopup->Popup(); _curSearchMatch = wxDataViewItem(); } // Handle the first key immediately _searchPopup->HandleKey(ev); } // Don't eat the event ev.Skip(); } void TreeView::_onSearch(SearchEvent& ev) { if (GetModel() == NULL) { ev.Skip(); // no model attached return; } TreeModel* model = dynamic_cast(GetModel()); if (model == NULL) { ev.Skip(); // not a TreeModel return; } wxDataViewItem oldMatch = _curSearchMatch; // Handle the search switch (ev.GetId()) { case SearchEvent::SEARCH: _curSearchMatch = model->FindNextString(ev.GetSearchString(), _colsToSearch); break; case SearchEvent::SEARCH_NEXT_MATCH: _curSearchMatch = model->FindNextString(ev.GetSearchString(), _colsToSearch, _curSearchMatch); break; case SearchEvent::SEARCH_PREV_MATCH: _curSearchMatch = model->FindPrevString(ev.GetSearchString(), _colsToSearch, _curSearchMatch); break; case SearchEvent::POPUP_DISMISSED: _searchPopup = NULL; _curSearchMatch = wxDataViewItem(); break; }; if (oldMatch != _curSearchMatch && _curSearchMatch.IsOk()) { UnselectAll(); Select(_curSearchMatch); EnsureVisible(_curSearchMatch); // Synthesise a selection changed signal wxDataViewEvent le(wxEVT_DATAVIEW_SELECTION_CHANGED, GetId()); le.SetEventObject(this); le.SetModel(GetModel()); le.SetItem(_curSearchMatch); ProcessWindowEvent(le); } } } // namespace DarkRadiant-2.5.0/libs/wxutil/TreeView.h000066400000000000000000000062621321750546400201070ustar00rootroot00000000000000#pragma once #include #include #include "TreeModel.h" namespace wxutil { /** * greebo: Extension of the regular wxDataViewCtrl to add * a few regularly need improvements, like automatic column sizing * for treeviews (a thing that seems to be problematic in the * pure wxDataViewCtrl). * * Use the named constructors Create*() to instantiate a new TreeView. */ class TreeView : public wxDataViewCtrl { protected: class SearchPopupWindow; SearchPopupWindow* _searchPopup; std::vector _colsToSearch; wxDataViewItem _curSearchMatch; TreeView(wxWindow* parent, TreeModel::Ptr model, long style); public: typedef wxWindowPtr Ptr; // Create a TreeView without model (single-selection mode) static TreeView* Create(wxWindow* parent, long style = wxDV_SINGLE); // Construct a TreeView using the given TreeModel, which will be associated // with this view (refcount is automatically decreased by one). static TreeView* CreateWithModel(wxWindow* parent, TreeModel::Ptr model, long style = wxDV_SINGLE); virtual ~TreeView(); // ovverride wxDataViewCtrl to make it more robust virtual bool AssociateModel(wxDataViewModel* model); // Enable the automatic recalculation of column widths void EnableAutoColumnWidthFix(bool enable = true); // Trigger an ItemChanged event on all the children of the given node // Call this if your text columns get squashed and are not sized correctly void TriggerColumnSizeEvent(const wxDataViewItem& item = wxDataViewItem()); // Expands the first level of all the top-level items void ExpandTopLevelItems(); // Removes any sort keys void ResetSortingOnAllColumns(); // Adds a column to search when the user starts typing void AddSearchColumn(const TreeModel::Column& column); // Returns true if the treeview search popup is currently visible bool HasActiveSearchPopup(); #if !defined(__linux__) // Triggers a rebuild of the tree (done by calling ItemDeleted+ItemAdded for each // of the root's immediate children. void Rebuild(); #endif public: // Event handled by the TreeView when the user triggers a search // or tries to navigate between search results class SearchEvent : public wxEvent { private: wxString _searchString; public: enum EventType { SEARCH, // user has entered something, changed the search terms SEARCH_NEXT_MATCH, // user wants to display the next match SEARCH_PREV_MATCH, // user wants to display the prev match POPUP_DISMISSED, // popup has been dismissed, search has ended }; SearchEvent(int id = SEARCH); SearchEvent(const wxString& searchString, int id = SEARCH); SearchEvent(const SearchEvent& ev); wxEvent* Clone() const; const wxString& GetSearchString() const; }; typedef void (wxEvtHandler::*SearchHandlerFunction)(SearchEvent&); private: void _onItemExpanded(wxDataViewEvent& ev); void _onChar(wxKeyEvent& ev); void _onSearch(SearchEvent& ev); void _onItemActivated(wxDataViewEvent& ev); }; // wx event macros wxDECLARE_EVENT(EV_TREEVIEW_SEARCH_EVENT, TreeView::SearchEvent); #define SearchEventHandler(func) wxEVENT_HANDLER_CAST(wxutil::TreeView::SearchHandlerFunction, func) } // namespace DarkRadiant-2.5.0/libs/wxutil/VFSTreePopulator.cpp000066400000000000000000000053031321750546400220670ustar00rootroot00000000000000#include "VFSTreePopulator.h" #include "TreeModel.h" namespace wxutil { VFSTreePopulator::VFSTreePopulator(const TreeModel::Ptr& store, const wxDataViewItem& toplevel) : _store(store), _topLevel(toplevel) {} // Destructor frees the DirIterMap VFSTreePopulator::~VFSTreePopulator() { _iters.clear(); } // Interface add function void VFSTreePopulator::addPath(const std::string& path) { // Pass an empty ColumnPopulationCallback addRecursive(path, [](TreeModel::Row&, const std::string&, bool) {}); // Add the explicit path to the set, we need it later // when being visited by the Visitor implementation _explicitPaths.insert(path); } void VFSTreePopulator::addPath(const std::string& path, const ColumnPopulationCallback& func) { // Call the addRecursive method to create all necessary nodes addRecursive(path, func); // Note that this implementation doesn't maintain the _explicitPaths set // since we don't need that information. } // Recursive add function const wxDataViewItem& VFSTreePopulator::addRecursive(const std::string& path, const ColumnPopulationCallback& func, int recursionLevel) { // Look up candidate in the map and return it if found NamedIterMap::iterator it = _iters.find(path); if (it != _iters.end()) { return it->second; } /* Otherwise, split the path on its rightmost slash, call recursively on the * first half in order to add the parent node, then add the second half as * a child. Recursive bottom-out is when there is no slash (top-level node). */ // Find rightmost slash std::size_t slashPos = path.rfind("/"); // Call recursively to get parent iter, leaving it at the toplevel if // there is no slash const wxDataViewItem& parIter = slashPos != std::string::npos ? addRecursive(path.substr(0, slashPos), func, recursionLevel + 1) : _topLevel; // Append a node to the tree view for this child TreeModel::Row row = _store->AddItem(parIter); // Call the population callback. If recursionLevel > 0 we had at least one // path split operation, so this must be a folder func(row, slashPos != std::string::npos ? path.substr(slashPos+1) : path, recursionLevel > 0); // Add a copy of the wxDataViewItem to our hashmap and return it std::pair result = _iters.insert( NamedIterMap::value_type(path, row.getItem())); return result.first->second; } // Traversal function void VFSTreePopulator::forEachNode(Visitor& visitor) { // Visit every entry in the iter map for (NamedIterMap::iterator i = _iters.begin(); i != _iters.end(); ++i) { TreeModel::Row row(i->second, *_store); visitor.visit(*_store, row, i->first, _explicitPaths.find(i->first) != _explicitPaths.end()); } } } // namespace DarkRadiant-2.5.0/libs/wxutil/VFSTreePopulator.h000066400000000000000000000113201321750546400215300ustar00rootroot00000000000000#pragma once #include #include #include #include "TreeModel.h" #include namespace wxutil { /** * Utility class to construct a TreeModel from a series of string paths * in the form "models/first/second/object.lwo" or similar. The class accepts * the Tree Store and then string paths, one by one, adding each one to the * tree in the appropriate place. * * Since the VFSTreePopulator has no knowledge of the column data to be inserted * into the tree, it does not set any of the values but merely calls * TreeStore::AddItem() to get an iterator pointing to the newly-added * row. In order to insert the data, the calling code must then call the * visitor function which will provide the visitor with two objects - the * created iterator and the full input string which the visitor can then * use to populate the data appropriately. * * When the VFSTreePopulator is destroyed it will free any temporary structures * used during tree creation, such as the hashtable of iterator objects * used to locate parent nodes. The TreeModel will not be destroyed since it * is owned by the calling code. */ class VFSTreePopulator { // The tree store to populate TreeModel::Ptr _store; // Toplevel node to add children under wxDataViewItem _topLevel; // Maps of names to corresponding treemodel iterators, for both intermediate // paths and explicitly presented paths typedef std::map NamedIterMap; NamedIterMap _iters; // Set of paths that are passed in through addPath(), to distinguish them // from intermediate constructed paths std::set _explicitPaths; public: /** * Construct a VFSTreePopulator which will populate the given tree store. * * @param store * Tree store to populate. * * @param toplevel * TreeModel::iterator pointing to the toplevel node, under which all paths should * be added. Default is empty to indicate that paths should be added under * the tree root. */ VFSTreePopulator(const TreeModel::Ptr& store, const wxDataViewItem& toplevel = wxDataViewItem()); /** Destroy the VFSTreePopulator and all temporary data. */ virtual ~VFSTreePopulator(); /** Add a single VFS string to the tree, which will be split automatically * and inserted at the correct place in the tree. */ void addPath(const std::string& path); // Column population function. The inserted Row will be passed as argument. typedef std::function ColumnPopulationCallback; /** * Like addPath() above this method also takes a function object as * argument to allow for immediate population of the TreeStore's column values. * As soon as the item has been created in the correct spot of the tree * the callback will be invoked passing the TreeModel::Row as argument. * * Client code needs to decide between this or the regular addPath() method * and cannot mix between these two, as this variant here does not keep track * of the "path-is-explicit" property and therefore doesn't support the * forEachNode() method being called with a Visitor. */ void addPath(const std::string& path, const ColumnPopulationCallback& func); /** Visitor interface. */ struct Visitor { virtual ~Visitor() {} /** * Visit callback function, called for each node in the tree. * * @param store * The tree store to insert into. * * @param row * The row to be processed. * * @param path * Full VFS path of the current node, as presented to addPath(). * * @param isExplicit * true if the path was explicitly inserted via addPath(), false if the * path was created as an intermediate parent of another explicit path. */ virtual void visit(TreeModel& store, wxutil::TreeModel::Row& row, const std::string& path, bool isExplicit) = 0; }; /** * Visit each node in the constructed tree, passing the wxDataViewItem and * the VFS string to the visitor object so that data can be inserted. * Note that this method is not meant to be used if the addPath() overload * taking a ColumnPopulationCallback has been used earlier on. */ void forEachNode(Visitor& visitor); private: // Main recursive add function. Accepts a VFS path and adds the new node, // calling itself if necessary to add all of the parent directories, then // returns a wxDataViewItem pointing to the new node. const wxDataViewItem& addRecursive(const std::string& path, const ColumnPopulationCallback& func, int recursionLevel = 0); }; } // namespace DarkRadiant-2.5.0/libs/wxutil/WindowPosition.cpp000066400000000000000000000107521321750546400217030ustar00rootroot00000000000000#include "WindowPosition.h" #include "iregistry.h" #include "string/convert.h" #include "MultiMonitor.h" #include #include namespace { const int DEFAULT_POSITION_X = 50; const int DEFAULT_POSITION_Y = 25; const int DEFAULT_SIZE_X = 400; const int DEFAULT_SIZE_Y = 300; } namespace wxutil { WindowPosition::WindowPosition() : _position(DEFAULT_POSITION_X, DEFAULT_POSITION_Y), _size(DEFAULT_SIZE_X, DEFAULT_SIZE_Y), _window(NULL) {} void WindowPosition::initialise(wxTopLevelWindow* window, const std::string& windowStateKey, float defaultXFraction, float defaultYFraction) { // Set up events and such connect(window); // Load from registry if possible if (GlobalRegistry().keyExists(windowStateKey)) { loadFromPath(windowStateKey); } else { fitToScreen(defaultXFraction, defaultYFraction); } applyPosition(); } // Connect the passed window to this object void WindowPosition::connect(wxTopLevelWindow* window) { if (_window != NULL) { disconnect(_window); } _window = window; applyPosition(); window->Connect(wxEVT_SIZE, wxSizeEventHandler(WindowPosition::onResize), NULL, this); window->Connect(wxEVT_MOVE, wxMoveEventHandler(WindowPosition::onMove), NULL, this); } void WindowPosition::disconnect(wxTopLevelWindow* window) { _window = NULL; window->Disconnect(wxEVT_SIZE, wxSizeEventHandler(WindowPosition::onResize), NULL, this); window->Disconnect(wxEVT_MOVE, wxMoveEventHandler(WindowPosition::onMove), NULL, this); } const WindowPosition::Position& WindowPosition::getPosition() const { return _position; } const WindowPosition::Size& WindowPosition::getSize() const { return _size; } void WindowPosition::setPosition(int x, int y) { _position[0] = x; _position[1] = y; } void WindowPosition::setSize(int width, int height) { _size[0] = width; _size[1] = height; } void WindowPosition::saveToPath(const std::string& path) { GlobalRegistry().setAttribute(path, "xPosition", string::to_string(_position[0])); GlobalRegistry().setAttribute(path, "yPosition", string::to_string(_position[1])); GlobalRegistry().setAttribute(path, "width", string::to_string(_size[0])); GlobalRegistry().setAttribute(path, "height", string::to_string(_size[1])); } void WindowPosition::loadFromPath(const std::string& path) { _position[0] = string::convert(GlobalRegistry().getAttribute(path, "xPosition")); _position[1] = string::convert(GlobalRegistry().getAttribute(path, "yPosition")); _size[0] = string::convert(GlobalRegistry().getAttribute(path, "width")); _size[1] = string::convert(GlobalRegistry().getAttribute(path, "height")); } // Applies the internally stored size/position info to the GtkWindow // The algorithm was adapted from original GtkRadiant code (window.h) void WindowPosition::applyPosition() { if (_window == NULL) return; // TODO: What about multi-monitor setups with overlapping windows? wxDisplay display(wxDisplay::GetFromWindow(_window)); // Sanity check of the window position if (_position[0] < 0 || _position[1] < 0 || _position[0] > display.GetGeometry().GetWidth() || _position[1] > display.GetGeometry().GetHeight()) { _window->CenterOnParent(); } else { _window->SetPosition(wxPoint(_position[0], _position[1])); } _window->SetSize(_size[0], _size[1]); } // Reads the position from the window void WindowPosition::readPosition() { if (_window != nullptr) { _window->GetScreenPosition(&_position[0], &_position[1]); _window->GetSize(&_size[0], &_size[1]); } } void WindowPosition::fitToScreen(float xfraction, float yfraction) { if (_window == NULL) return; wxDisplay display(wxDisplay::GetFromWindow(_window)); // Pass the call fitToScreen(display.GetGeometry(), xfraction, yfraction); } void WindowPosition::fitToScreen(const wxRect& screen, float xfraction, float yfraction) { _size[0] = static_cast(screen.GetWidth() * xfraction) - 12; _size[1] = static_cast(screen.GetHeight() * yfraction) - 48; _position[0] = screen.GetX() + static_cast((screen.GetWidth() - _size[0] - 12)/2); _position[1] = screen.GetY() + static_cast((screen.GetHeight() - _size[1] - 48)/2); } void WindowPosition::onResize(wxSizeEvent& ev) { setSize(ev.GetSize().GetWidth(), ev.GetSize().GetHeight()); ev.Skip(); } void WindowPosition::onMove(wxMoveEvent& ev) { setPosition(ev.GetPosition().x, ev.GetPosition().y); ev.Skip(); } } // namespace DarkRadiant-2.5.0/libs/wxutil/WindowPosition.h000066400000000000000000000050331321750546400213440ustar00rootroot00000000000000#pragma once #include "math/Vector2.h" #include #include class wxTopLevelWindow; /** * greebo: A WindowPosition object keeps track of the window's size and position. * * Use the connect() method to connect a wxFrame to this object. * * Use the loadFromNode() and saveToNode() methods to save the internal * size info into the given xml::Node * * This is used by the XYWnd classes to save/restore the window state upon restart. */ namespace wxutil { class WindowPosition : public wxEvtHandler { public: typedef BasicVector2 Position; typedef BasicVector2 Size; private: // The size and position of this object Position _position; Size _size; // The connected window wxTopLevelWindow* _window; public: WindowPosition(); // All-in-one method to connect a window and load its state from the given path // or (if the key is not existent) set up a reasonable default position/size. void initialise(wxTopLevelWindow* window, const std::string& windowStateKey, float defaultXFraction, float defaultYFraction); // Connect the passed window to this object void connect(wxTopLevelWindow* window); void disconnect(wxTopLevelWindow* window); const Position& getPosition() const; const Size& getSize() const; void setPosition(int x, int y); void setSize(int width, int height); // Loads/saves the window position to the given Registry path void saveToPath(const std::string& path); void loadFromPath(const std::string& path); // Applies the internally stored size/position info to the GtkWindow // The algorithm was adapted from original GtkRadiant code (window.h) void applyPosition(); // Reads the position from the GtkWindow void readPosition(); /** * Fits the current position/dimension to the screen of the connected * window. This object has to be connected to a GtkWindow before * the method can function properly. * * @xfraction,yfraction: the fraction of the screen which the window * should occupy. (e.g. Pass 0.5/0.66 to let the window half of the * monitor width and two thirds of the monitor height. * * Note: applyPosition() has to be called for the changes to take effect. */ void fitToScreen(float xfraction = 1, float yfraction = 1); // Adjusts the position/dimensions to fit on the given screen (wxRect) void fitToScreen(const wxRect& screen, float xfraction = 1, float yfraction = 1); private: // The callback that gets invoked on window size/position changes void onResize(wxSizeEvent& ev); void onMove(wxMoveEvent& ev); }; } // namespace DarkRadiant-2.5.0/libs/wxutil/XmlResourceBasedWidget.h000066400000000000000000000036031321750546400227240ustar00rootroot00000000000000#pragma once #include #include #include #include namespace wxutil { /** * Base class for wxWindows that load all (or parts of their) controls * from an XRC file in the application's ui/ directory. */ class XmlResourceBasedWidget { protected: // Loads a named Panel from the XRC resources wxPanel* loadNamedPanel(wxWindow* parent, const std::string& name) { wxPanel* panel = wxXmlResource::Get()->LoadPanel(parent, name); assert(panel != NULL); return panel; } static const wxToolBarToolBase* getToolBarToolByLabel(wxToolBarBase* toolbar, const std::string& name) { wxString wxName(name); for (unsigned int i = 0; i < toolbar->GetToolsCount(); i++) { const wxToolBarToolBase* candidate = toolbar->GetToolByPos(i); if (candidate->GetLabel() == wxName) { return candidate; } } return NULL; } // Find a named panel among the parent's children wxPanel* findNamedPanel(wxWindow* parent, const std::string& name) { return findNamedObject(parent, name); } // Find a named panel among the parent's children template ObjectClass* findNamedObject(wxWindow* parent, const std::string& name) { wxString wxName(name); ObjectClass* named = dynamic_cast(parent->FindWindow(name)); assert(named != NULL); return named; } // Find a named panel among the parent's children, doesn't throw when the item is not found template ObjectClass* tryGetNamedObject(wxWindow* parent, const std::string& name) { wxString wxName(name); ObjectClass* named = dynamic_cast(parent->FindWindow(name)); return named; } void makeLabelBold(wxWindow* parent, const std::string& widgetName) { wxStaticText* text = findNamedObject(parent, widgetName); text->SetFont(text->GetFont().Bold()); } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/clipboard.cpp000066400000000000000000000012271321750546400206430ustar00rootroot00000000000000#include "clipboard.h" #include namespace wxutil { void copyToClipboard(const std::string& contents) { if (wxTheClipboard->Open()) { // This data objects are held by the clipboard, so do not delete them in the app. wxTheClipboard->SetData(new wxTextDataObject(contents)); wxTheClipboard->Close(); } } std::string pasteFromClipboard() { std::string returnValue; if (wxTheClipboard->Open()) { if (wxTheClipboard->IsSupported(wxDF_TEXT)) { wxTextDataObject data; wxTheClipboard->GetData(data); returnValue = data.GetText().ToStdString(); } wxTheClipboard->Close(); } return returnValue; } } // namespace gtkutil DarkRadiant-2.5.0/libs/wxutil/clipboard.h000066400000000000000000000003761321750546400203140ustar00rootroot00000000000000#pragma once #include namespace wxutil { /// Copy the given string to the system clipboard void copyToClipboard(const std::string& str); /// Return the contents of the clipboard as a string std::string pasteFromClipboard(); } DarkRadiant-2.5.0/libs/wxutil/dialog/000077500000000000000000000000001321750546400174355ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/wxutil/dialog/Dialog.cpp000066400000000000000000000132151321750546400213420ustar00rootroot00000000000000#include "Dialog.h" #include "imainframe.h" #include "itextstream.h" #include "DialogElements.h" #include "../EntryAbortedException.h" #include namespace wxutil { Dialog::Dialog(const std::string& title, wxWindow* parent) : _dialog(new wxutil::DialogBase(title, parent != NULL ? parent : GlobalMainFrame().getWxTopLevelWindow())), _result(RESULT_CANCELLED), _elementsTable(new wxFlexGridSizer(1, 2, 6, 12)), // Nx2 table _constructed(false), _highestUsedHandle(0), _focusWidget(0) { _dialog->SetSizer(new wxBoxSizer(wxVERTICAL)); _dialog->GetSizer()->Add(_elementsTable, 1, wxEXPAND | wxALL, 12); } Dialog::~Dialog() { // wxWidgets is responsible of deleting the dialog from this point _dialog->Destroy(); } void Dialog::setTitle(const std::string& title) { // Dispatch this call to the base class _dialog->SetTitle(title); } void Dialog::setFocus(Handle handle) { _focusWidget = handle; } void Dialog::setDefaultSize(int width, int height) { _dialog->SetSize(width, height); } wxWindow* Dialog::getElementParent() { return _dialog; } void Dialog::createButtons() { // Buttons are added to the dialog last _dialog->GetSizer()->Add(_dialog->CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT | wxBOTTOM | wxLEFT | wxRIGHT, 12); } void Dialog::construct() { // Call the virtual method createButtons(); } ui::IDialog::Handle Dialog::addElement(const DialogElementPtr& element) { wxStaticText* first = element->getLabel(); wxWindow* second = element->getValueWidget(); if (first == NULL && second == NULL) return ui::INVALID_HANDLE; // At least one of the widgets is non-NULL // Acquire a new handle Handle handle = ++_highestUsedHandle; // Store this element in the map _elements[handle] = element; int numRows = static_cast(_elements.size()); // Push the widgets into the dialog, resize the table to fit _elementsTable->SetRows(numRows); if (first != second) { // Widgets are not equal, check for NULL-ness if (second == NULL) { // One single widget _elementsTable->Add(first, 1, wxEXPAND); _elementsTable->Add(new wxStaticText(_dialog, wxID_ANY, "")); } else if (first == NULL) { // One single widget _elementsTable->Add(new wxStaticText(_dialog, wxID_ANY, "")); _elementsTable->Add(second, 1, wxEXPAND); } else // Both are non-NULL { // The label (first column) _elementsTable->Add(first, 0, wxALIGN_CENTER_VERTICAL); // The edit widgets (second column) _elementsTable->Add(second, 1, wxEXPAND); } } else // The widgets are the same, non-NULL { _elementsTable->Add(first, 1); _elementsTable->Add(new wxStaticText(_dialog, wxID_ANY, "")); } return handle; } ui::IDialog::Handle Dialog::addLabel(const std::string& text) { return addElement(DialogElementPtr(new DialogLabel(_dialog, text))); } ui::IDialog::Handle Dialog::addComboBox(const std::string& label, const ComboBoxOptions& options) { return addElement(DialogComboBoxPtr(new DialogComboBox(_dialog, label, options))); } ui::IDialog::Handle Dialog::addEntryBox(const std::string& label) { return addElement(DialogElementPtr(new DialogEntryBox(_dialog, label))); } ui::IDialog::Handle Dialog::addPathEntry(const std::string& label, bool foldersOnly) { return addElement(DialogElementPtr(new DialogPathEntry(_dialog, label, foldersOnly))); } ui::IDialog::Handle Dialog::addSpinButton(const std::string& label, double min, double max, double step, unsigned int digits) { return addElement(DialogElementPtr(new DialogSpinButton(_dialog, label, min, max, step, digits))); } ui::IDialog::Handle Dialog::addCheckbox(const std::string& label) { return addElement(DialogElementPtr(new DialogCheckBox(_dialog, label))); } void Dialog::setElementValue(const ui::IDialog::Handle& handle, const std::string& value) { ElementMap::iterator i = _elements.find(handle); if (i == _elements.end()) { rError() << "Dialog: cannot find element with handle " << handle << std::endl; return; } // Import the data from the string into the widget i->second->importFromString(value); } std::string Dialog::getElementValue(const ui::IDialog::Handle& handle) { ElementMap::iterator i = _elements.find(handle); if (i == _elements.end()) { rError() << "Dialog: cannot find element with handle " << handle << std::endl; return ""; } // Export the widget's contents to a string return i->second->exportToString(); } ui::IDialog::Result Dialog::run() { if (!_constructed) { _constructed = true; // Call the virtual method, gives subclasses a chance to do their stuff construct(); } _dialog->Fit(); _dialog->CenterOnParent(); // Set the focus widget ElementMap::const_iterator found = _elements.find(_focusWidget); if (found != _elements.end() && found->second->getValueWidget()) { found->second->getValueWidget()->SetFocus(); } // Show the dialog (enters main loop and blocks) int result = _dialog->ShowModal(); switch (result) { case wxID_OK: _result = RESULT_OK; break; case wxID_CANCEL: default: _result = RESULT_CANCELLED; break; }; return _result; } std::string Dialog::TextEntryDialog(const std::string& title, const std::string& prompt, const std::string& defaultText, wxWindow* mainFrame) { Dialog dialog(title, mainFrame); Dialog::Handle entryHandle = dialog.addEntryBox(prompt); // Set the focus on the entry widget dialog.setFocus(entryHandle); Dialog::Result result = dialog.run(); if (result == Dialog::RESULT_OK) { std::string returnValue = dialog.getElementValue(entryHandle); return returnValue; } else { throw EntryAbortedException("textEntryDialog(): dialog cancelled"); } } } // namespace wxutil DarkRadiant-2.5.0/libs/wxutil/dialog/Dialog.h000066400000000000000000000065011321750546400210070ustar00rootroot00000000000000#pragma once #include "idialogmanager.h" #include "DialogBase.h" #include class wxFlexGridSizer; namespace wxutil { class DialogManager; class DialogElement; typedef std::shared_ptr DialogElementPtr; /** * greebo: A customisable Dialog window featuring Ok and Cancel buttons. * * Dialog elements can be added to the window using the addLabel(), * addButton(), add*() methods etc., which are returning a unique Handle. * * Use the getElementValue() and setElementValue() methods to * get and set the values of these dialog elements. * * Once the run() method is invoked, the Dialog enters a main loop, * showing the dialog and blocking the application. Use the result * returned by run() to see which action the user has taken. */ class Dialog : public ui::IDialog { protected: // The actual dialog implementation // We don't inherit from the dialog since wxWidgets has its // own memory management routines and requires dialogs to be // deallocated by calling Destroy(), not by directly deleting them. DialogBase* _dialog; ui::IDialog::Result _result; // The table carrying the elements wxFlexGridSizer* _elementsTable; // Whether all widgets have been created bool _constructed; // The elements added to this dialog, indexed by handle typedef std::map ElementMap; ElementMap _elements; Handle _highestUsedHandle; // The widget that should be focused when the dialog shown Handle _focusWidget; public: Dialog(const std::string& title, wxWindow* parent = NULL); virtual ~Dialog(); virtual void setTitle(const std::string& title); virtual Handle addLabel(const std::string& text); virtual Handle addComboBox(const std::string& label, const ComboBoxOptions& options); virtual Handle addEntryBox(const std::string& label); virtual Handle addPathEntry(const std::string& label, bool foldersOnly = false); virtual Handle addSpinButton(const std::string& label, double min, double max, double step, unsigned int digits); virtual Handle addCheckbox(const std::string& label); virtual void setElementValue(const Handle& handle, const std::string& value); virtual std::string getElementValue(const Handle& handle); // Sets the focus on the value widget of the element using the given handle void setFocus(Handle handle); void setDefaultSize(int width, int height); // Enter the main loop virtual ui::IDialog::Result run(); // Add a custom DialogElement ui::IDialog::Handle addElement(const DialogElementPtr& element); // get the parent widget needed to construct custom elements wxWindow* getElementParent(); protected: // Constructs the dialog (is invoked right before entering the main loop) virtual void construct(); // Overridable button creation method virtual void createButtons(); public: // Static methods to display pre-fabricated dialogs /** * Display a text entry dialog with the given title and prompt text. Returns a * std::string with the entered value, or throws EntryAbortedException if the * dialog was cancelled. The text entry will be filled with the given defaultText * at start. */ static std::string TextEntryDialog(const std::string& title, const std::string& prompt, const std::string& defaultText, wxWindow* mainFrame); }; typedef std::shared_ptr DialogPtr; } // namespace wxutil DarkRadiant-2.5.0/libs/wxutil/dialog/DialogBase.h000066400000000000000000000033351321750546400216040ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "imainframe.h" namespace wxutil { /** * Base class for many DarkRadiant dialogs. * It comes with a panel/sizer combination pre-populated, use * the _mainPanel member to add more stuff. */ class DialogBase : public wxDialog { private: void _onDelete(wxCloseEvent& ev) { if (_onDeleteEvent()) { ev.Veto(); } else { EndModal(wxID_CANCEL); } } public: DialogBase(const std::string& title, wxWindow* parent = NULL) : wxDialog(parent != NULL ? parent : GlobalMainFrame().getWxTopLevelWindow(), wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(DialogBase::_onDelete), NULL, this); } /** * Adjust this window to fit the display DarkRadiant is currently (mainly) active on. * Set the xProp and yProp factors to control how much space this window should use. * The factors should be in the range (0.0..1.0], where 1.0 takes the entire space. */ void FitToScreen(float xProp, float yProp) { int curDisplayIdx = 0; if (GlobalMainFrame().getWxTopLevelWindow() != NULL) { curDisplayIdx = wxDisplay::GetFromWindow(GlobalMainFrame().getWxTopLevelWindow()); } wxDisplay curDisplay(curDisplayIdx); wxRect rect = curDisplay.GetGeometry(); int newWidth = static_cast(rect.GetWidth() * xProp); int newHeight = static_cast(rect.GetHeight() * yProp); SetSize(newWidth, newHeight); CenterOnScreen(); } protected: // Overrideable: return true to prevent the window from being deleted virtual bool _onDeleteEvent() { return false; } }; } // namespace DarkRadiant-2.5.0/libs/wxutil/dialog/DialogElements.h000066400000000000000000000121321321750546400225010ustar00rootroot00000000000000#pragma once #include "idialogmanager.h" #include "../SerialisableWidgets.h" #include "../PathEntry.h" #include #include namespace wxutil { /** * greebo: Each dialog element has a label and a string-serialisable value * The getValueWidget() method retrieves the widget carrying the actual value, * use the getLabel() method to retrieve the descriptive label carrying the title. */ class DialogElement : public StringSerialisable { protected: // The label wxStaticText* _label; // The widget carrying the value wxWindow* _widget; protected: /** * Protected constructor, to be called by subclasses * @label: the name of this element, to be displayed next to it. */ DialogElement(wxWindow* parent, const std::string& label) : _label(new wxStaticText(parent, wxID_ANY, label)), _widget(NULL) {} public: // Retrieve the label virtual wxStaticText* getLabel() const { return _label; } // Retrieve the widget carrying the value virtual wxWindow* getValueWidget() const { return _widget; } protected: void setValueWidget(wxWindow* widget) { _widget = widget; } }; // ----------------------------------------------------------------------- class DialogEntryBox : public DialogElement, public SerialisableTextEntry { public: DialogEntryBox(wxWindow* parent, const std::string& label) : DialogElement(parent, label), SerialisableTextEntry(parent) { setValueWidget(this); // this as SerialisableTextEntry } virtual std::string exportToString() const { return SerialisableTextEntry::exportToString(); } virtual void importFromString(const std::string& str) { SerialisableTextEntry::importFromString(str); } }; // ----------------------------------------------------------------------- class DialogSpinButton : public DialogElement, public SerialisableSpinButton { public: DialogSpinButton(wxWindow* parent, const std::string& label, double min, double max, double step, unsigned int digits) : DialogElement(parent, label), SerialisableSpinButton(parent, min, min, max, step, digits) { DialogElement::setValueWidget(this); } virtual std::string exportToString() const { return SerialisableSpinButton::exportToString(); } virtual void importFromString(const std::string& str) { SerialisableSpinButton::importFromString(str); } }; // ----------------------------------------------------------------------- class DialogPathEntry : public PathEntry, public DialogElement { public: DialogPathEntry(wxWindow* parent, const std::string& label, bool foldersOnly) : PathEntry(parent, foldersOnly), DialogElement(parent, label) { DialogElement::setValueWidget(this); // this as *PathEntry } // Implementation of StringSerialisable, wrapping to base class virtual std::string exportToString() const { return PathEntry::getValue(); } virtual void importFromString(const std::string& str) { PathEntry::setValue(str); } }; // ----------------------------------------------------------------------- class DialogCheckBox : public DialogElement, public SerialisableCheckButton { public: DialogCheckBox(wxWindow* parent, const std::string& label) : DialogElement(parent, ""), // empty label, the description is included in the toggle button SerialisableCheckButton(parent, label) { DialogElement::setValueWidget(this); // this as SerialisableCheckButton } virtual std::string exportToString() const { return SerialisableCheckButton::exportToString(); } virtual void importFromString(const std::string& str) { SerialisableCheckButton::importFromString(str); } }; // ----------------------------------------------------------------------- class DialogLabel : public DialogElement, public wxStaticText { public: DialogLabel(wxWindow* parent, const std::string& label) : DialogElement(parent, label), // no standard label wxStaticText(parent, wxID_ANY, label) { DialogElement::setValueWidget(this); } // Implementation of StringSerialisable virtual std::string exportToString() const { return GetLabel().ToStdString(); } virtual void importFromString(const std::string& str) { SetLabel(str); } }; // ----------------------------------------------------------------------- /** * Creates a new ComboBox carrying the values passed to the constructor * Complies with the DialogElement interface. */ class DialogComboBox : public DialogElement, public SerialisableComboBox_Text { public: DialogComboBox(wxWindow* parent, const std::string& label, const ui::IDialog::ComboBoxOptions& options) : DialogElement(parent, label), SerialisableComboBox_Text(parent) { // Pass ourselves as widget to the DialogElement base class DialogElement::setValueWidget(this); // Add the options to the combo box for (ui::IDialog::ComboBoxOptions::const_iterator i = options.begin(); i != options.end(); ++i) { this->Append(*i); } } virtual std::string exportToString() const { return SerialisableComboBox_Text::exportToString(); } virtual void importFromString(const std::string& str) { SerialisableComboBox_Text::importFromString(str); } }; typedef std::shared_ptr DialogComboBoxPtr; } // namespace wxutil DarkRadiant-2.5.0/libs/wxutil/dialog/MessageBox.cpp000066400000000000000000000052211321750546400221760ustar00rootroot00000000000000#include "MessageBox.h" #include "imainframe.h" #include "itextstream.h" #include "i18n.h" #include #include #include namespace wxutil { Messagebox::Messagebox(const std::string& title, const std::string& text, ui::IDialog::MessageType type, wxWindow* parent) : _dialog(new wxMessageDialog(getTopLevelWindowSafe(parent), text, title, getDialogStyle(type))) { if (type == ui::IDialog::MESSAGE_SAVECONFIRMATION) { _dialog->SetYesNoLabels(wxString(_("Save")), wxString(_("Close without saving"))); } } Messagebox::~Messagebox() { _dialog->Destroy(); } wxWindow* Messagebox::getTopLevelWindowSafe(wxWindow* parent) { if (parent != nullptr) return parent; // No parent, check module registry if (module::GlobalModuleRegistry().moduleExists(MODULE_MAINFRAME)) { return GlobalMainFrame().getWxTopLevelWindow(); } return nullptr; } void Messagebox::setTitle(const std::string& title) { _dialog->SetTitle(title); } ui::IDialog::Result Messagebox::run() { int returnCode = _dialog->ShowModal(); // Map the wx result codes to ui::IDialog namespace switch (returnCode) { case wxID_OK: return ui::IDialog::RESULT_OK; case wxID_CANCEL: return ui::IDialog::RESULT_CANCELLED; case wxID_YES: return ui::IDialog::RESULT_YES; case wxID_NO: return ui::IDialog::RESULT_NO; default: return ui::IDialog::RESULT_CANCELLED; }; } long Messagebox::getDialogStyle(ui::IDialog::MessageType type) { long style = wxCENTER; switch (type) { case ui::IDialog::MESSAGE_CONFIRM: style |= wxOK | wxICON_INFORMATION | wxOK_DEFAULT; break; case ui::IDialog::MESSAGE_ASK: style |= wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT; break; case ui::IDialog::MESSAGE_WARNING: style |= wxOK | wxICON_WARNING | wxOK_DEFAULT; break; case ui::IDialog::MESSAGE_ERROR: style |= wxOK | wxICON_ERROR | wxOK_DEFAULT; break; case ui::IDialog::MESSAGE_YESNOCANCEL: style |= wxYES_NO | wxCANCEL | wxICON_QUESTION | wxYES_DEFAULT; break; case ui::IDialog::MESSAGE_SAVECONFIRMATION: style |= wxYES_NO | wxCANCEL | wxICON_WARNING | wxYES_DEFAULT; break; }; return style; } ui::IDialog::Result Messagebox::Show(const std::string& title, const std::string& text, ui::IDialog::MessageType type, wxWindow* parent) { Messagebox msg(title, text, type, parent); return msg.run(); } void Messagebox::ShowError(const std::string& errorText, wxWindow* parent) { Messagebox msg("Error", errorText, ui::IDialog::MESSAGE_ERROR, parent); msg.run(); } void Messagebox::ShowFatalError(const std::string& errorText, wxWindow* parent) { ShowError(errorText, parent); abort(); } } // namespace DarkRadiant-2.5.0/libs/wxutil/dialog/MessageBox.h000066400000000000000000000057201321750546400216470ustar00rootroot00000000000000#pragma once #include "idialogmanager.h" #include "imainframe.h" #include #include namespace wxutil { /** * A MessageBox is a specialised Dialog used for popup messages of various purpose. * Supported are things like Notifications, Warnings, Errors and simple Yes/No questions. * * Each messagebox is equipped with a special stock icon, corresponding to its type. * * Note: had to change this to lowercase to not conflict with the MessageBox #define in * some of those windows headers. */ class Messagebox : public ui::IDialog { protected: wxMessageDialog* _dialog; // The message text std::string _text; // The message type ui::IDialog::MessageType _type; public: // Constructs a new messageBox using the given title and text Messagebox(const std::string& title, const std::string& text, ui::IDialog::MessageType type, wxWindow* parent = NULL); protected: // Used during construction long getDialogStyle(ui::IDialog::MessageType type); // Tries to return a toplevel window pointer value that is valid // even in cases where no top level window or mainframe is visible. // nullptr is also considered a valid value. wxWindow* getTopLevelWindowSafe(wxWindow* parent); public: virtual ~Messagebox(); virtual void setTitle(const std::string& title); // We implement these using an empty body virtual Handle addLabel(const std::string& text) { return ui::INVALID_HANDLE; } virtual Handle addComboBox(const std::string& label, const ComboBoxOptions& options) { return ui::INVALID_HANDLE; } virtual Handle addEntryBox(const std::string& label) { return ui::INVALID_HANDLE; } virtual Handle addPathEntry(const std::string& label, bool foldersOnly = false) { return ui::INVALID_HANDLE; } virtual Handle addSpinButton(const std::string& label, double min, double max, double step, unsigned int digits) { return ui::INVALID_HANDLE; } virtual Handle addCheckbox(const std::string& label) { return ui::INVALID_HANDLE; } virtual void setElementValue(const Handle& handle, const std::string& value) {} virtual std::string getElementValue(const Handle& handle) { return ""; } // Enter the main loop virtual ui::IDialog::Result run(); // Static methods /** * Display a message box of the given type, using title as window caption * and text as content to display. Will return the result code. */ static ui::IDialog::Result Show(const std::string& title, const std::string& text, ui::IDialog::MessageType type, wxWindow* parent = GlobalMainFrame().getWxTopLevelWindow()); /** * Display a modal error dialog. */ static void ShowError(const std::string& errorText, wxWindow* parent = GlobalMainFrame().getWxTopLevelWindow()); /** * Display a modal error dialog and quit immediately. */ static void ShowFatalError(const std::string& errorText, wxWindow* parent = GlobalMainFrame().getWxTopLevelWindow()); }; typedef std::shared_ptr MessageboxPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/dialog/MessageboxDialogAdapter.h000066400000000000000000000021441321750546400243250ustar00rootroot00000000000000#pragma once #include "idialogmanager.h" #include "MessageBox.h" namespace wxutil { // An adapter class which provides the IDialog interface for a Messagebox class MessageBoxDialogAdapter : public ui::IDialog { private: Messagebox* _msgBox; public: MessageBoxDialogAdapter(const std::string& title, const std::string& text, ui::IDialog::MessageType type, wxWindow* parent = NULL); virtual void setTitle(const std::string& title); virtual Handle addLabel(const std::string& text); virtual Handle addComboBox(const std::string& label, const ComboBoxOptions& options); virtual Handle addEntryBox(const std::string& label); virtual Handle addPathEntry(const std::string& label, bool foldersOnly = false); virtual Handle addSpinButton(const std::string& label, double min, double max, double step, unsigned int digits); virtual Handle addCheckbox(const std::string& label); virtual void setElementValue(const Handle& handle, const std::string& value); virtual std::string getElementValue(const Handle& handle); // Enter the main loop virtual ui::IDialog::Result run(); }; } // namespace DarkRadiant-2.5.0/libs/wxutil/dialog/ScrollEventPropagationFilter.h000066400000000000000000000031041321750546400254160ustar00rootroot00000000000000#pragma once #include #include #include "../GLWidget.h" namespace wxutil { /** * greebo: Propagate the mousewheel event to the GL window below the cursor * not the one that might happen to have focus in MS Windows */ class ScrollEventPropagationFilter : public wxEventFilter { public: ScrollEventPropagationFilter() { wxEvtHandler::AddFilter(this); } virtual ~ScrollEventPropagationFilter() { wxEvtHandler::RemoveFilter(this); } virtual int FilterEvent(wxEvent& ev) { if (ev.GetEventType() != wxEVT_MOUSEWHEEL) { return Event_Skip; } wxPoint mousePos = wxGetMousePosition(); wxWindow* windowAtPoint = wxFindWindowAtPointer(mousePos); if (windowAtPoint) { // Special case for wxDataViewCtrls. wxDataViewMainWindow is the one that is returned by // the wxFindWindowAtPointer method, but it doesn't process the mouse wheel events, it's // its parent wxDataViewCtrl taking care of that - so with this special knowledge... if (wxString(windowAtPoint->GetClassInfo()->GetClassName()) == "wxDataViewMainWindow") { // ...redirect the mouse wheel event to the wxDataViewCtrl windowAtPoint = windowAtPoint->GetParent(); } if (windowAtPoint) { return windowAtPoint->GetEventHandler()->ProcessEvent(ev) ? Event_Processed : Event_Skip; } } return Event_Skip; } }; typedef std::shared_ptr ScrollEventPropagationFilterPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/event/000077500000000000000000000000001321750546400173175ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/wxutil/event/KeyEventFilter.h000066400000000000000000000035031321750546400223710ustar00rootroot00000000000000#pragma once #include #include #include #include namespace wxutil { /** * A KeyEventFilter will register itself with wxWidgets on construction * and will invoke a given callback as soon as a defined keycode is * encountered during keydown events. */ class KeyEventFilter : public wxEventFilter { public: // Result type to be returned by the callback to indicate what happened enum class Result { KeyProcessed, // event has been processed, don't propagate further KeyIgnored, // event can be propagated as usual }; typedef std::function Callback; private: wxKeyCode _keyCodeToCapture; Callback _callback; public: // Construct the filter with a keycode to observer and a callback // function that is invoked when the keycode occurs KeyEventFilter(wxKeyCode keyCodeToCapture, const Callback& callback) : _keyCodeToCapture(keyCodeToCapture), _callback(callback) { wxEvtHandler::AddFilter(this); } virtual ~KeyEventFilter() { wxEvtHandler::RemoveFilter(this); } virtual int FilterEvent(wxEvent& event) { const wxEventType t = event.GetEventType(); if (t == wxEVT_KEY_DOWN && static_cast(event).GetKeyCode() == _keyCodeToCapture) { Result result = Result::KeyProcessed; if (_callback) { result = _callback(); } // Stop propagation if the key was processed return result == Result::KeyProcessed ? Event_Processed : Event_Skip; } // Continue processing the event normally if it doesn't match our signature return Event_Skip; } }; typedef std::shared_ptr KeyEventFilterPtr; } DarkRadiant-2.5.0/libs/wxutil/event/SingleIdleCallback.h000066400000000000000000000044661321750546400231360ustar00rootroot00000000000000#pragma once #include #include #include namespace wxutil { /** * Base class for classes which wish to receive a single callback when the GUI is * idle, to perform processing that does not block the GUI. * * A class which derives from this must invoke the requestIdleCallback() method * whenever it is ready to receive an idle callback. Subsequently, it will have * its onIdle() method invoked once during the idle period. */ class SingleIdleCallback : public wxEvtHandler { private: bool _callbackPending; private: // Actual wxIdelEvent callback method, which invokes the child class' // implementing method void _onIdle(wxIdleEvent& ev) { wxTheApp->Disconnect(wxEVT_IDLE, wxIdleEventHandler(wxutil::SingleIdleCallback::_onIdle), NULL, this); // Call the virtual function onIdle(); _callbackPending = false; } // Remove the callback void deregisterCallback() { if (_callbackPending) { if (wxTheApp) { wxTheApp->Disconnect(wxEVT_IDLE, wxIdleEventHandler(wxutil::SingleIdleCallback::_onIdle), NULL, this); } _callbackPending = false; } } protected: /** * Request an idle callback. The onIdle() method will be invoked during * the next idle period. */ void requestIdleCallback() { if (_callbackPending) return; // avoid duplicate registrations if (wxTheApp) { _callbackPending = true; wxTheApp->Connect(wxEVT_IDLE, wxIdleEventHandler(wxutil::SingleIdleCallback::_onIdle), NULL, this); } } // TRUE if an idle callback is pending bool callbacksPending() const { return _callbackPending; } void cancelCallbacks() { deregisterCallback(); } // Flushes any pending events, forces a call to onGtkIdle, if necessary void flushIdleCallback() { if (_callbackPending) { // Cancel the event and force an onIdle() call deregisterCallback(); // Invoke the idle callback onIdle(); } } /** * Implementing method for the idle callback. Code in this method will * be executed only when the app is idle. */ virtual void onIdle() = 0; public: SingleIdleCallback() : _callbackPending(0) {} /** * Destructor. De-registers the callback from wx, so that the method will * not be invoked after the object is destroyed. */ virtual ~SingleIdleCallback() { deregisterCallback(); } }; } DarkRadiant-2.5.0/libs/wxutil/menu/000077500000000000000000000000001321750546400171425ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/wxutil/menu/CommandMenuItem.h000066400000000000000000000014271321750546400223410ustar00rootroot00000000000000#pragma once #include "MenuItem.h" #include "icommandsystem.h" #include namespace wxutil { /** * A specialised MenuItem invoking a specific command when clicked. */ class CommandMenuItem : public MenuItem { protected: std::string _statementName; public: CommandMenuItem(wxMenuItem* menuItem, const std::string& statementName, const ui::IMenu::SensitivityTest& sensTest = AlwaysSensitive, const ui::IMenu::VisibilityTest& visTest = AlwaysVisible) : MenuItem(menuItem, std::bind(&CommandMenuItem::executeCommand, this), sensTest, visTest), _statementName(statementName) {} void executeCommand() { GlobalCommandSystem().execute(_statementName); } }; typedef std::shared_ptr CommandMenuItemPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/menu/IconTextMenuItem.h000066400000000000000000000015071321750546400225170ustar00rootroot00000000000000#pragma once #include "iuimanager.h" #include #include namespace wxutil { class IconTextMenuItem : public wxMenuItem { public: // Use a local bitmap as found in the application's bitmaps folder. IconTextMenuItem(const std::string& text, const std::string& localBitmapFilename) : wxMenuItem(NULL, wxID_ANY, text, "") { SetBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + localBitmapFilename)); } }; class StockIconTextMenuItem : public wxMenuItem { public: // Use a stock image specified by its art ID StockIconTextMenuItem(const std::string& text, const wxArtID& artId) : wxMenuItem(NULL, wxID_ANY, text, "") { // On MSW some stock icons come in 32x32 format, so specify 16x16 explicitly SetBitmap(wxArtProvider::GetBitmap(artId, wxART_MENU, wxSize(16, 16))); } }; }DarkRadiant-2.5.0/libs/wxutil/menu/MenuItem.h000066400000000000000000000020751321750546400210420ustar00rootroot00000000000000#pragma once #include "imenu.h" #include namespace wxutil { // Data class containing the elements of a menu item class MenuItem : public ui::IMenuItem { protected: wxMenuItem* _menuItem; ui::IMenu::Callback _callback; ui::IMenu::SensitivityTest _sensitivityTest; ui::IMenu::VisibilityTest _visibilityTest; public: MenuItem(wxMenuItem* menuItem, const ui::IMenu::Callback& callback, const ui::IMenu::SensitivityTest& sensTest = AlwaysSensitive, const ui::IMenu::VisibilityTest& visTest = AlwaysVisible) : _menuItem(menuItem), _callback(callback), _sensitivityTest(sensTest), _visibilityTest(visTest) {} virtual wxMenuItem* getMenuItem() { return _menuItem; } virtual void execute() { _callback(); } virtual bool isVisible() { return _visibilityTest(); } virtual bool isSensitive() { return _sensitivityTest(); } static bool AlwaysVisible() { return true; } static bool AlwaysSensitive() { return true; } void onActivate() { execute(); } }; typedef std::shared_ptr MenuItemPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/menu/PopupMenu.cpp000066400000000000000000000034511321750546400216010ustar00rootroot00000000000000#include "PopupMenu.h" namespace wxutil { // Default constructor PopupMenu::PopupMenu() : wxMenu() { Connect(wxEVT_MENU, wxCommandEventHandler(PopupMenu::_onItemClick), NULL, this); } PopupMenu::~PopupMenu() { } // Add a named menu item void PopupMenu::addItem(wxMenuItem* widget, const Callback& callback, const SensitivityTest& sensTest, const VisibilityTest& visTest) { // Construct a wrapper and pass to specialised method addItem(ui::IMenuItemPtr(new wxutil::MenuItem(widget, callback, sensTest, visTest))); } void PopupMenu::addItem(const ui::IMenuItemPtr& item) { _menuItems.push_back(item); // Add the widget to the menu Append(item->getMenuItem()); } void PopupMenu::addSeparator() { AppendSeparator(); } // Show the menu void PopupMenu::show(wxWindow* parent) { // Iterate through the list of MenuItems, enabling or disabling each widget // based on its SensitivityTest for (MenuItemList::iterator i = _menuItems.begin(); i != _menuItems.end(); ++i) { ui::IMenuItem& item = *(*i); bool visible = item.isVisible(); if (visible) { // Visibility check passed item.getMenuItem()->Enable(item.isSensitive()); } else { // Visibility check failed, skip sensitivity check item.getMenuItem()->Enable(false); } } parent->PopupMenu(this); } void PopupMenu::_onItemClick(wxCommandEvent& ev) { int commandId = ev.GetId(); // Find the menu item with that ID for (MenuItemList::iterator i = _menuItems.begin(); i != _menuItems.end(); ++i) { ui::IMenuItem& item = *(*i); if (item.getMenuItem()->GetId() == commandId) { item.execute(); break; } } } void PopupMenu::foreachMenuItem(const std::function& functor) { for (const ui::IMenuItemPtr& item : _menuItems) { functor(item); } } } // namespace DarkRadiant-2.5.0/libs/wxutil/menu/PopupMenu.h000066400000000000000000000036061321750546400212500ustar00rootroot00000000000000#pragma once #include "imenu.h" #include #include #include "util/Noncopyable.h" #include #include #include "MenuItem.h" namespace wxutil { /** * A free pop-up menu populated with items and displayed on demand. Useful for * right-click context menus. */ class PopupMenu : public wxMenu, public ui::IMenu { private: // List of menu items typedef std::list MenuItemList; MenuItemList _menuItems; private: // Mouse click callback (if required) void _onItemClick(wxCommandEvent& ev); public: /** * Default constructor. */ PopupMenu(); /** * Destructor. */ virtual ~PopupMenu(); /** * Add an item to this menu using a widget and callback function. * * @param widget * The menu item containing the displayed elements (i.e. icon and text). * * @param callback * A callback function to be invoked when this menu item is activated. * * @param sensTest * SensitivityTest function object to determine whether this menu item is * currently clickable (optional). * * @param visTest * VisibilityTest function object to determine whether this menu item is * currently visible (optional). */ virtual void addItem(wxMenuItem* widget, const Callback& callback, const SensitivityTest& sensTest = SensitivityTest(_alwaysTrue), const VisibilityTest& visTest = VisibilityTest(_alwaysTrue)); virtual void addItem(const ui::IMenuItemPtr& item); // Adds a separator item (horizontal line) virtual void addSeparator(); /** * Show this menu. Each menu item's SensitivityTest will be invoked to * determine whether it should be enabled or not, then the menu will be * displayed. */ virtual void show(wxWindow* parent); protected: virtual void foreachMenuItem(const std::function& functor); }; typedef std::shared_ptr PopupMenuPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/preview/000077500000000000000000000000001321750546400176575ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/wxutil/preview/GuiRenderer.cpp000066400000000000000000000113371321750546400226030ustar00rootroot00000000000000#include "GuiRenderer.h" #include "igl.h" #include "ifonts.h" #include "ishaders.h" #include "math/Matrix4.h" namespace wxutil { GuiRenderer::GuiRenderer() : _areaTopLeft(0,0), _areaBottomRight(640, 480), _ignoreVisibility(false) {} void GuiRenderer::setGui(const gui::IGuiPtr& gui) { _gui = gui; } void GuiRenderer::setVisibleArea(const Vector2& topLeft, const Vector2& bottomRight) { _areaTopLeft = topLeft; _areaBottomRight = bottomRight; } void GuiRenderer::setIgnoreVisibility(bool ignoreVisibility) { _ignoreVisibility = ignoreVisibility; } void GuiRenderer::setWindowDefFilter(const std::string& windowDef) { _windowDefFilter = windowDef; } void GuiRenderer::render() { glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // set up viewpoint glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Initialise the 2D projection matrix with: left, right, bottom, top, znear, zfar glOrtho(_areaTopLeft[0], // left _areaBottomRight[0], // right _areaBottomRight[1], // bottom _areaTopLeft[1], // top -1, 1); // Tell openGL to draw front and back of the polygons in textured mode glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (_gui != NULL) { // Fetch the desktop windowDef and render it render(_gui->getDesktop()); } glDisable(GL_BLEND); } void GuiRenderer::render(const gui::IGuiWindowDefPtr& window, bool ignoreFilter) { if (window == NULL) return; if (!window->visible && !_ignoreVisibility) return; if (!ignoreFilter && !_windowDefFilter.empty()) { if (window->name == _windowDefFilter) { ignoreFilter = true; // this is the window we want, ignore filter from here on } else if (!window->findWindowDef(_windowDefFilter)) { return; // filtered window is not even a child } } Vector4 rect = window->rect; Vector4 backcolor = window->backcolor; if (backcolor[3] > 0) { glColor4dv(backcolor); // Background quad glBegin(GL_QUADS); glVertex2d(rect[0], rect[1]); // Upper left glVertex2d(rect[0] + rect[2], rect[1]); // Upper right glVertex2d(rect[0] + rect[2], rect[1] + rect[3]); // Lower right glVertex2d(rect[0], rect[1] + rect[3]); // Lower left glEnd(); } // Realise background shader if necessary if (!window->background.getValue().empty() && window->backgroundShader == NULL) { window->backgroundShader = GlobalMaterialManager().getMaterialForName(window->background); } // Acquire the texture number of the active texture Vector4 matcolor = window->matcolor; if (window->backgroundShader != NULL && (matcolor[3] > 0 || _ignoreVisibility)) { // Get the diffuse layer const ShaderLayerVector& layers = window->backgroundShader->getAllLayers(); TexturePtr tex; for (ShaderLayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i) { if ((*i)->getType() == ShaderLayer::DIFFUSE) { // Found the diffuse tex = (*i)->getTexture(); break; } } if (tex == NULL) { tex = window->backgroundShader->getEditorImage(); } if (tex != NULL) { glBindTexture(GL_TEXTURE_2D, tex->getGLTexNum()); // Draw the textured quad glColor4dv(matcolor); // Render background image as opaque if _ignoreVisibility is set to true if (_ignoreVisibility && matcolor[3] <= 0) { glColor4d(matcolor[0], matcolor[1], matcolor[2], 1); } glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex2d(rect[0], rect[1]); // Upper left glTexCoord2f(1,0); glVertex2d(rect[0] + rect[2], rect[1]); // Upper right glTexCoord2f(1,1); glVertex2d(rect[0] + rect[2], rect[1] + rect[3]); // Lower right glTexCoord2f(0,1); glVertex2d(rect[0], rect[1] + rect[3]); // Lower left glEnd(); glDisable(GL_TEXTURE_2D); } } // Render the text if (!window->text.getValue().empty()) { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_TEXTURE_2D); Vector4 forecolor = window->forecolor; glColor4dv(forecolor); // Render text as opaque if _ignoreVisibility is set to true if (_ignoreVisibility && forecolor[3] <= 0) { glColor4d(forecolor[0], forecolor[1], forecolor[2], 1); } window->getRenderableText().render(); glDisable(GL_TEXTURE_2D); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); } // Push the translation before rendering the children, so that they // can continue rendering in local coordinates, but the results appear relative to // this parent windowDef glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslated(rect[0], rect[1], 0); for (const gui::IGuiWindowDefPtr& child : window->children) { render(child, ignoreFilter); } glMatrixMode(GL_MODELVIEW); glPopMatrix(); } } // namespace DarkRadiant-2.5.0/libs/wxutil/preview/GuiRenderer.h000066400000000000000000000017351321750546400222510ustar00rootroot00000000000000#pragma once #include "igui.h" #include "math/Vector2.h" #include "util/Noncopyable.h" #include namespace wxutil { class GuiRenderer : public util::Noncopyable { private: gui::IGuiPtr _gui; Vector2 _areaTopLeft; Vector2 _areaBottomRight; // Whether invisible windowDefs should be rendered anyway bool _ignoreVisibility; std::string _windowDefFilter; public: // Construct a new renderer GuiRenderer(); void setGui(const gui::IGuiPtr& gui); void setIgnoreVisibility(bool ignoreVisibility); // Sets the visible area to be rendered, in absolute GUI coordinates [0,0..640,480] void setVisibleArea(const Vector2& topLeft, const Vector2& bottomRight); // Only draw this windowDef and its children. void setWindowDefFilter(const std::string& windowDef); // Starts rendering the attached GUI // The GL context must be set before calling this method void render(); private: void render(const gui::IGuiWindowDefPtr& window, bool ignoreFilter = false); }; } DarkRadiant-2.5.0/libs/wxutil/preview/GuiView.cpp000066400000000000000000000050601321750546400217430ustar00rootroot00000000000000#include "GuiView.h" #include "igl.h" #include "math/Matrix4.h" #include namespace wxutil { namespace { const int DEFAULT_WIDTH = 640; const int DEFAULT_HEIGHT = 480; } GuiView::GuiView(wxWindow* parent) : GLWidget(parent, std::bind(&GuiView::draw, this), "GUI") { SetMinSize(wxSize(DEFAULT_WIDTH, DEFAULT_HEIGHT)); // greebo: The "size-allocate" event is needed to determine the window size, as expose-event is // often called for subsets of the widget and the size info in there is therefore not reliable. Connect(wxEVT_SIZE, wxSizeEventHandler(GuiView::onSizeAllocate), NULL, this); // Ignore visibility flag and turn invisible background images to visible ones _renderer.setIgnoreVisibility(true); } void GuiView::redraw() { Refresh(); } void GuiView::setGui(const gui::IGuiPtr& gui) { // Check for equality if (gui != _gui) { _gui = gui; _renderer.setGui(gui); } } const gui::IGuiPtr& GuiView::getGui() { return _gui; } void GuiView::setWindowDefFilter(const std::string& windowDef) { _renderer.setWindowDefFilter(windowDef); } void GuiView::initialiseView() { } void GuiView::setGLViewPort() { GlobalOpenGL().assertNoErrors(); double width = _windowDims[0]; double height = _windowDims[1]; double aspectRatio = static_cast(DEFAULT_WIDTH) / DEFAULT_HEIGHT; if (width / height > aspectRatio) { width = height * aspectRatio; } else { height = width / aspectRatio; } glViewport(0, 0, static_cast(width), static_cast(height)); GlobalOpenGL().assertNoErrors(); } void GuiView::draw() { if (_gui == NULL) return; GlobalOpenGL().assertNoErrors(); // Clear the window glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glClearColor(0.0, 0.0, 0.0, 0); glClearDepth(100.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Set up the camera glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Enable depth buffer writing, to be safe glDepthMask(GL_TRUE); GlobalOpenGL().assertNoErrors(); // Prepare the GUI for rendering, like re-compiling texts etc. // This has to be performed before states are initialised _gui->pepareRendering(); GlobalOpenGL().assertNoErrors(); setGLViewPort(); GlobalOpenGL().assertNoErrors(); // Set up the scale glMatrixMode(GL_MODELVIEW); glLoadIdentity(); GlobalOpenGL().assertNoErrors(); _renderer.render(); } void GuiView::onSizeAllocate(wxSizeEvent& ev) { // Store the window dimensions for later calculations _windowDims = Vector2(ev.GetSize().GetWidth(), ev.GetSize().GetHeight()); // Queue an expose event Refresh(); } } // namespace DarkRadiant-2.5.0/libs/wxutil/preview/GuiView.h000066400000000000000000000024521321750546400214120ustar00rootroot00000000000000#pragma once #include "igui.h" #include "wxutil/GLWidget.h" #include "math/Vector2.h" #include "GuiRenderer.h" namespace wxutil { /** * greebo: Use this class to add a GUI display to your dialogs. * It is owning a GL widget as well as a GuiRenderer which * is taking care of rendering the GUI elements to GL. */ class GuiView : public wxutil::GLWidget { protected: // The GUI renderer is submitting stuff to GL GuiRenderer _renderer; // The GUI to render gui::IGuiPtr _gui; // The dimensions of the GL widget in pixels. Vector2 _windowDims; public: GuiView(wxWindow* parent); virtual ~GuiView() {} // Sets the GUI to render (can be NULL to clear this view) virtual void setGui(const gui::IGuiPtr& gui); // Only draw the given windowDef and its children virtual void setWindowDefFilter(const std::string& windowDef); // Returns the current GUI (can be NULL) const gui::IGuiPtr& getGui(); /** * Initialise the GL view. This clears the window and sets up the * initial matrices. */ void initialiseView(); // Triggers a redraw void redraw(); protected: // Performs the actual GL setup and drawing virtual void draw(); // Calculates the visible area virtual void setGLViewPort(); private: void onSizeAllocate(wxSizeEvent& ev); }; typedef std::shared_ptr GuiViewPtr; } DarkRadiant-2.5.0/libs/wxutil/preview/ModelPreview.cpp000066400000000000000000000117501321750546400227710ustar00rootroot00000000000000#include "ModelPreview.h" #include "../GLWidget.h" #include "ifilter.h" #include "iuimanager.h" #include "imodelcache.h" #include "i18n.h" #include "ieclass.h" #include "os/path.h" #include "math/AABB.h" #include "modelskin.h" #include "entitylib.h" #include "scene/Node.h" #include "scene/BasicRootNode.h" #include "wxutil/dialog/MessageBox.h" #include "string/convert.h" #include #include "iuimanager.h" namespace wxutil { /* CONSTANTS */ namespace { const char* const FUNC_STATIC_CLASS = "func_static"; } // Construct the widgets ModelPreview::ModelPreview(wxWindow* parent) : RenderPreview(parent, false), _lastModel(""), _defaultCamDistanceFactor(2.8f) {} // Set the model, this also resets the camera void ModelPreview::setModel(const std::string& model) { // If the model name is empty, release the model if (model.empty()) { if (_modelNode) { _entity->removeChildNode(_modelNode); } _modelNode.reset(); stopPlayback(); return; } // Set up the scene if (!_entity) { getScene(); // trigger a setupscenegraph call } if (_modelNode) { _entity->removeChildNode(_modelNode); } _modelNode = GlobalModelCache().getModelNode(model); if (_modelNode) { _entity->addChildNode(_modelNode); // Trigger an initial update of the subgraph GlobalFilterSystem().updateSubgraph(getScene()->root()); // Reset camera if the model has changed if (model != _lastModel) { // Reset preview time stopPlayback(); // Reset the model rotation resetModelRotation(); // Reset the default view, facing down to the model from diagonally above the bounding box double distance = _modelNode->localAABB().getRadius() * _defaultCamDistanceFactor; setViewOrigin(Vector3(1, 1, 1) * distance); setViewAngles(Vector3(34, 135, 0)); } _lastModel = model; } // Redraw queueDraw(); } // Set the skin, this does NOT reset the camera void ModelPreview::setSkin(const std::string& skin) { // Load and apply the skin, checking first to make sure the model is valid // and not null if (_modelNode != NULL) { model::ModelNodePtr model = Node_getModel(_modelNode); if (model) { ModelSkin& mSkin = GlobalModelSkinCache().capture(skin); model->getIModel().applySkin(mSkin); } } // Redraw queueDraw(); } void ModelPreview::setDefaultCamDistanceFactor(float factor) { _defaultCamDistanceFactor = factor; } void ModelPreview::setupSceneGraph() { RenderPreview::setupSceneGraph(); try { _rootNode = std::make_shared(); _entity = GlobalEntityCreator().createEntity( GlobalEntityClassManager().findClass(FUNC_STATIC_CLASS)); _rootNode->addChildNode(_entity); _entity->enable(scene::Node::eHidden); // This entity is acting as our root node in the scene getScene()->setRoot(_rootNode); // Set up the light _light = GlobalEntityCreator().createEntity( GlobalEntityClassManager().findClass("light")); Node_getEntity(_light)->setKeyValue("light_radius", "600 600 600"); Node_getEntity(_light)->setKeyValue("origin", "0 0 300"); _rootNode->addChildNode(_light); } catch (std::runtime_error&) { wxutil::Messagebox::ShowError(fmt::format(_("Unable to setup the preview,\n" "could not find the entity class {0}"), FUNC_STATIC_CLASS)); } } AABB ModelPreview::getSceneBounds() { if (!_modelNode) { return RenderPreview::getSceneBounds(); } return _modelNode->localAABB(); } bool ModelPreview::onPreRender() { if (_light) { Vector3 lightOrigin = _viewOrigin + Vector3(0, 0, 20); // Position the light just above the camera Node_getEntity(_light)->setKeyValue("origin", string::to_string(lightOrigin)); // Let the light encompass the object float radius = (getSceneBounds().getOrigin() - lightOrigin).getLength() * 2.0f; radius = std::max(radius, 200.f); std::ostringstream value; value << radius << ' ' << radius << ' ' << radius; Node_getEntity(_light)->setKeyValue("light_radius", value.str()); Node_getEntity(_light)->setKeyValue("_color", "0.6 0.6 0.6"); } return _modelNode != nullptr; } void ModelPreview::onModelRotationChanged() { if (_entity) { // Update the model rotation on the entity std::ostringstream value; value << _modelRotation.xx() << ' ' << _modelRotation.xy() << ' ' << _modelRotation.xz() << ' ' << _modelRotation.yx() << ' ' << _modelRotation.yy() << ' ' << _modelRotation.yz() << ' ' << _modelRotation.zx() << ' ' << _modelRotation.zy() << ' ' << _modelRotation.zz(); Node_getEntity(_entity)->setKeyValue("rotation", value.str()); } } RenderStateFlags ModelPreview::getRenderFlagsFill() { return RenderPreview::getRenderFlagsFill() | RENDER_DEPTHWRITE | RENDER_DEPTHTEST; } } // namespace ui DarkRadiant-2.5.0/libs/wxutil/preview/ModelPreview.h000066400000000000000000000033621321750546400224360ustar00rootroot00000000000000#pragma once #include "RenderPreview.h" #include "imodel.h" #include "imap.h" #include #include namespace wxutil { /** * \brief * Preview widget for models and skins. * * Subclass of RenderPreview which holds a model and displays it optionally with * a skin. */ class ModelPreview : public RenderPreview { scene::IMapRootNodePtr _rootNode; // The parent entity scene::INodePtr _entity; // Current model to display scene::INodePtr _modelNode; // The light scene::INodePtr _light; // Name of last model, to detect changes in model which require camera // recalculation std::string _lastModel; float _defaultCamDistanceFactor; private: // Creates parent entity etc. void setupSceneGraph() override; AABB getSceneBounds() override; bool onPreRender() override; RenderStateFlags getRenderFlagsFill() override; protected: virtual void onModelRotationChanged() override; public: /// Construct a ModelPreview widget. ModelPreview(wxWindow* parent); /** * Set the widget to display the given model. If the model name is the * empty string, the widget will release the currently displayed model. * * @param * String name of the model to display. */ void setModel(const std::string& model); /// Set the skin to apply to the model for rendering void setSkin(const std::string& skin); // Multiply the model node's AABB radius by this factor to get the default camera distance // defaults to 6. void setDefaultCamDistanceFactor(float factor); /// Return the current model node scene::INodePtr getModelNode() { return _modelNode; } }; typedef std::shared_ptr ModelPreviewPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/preview/ParticlePreview.cpp000066400000000000000000000170011321750546400234670ustar00rootroot00000000000000#include "ParticlePreview.h" #include "iuimanager.h" #include "ieventmanager.h" #include "ientity.h" #include "ieclass.h" #include "iparticles.h" #include "iparticlestage.h" #include "i18n.h" #include "scene/Node.h" #include "scene/BasicRootNode.h" #include "entitylib.h" #include "string/string.h" #include "wxutil/GLWidget.h" #include "wxutil/dialog/MessageBox.h" #include #include #include "string/predicate.h" namespace wxutil { namespace { const char* const FUNC_EMITTER_CLASS = "func_emitter"; enum ToolItems { TOOL_SHOW_AXES = 100, TOOL_SHOW_WIREFRAME, TOOL_REFRESH, TOOL_AUTO_LOOP }; } // Construct the widgets ParticlePreview::ParticlePreview(wxWindow* parent) : RenderPreview(parent, true) { // Add one additional toolbar for particle-related stuff wxToolBar* toolbar = new wxToolBar(_mainPanel, wxID_ANY); toolbar->SetToolBitmapSize(wxSize(24, 24)); _showAxesButton = toolbar->AddCheckTool(TOOL_SHOW_AXES, "", wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "axes.png", wxART_TOOLBAR)); _showAxesButton->SetShortHelp(_("Show coordinate axes")); toolbar->Connect(_showAxesButton->GetId(), wxEVT_TOOL, wxCommandEventHandler(ParticlePreview::onToolItemClickRefresh), NULL, this); _showWireFrameButton = toolbar->AddCheckTool(TOOL_SHOW_WIREFRAME, "", wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "wireframe.png", wxART_TOOLBAR)); _showWireFrameButton->SetShortHelp(_("Show wireframe")); toolbar->Connect(_showWireFrameButton->GetId(), wxEVT_TOOL, wxCommandEventHandler(ParticlePreview::onToolItemClickRefresh), NULL, this); _automaticLoopButton = toolbar->AddCheckTool(TOOL_AUTO_LOOP, _("Auto Loop"), wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "loop.png", wxART_TOOLBAR)); _automaticLoopButton->SetShortHelp(_("Auto Loop")); _reloadButton = toolbar->AddTool(TOOL_REFRESH, "", wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "refresh.png", wxART_TOOLBAR)); _reloadButton->SetShortHelp(_("Reload Particle Defs")); IEventPtr ev = GlobalEventManager().findEvent("ReloadParticles"); ev->connectToolItem(_reloadButton); toolbar->Realize(); addToolbar(toolbar); } ParticlePreview::~ParticlePreview() { IEventPtr ev = GlobalEventManager().findEvent("ReloadParticles"); ev->disconnectToolItem(_reloadButton); } void ParticlePreview::setParticle(const std::string& name) { std::string nameClean = name; if (string::ends_with(nameClean, ".prt")) { nameClean = nameClean.substr(0, nameClean.length() - 4); } // If the model name is empty, release the model if (nameClean.empty()) { if (_particleNode) { _entity->removeChildNode(_particleNode); } _particleNode.reset(); _lastParticle = ""; stopPlayback(); return; } // Set up the scene if (!_entity) { setupSceneGraph(); } if (!_entity) return; // FUNC_EMITTER_CLASS not found if (_particleNode) { _entity->removeChildNode(_particleNode); } // Construct the particle emitter node _particleNode = GlobalParticlesManager().createParticleNode(nameClean); if (_particleNode && _lastParticle != nameClean) { _entity->addChildNode(_particleNode); // Reset preview time stopPlayback(); // Call update(0) once to enable the bounds calculation _particleNode->getParticle()->update(_modelView); // Reset the model rotation resetModelRotation(); // Use particle AABB to adjust camera distance const AABB& particleBounds = _particleNode->getParticle()->getBounds(); if (particleBounds.isValid()) { // Reset the default view, facing down to the model from diagonally above the bounding box double distance = particleBounds.getRadius() * 2.0f; setViewOrigin(Vector3(1, 1, 1) * distance); } else { // Bounds not valid, fall back to default setViewOrigin(Vector3(1, 1, 1) * 40.0f); } setViewAngles(Vector3(34, 135, 0)); _lastParticle = nameClean; // Start playback when switching particles startPlayback(); } // Redraw queueDraw(); } void ParticlePreview::setupSceneGraph() { RenderPreview::setupSceneGraph(); try { _rootNode = std::make_shared(); _entity = GlobalEntityCreator().createEntity( GlobalEntityClassManager().findClass(FUNC_EMITTER_CLASS)); _rootNode->addChildNode(_entity); _entity->enable(scene::Node::eHidden); // This entity is acting as our root node in the scene getScene()->setRoot(_rootNode); } catch (std::runtime_error&) { wxutil::Messagebox::ShowError(fmt::format(_("Unable to setup the preview,\n" "could not find the entity class {0}"), FUNC_EMITTER_CLASS)); } } AABB ParticlePreview::getSceneBounds() { if (!_particleNode) { return RenderPreview::getSceneBounds(); } return _particleNode->getParticle()->getBounds(); } bool ParticlePreview::onPreRender() { return _particleNode != NULL; } void ParticlePreview::onPostRender() { if (_showWireFrameButton->IsToggled()) { renderWireFrame(); } // Draw coordinate axes for better orientation if (_showAxesButton->IsToggled()) { drawAxes(); } const particles::IParticleDefPtr& def = _particleNode->getParticle()->getParticleDef(); // Calculate the total time of the particles std::size_t totalTimeMsec = 0; for (std::size_t i = 0; i < def->getNumStages(); ++i) { const particles::IStageDef& stage = def->getStage(i); // For ever-repeating stages, set stuff to INT_MAX and break if (stage.getCycles() == 0) { totalTimeMsec = INT_MAX; break; } totalTimeMsec += static_cast(stage.getCycleMsec() * stage.getCycles()); } // Update the sensitivity of the auto-loop button if (totalTimeMsec < INT_MAX) { _automaticLoopButton->GetToolBar()->EnableTool(TOOL_AUTO_LOOP, true); // Auto-Loop is possible, check if we should reset the time if (_automaticLoopButton->IsToggled() && _renderSystem->getTime() > totalTimeMsec) { _renderSystem->setTime(0); } } else { _automaticLoopButton->GetToolBar()->EnableTool(TOOL_AUTO_LOOP, false); } } void ParticlePreview::onModelRotationChanged() { if (_entity) { // Update the model rotation on the entity std::ostringstream value; value << _modelRotation.xx() << ' ' << _modelRotation.xy() << ' ' << _modelRotation.xz() << ' ' << _modelRotation.yx() << ' ' << _modelRotation.yy() << ' ' << _modelRotation.yz() << ' ' << _modelRotation.zx() << ' ' << _modelRotation.zy() << ' ' << _modelRotation.zz(); Node_getEntity(_entity)->setKeyValue("rotation", value.str()); } } void ParticlePreview::drawAxes() { glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glLineWidth(2); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glBegin(GL_LINES); glColor4f(1,0,0,0.6f); glVertex3f(0,0,0); glVertex3f(5,0,0); glColor4f(0,1,0,0.6f); glVertex3f(0,0,0); glVertex3f(0,5,0); glColor4f(0,0,1,0.6f); glVertex3f(0,0,0); glVertex3f(0,0,5); glEnd(); } void ParticlePreview::onToolItemClickRefresh(wxCommandEvent& ev) { queueDraw(); } } // namespace ui DarkRadiant-2.5.0/libs/wxutil/preview/ParticlePreview.h000066400000000000000000000034341321750546400231410ustar00rootroot00000000000000#pragma once #include "RenderPreview.h" #include #include "iparticles.h" #include "iparticlenode.h" #include "imap.h" #include #include namespace wxutil { /// RenderPreview widget for particle systems class ParticlePreview : public RenderPreview { private: wxToolBarToolBase* _showAxesButton; wxToolBarToolBase* _showWireFrameButton; wxToolBarToolBase* _automaticLoopButton; wxToolBarToolBase* _reloadButton; scene::IMapRootNodePtr _rootNode; // A particle is attached to a parent entity scene::INodePtr _entity; // Current particle node to display particles::IParticleNodePtr _particleNode; std::string _lastParticle; public: /// Construct a ParticlePreview widget. ParticlePreview(wxWindow* parent); ~ParticlePreview(); /** * Set the widget to display the given particle. If the particle name is the * empty string, the widget will release the currently displayed one. * * @param * String name of the particle to display. */ void setParticle(const std::string& particle); /** * Get the model from the widget, in order to display properties about it. */ particles::IParticleDefPtr getParticle() { return _particleNode ? _particleNode->getParticle()->getParticleDef() : particles::IParticleDefPtr(); } protected: // Creates parent entity etc. void setupSceneGraph() override; AABB getSceneBounds() override; bool onPreRender() override; void onPostRender() override; void onModelRotationChanged() override; private: void drawAxes(); void drawDebugInfo(); void onToolItemClickRefresh(wxCommandEvent& ev); }; typedef std::shared_ptr ParticlePreviewPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/preview/RenderPreview.cpp000066400000000000000000000563711321750546400231600ustar00rootroot00000000000000#include "RenderPreview.h" #include "ifilter.h" #include "i18n.h" #include "igl.h" #include "icamera.h" #include "iscenegraphfactory.h" #include "irendersystemfactory.h" #include "iuimanager.h" #include "math/AABB.h" #include "util/ScopedBoolLock.h" #include "registry/registry.h" #include "../GLWidget.h" #include #include #include #include #include namespace wxutil { namespace { const GLfloat PREVIEW_FOV = 60; const unsigned int MSEC_PER_FRAME = 16; // Widget names const std::string BOTTOM_BOX("bottomBox"); const std::string PAUSE_BUTTON("pauseButton"); const std::string STOP_BUTTON("stopButton"); const std::string RKEY_RENDERPREVIEW_SHOWGRID("ui/renderPreview/showGrid"); } RenderPreview::RenderPreview(wxWindow* parent, bool enableAnimation) : _mainPanel(loadNamedPanel(parent, "RenderPreviewPanel")), _glWidget(new wxutil::GLWidget(_mainPanel, std::bind(&RenderPreview::drawPreview, this), "RenderPreview")), _initialised(false), _renderGrid(registry::getValue(RKEY_RENDERPREVIEW_SHOWGRID)), _renderSystem(GlobalRenderSystemFactory().createRenderSystem()), _sceneWalker(_renderer, _volumeTest), _viewOrigin(0, 0, 0), _viewAngles(0, 0, 0), _modelView(Matrix4::getIdentity()), _modelRotation(Matrix4::getIdentity()), _lastX(0), _lastY(0), _renderingInProgress(false), _timer(this), _previewWidth(0), _previewHeight(0), _filtersMenu(GlobalUIManager().createFilterMenu()), _filterTool(nullptr) { Connect(wxEVT_TIMER, wxTimerEventHandler(RenderPreview::_onFrame), NULL, this); // Insert GL widget _mainPanel->GetSizer()->Prepend(_glWidget, 1, wxEXPAND); _glWidget->Connect(wxEVT_SIZE, wxSizeEventHandler(RenderPreview::onSizeAllocate), NULL, this); _glWidget->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(RenderPreview::onGLScroll), NULL, this); _glWidget->Connect(wxEVT_MOTION, wxMouseEventHandler(RenderPreview::onGLMotion), NULL, this); _glWidget->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(RenderPreview::onGLMouseClick), NULL, this); _glWidget->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(RenderPreview::onGLMouseClick), NULL, this); _glWidget->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(RenderPreview::onGLMouseClick), NULL, this); _glWidget->Connect(wxEVT_RIGHT_DCLICK, wxMouseEventHandler(RenderPreview::onGLMouseClick), NULL, this); _glWidget->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(RenderPreview::onGLKeyPress), NULL, this); setupToolbars(enableAnimation); // Clicks are eaten when the FreezePointer is active, request to receive them _freezePointer.connectMouseEvents( std::bind(&RenderPreview::onGLMouseClick, this, std::placeholders::_1), std::bind(&RenderPreview::onGLMouseRelease, this, std::placeholders::_1)); } void RenderPreview::setupToolbars(bool enableAnimation) { wxToolBar* toolbar = findNamedObject(_mainPanel, "RenderPreviewAnimToolbar"); _toolbarSizer = toolbar->GetContainingSizer(); // Set up the toolbar if (enableAnimation) { connectToolbarSignals(); } else { toolbar->Hide(); } // Connect filters menu to toolbar wxToolBar* filterToolbar = findNamedObject(_mainPanel, "RenderPreviewFilterToolbar"); wxMenu* filterSubmenu = _filtersMenu->getMenuWidget(); wxToolBarToolBase* filterTool = filterToolbar->AddTool(wxID_ANY, _("Filters"), wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "iconFilter16.png"), _("Filters"), wxITEM_DROPDOWN); // By setting it as dropdown menu the toolitem will take ownership and delete the menu on destruction filterToolbar->SetDropdownMenu(filterTool->GetId(), filterSubmenu); filterToolbar->Realize(); // Get notified of filter changes GlobalFilterSystem().filtersChangedSignal().connect( sigc::mem_fun(this, &RenderPreview::filtersChanged) ); wxToolBar* renderToolbar = findNamedObject(_mainPanel, "RenderPreviewRenderModeToolbar"); renderToolbar->Connect(getToolBarToolByLabel(renderToolbar, "texturedModeButton")->GetId(), wxEVT_TOOL, wxCommandEventHandler(RenderPreview::onRenderModeChanged), NULL, this); renderToolbar->Connect(getToolBarToolByLabel(renderToolbar, "lightingModeButton")->GetId(), wxEVT_TOOL, wxCommandEventHandler(RenderPreview::onRenderModeChanged), NULL, this); updateActiveRenderModeButton(); wxToolBar* utilToolbar = findNamedObject(_mainPanel, "RenderPreviewUtilToolbar"); utilToolbar->Connect(getToolBarToolByLabel(utilToolbar, "gridButton")->GetId(), wxEVT_TOOL, wxCommandEventHandler(RenderPreview::onGridButtonClick), NULL, this); utilToolbar->ToggleTool(getToolBarToolByLabel(utilToolbar, "gridButton")->GetId(), _renderGrid); } void RenderPreview::connectToolbarSignals() { wxToolBar* toolbar = findNamedObject(_mainPanel, "RenderPreviewAnimToolbar"); toolbar->Connect(getToolBarToolByLabel(toolbar, "startTimeButton")->GetId(), wxEVT_TOOL, wxCommandEventHandler(RenderPreview::onStartPlaybackClick), NULL, this); toolbar->Connect(getToolBarToolByLabel(toolbar, "pauseTimeButton")->GetId(), wxEVT_TOOL, wxCommandEventHandler(RenderPreview::onPausePlaybackClick), NULL, this); toolbar->Connect(getToolBarToolByLabel(toolbar, "stopTimeButton")->GetId(), wxEVT_TOOL, wxCommandEventHandler(RenderPreview::onStopPlaybackClick), NULL, this); toolbar->Connect(getToolBarToolByLabel(toolbar, "prevButton")->GetId(), wxEVT_TOOL, wxCommandEventHandler(RenderPreview::onStepBackClick), NULL, this); toolbar->Connect(getToolBarToolByLabel(toolbar, "nextButton")->GetId(), wxEVT_TOOL, wxCommandEventHandler(RenderPreview::onStepForwardClick), NULL, this); } RenderPreview::~RenderPreview() { _timer.Stop(); } void RenderPreview::updateActiveRenderModeButton() { wxToolBar* toolbar = static_cast(_mainPanel->FindWindow("RenderPreviewRenderModeToolbar")); if (getLightingModeEnabled()) { toolbar->ToggleTool(getToolBarToolByLabel(toolbar, "lightingModeButton")->GetId(), true); } else { toolbar->ToggleTool(getToolBarToolByLabel(toolbar, "texturedModeButton")->GetId(), true); } } void RenderPreview::filtersChanged() { if (!getScene()->root()) return; GlobalFilterSystem().updateSubgraph(getScene()->root()); queueDraw(); } void RenderPreview::addToolbar(wxToolBar* toolbar) { _toolbarSizer->Add(toolbar, 0, wxEXPAND); } void RenderPreview::queueDraw() { if (!_renderingInProgress) { _glWidget->Refresh(); } } void RenderPreview::setSize(int width, int height) { _glWidget->SetClientSize(width, height); } void RenderPreview::initialisePreview() { _initialised = true; // Set up the lights glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); GLfloat l0Amb[] = { 0.3f, 0.3f, 0.3f, 1.0f }; GLfloat l0Dif[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat l0Pos[] = { 1.0f, 1.0f, 1.0f, 0.0f }; glLightfv(GL_LIGHT0, GL_AMBIENT, l0Amb); glLightfv(GL_LIGHT0, GL_DIFFUSE, l0Dif); glLightfv(GL_LIGHT0, GL_POSITION, l0Pos); glEnable(GL_LIGHT1); GLfloat l1Dif[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat l1Pos[] = { 0.0, 0.0, 1.0, 0.0 }; glLightfv(GL_LIGHT1, GL_DIFFUSE, l1Dif); glLightfv(GL_LIGHT1, GL_POSITION, l1Pos); if (GlobalOpenGL().shaderProgramsAvailable()) { setLightingModeEnabled(false); } updateModelViewMatrix(); } void RenderPreview::setViewOrigin(const Vector3& origin) { _viewOrigin = origin; updateModelViewMatrix(); } void RenderPreview::setViewAngles(const Vector3& angles) { _viewAngles = angles; updateModelViewMatrix(); } bool RenderPreview::getLightingModeEnabled() { return _renderSystem->getCurrentShaderProgram() == RenderSystem::SHADER_PROGRAM_INTERACTION; } void RenderPreview::setLightingModeEnabled(bool enabled) { if (enabled && !getLightingModeEnabled()) { _renderSystem->setShaderProgram(RenderSystem::SHADER_PROGRAM_INTERACTION); queueDraw(); } else if (!enabled && getLightingModeEnabled()) { _renderSystem->setShaderProgram(RenderSystem::SHADER_PROGRAM_NONE); queueDraw(); } } const scene::GraphPtr& RenderPreview::getScene() { if (!_scene) { _scene = GlobalSceneGraphFactory().createSceneGraph(); setupSceneGraph(); associateRenderSystem(); } return _scene; } void RenderPreview::setupSceneGraph() { // Set our render time to 0 _renderSystem->setTime(0); } void RenderPreview::associateRenderSystem() { if (_scene && _scene->root()) { _scene->root()->setRenderSystem(_renderSystem); } } Matrix4 RenderPreview::getProjectionMatrix(float near_z, float far_z, float fieldOfView, int width, int height) { const float half_width = near_z * tan(degrees_to_radians(fieldOfView * 0.5f)); const float half_height = half_width * (static_cast(height) / static_cast(width)); return Matrix4::getProjectionForFrustum( -half_width, half_width, -half_height, half_height, near_z, far_z ); } const Matrix4& RenderPreview::getModelViewMatrix() { return _modelView; } Matrix4 RenderPreview::calculateModelViewMatrix() { static const Matrix4 RADIANT2OPENGL = Matrix4::byColumns( 0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 1 ); // greebo: This is modeled after the camera's modelview code // Some notes: first the matrix stack is built in the reverse order, // then a final call to inverse() is made to bring them in the correct order // In the final form the matrix stack does this: // 0. We start with the view at the 0,0,0 origin, facing down the negative z axis // 1. Move the view point away from the model (translate by -viewOrigin) // 2. Rotate the view using Euler angles (rotating about two specific axes) // 3. Apply the radiant2openGL matrix which basically rotates the model 90 degrees // around two axes. This is needed to resemble the view of a human (who - with // pitch, yaw and roll angles equal to zero - looks parallel to the xy plane, // and not down the negative z axis like openGL would. // Translate the model by viewOrigin (with the later inverse() call this will // be the first applied translation by -viewOrigin) Matrix4 modelview = Matrix4::getTranslation(_viewOrigin); // Rotate the view like a human would turn their head. Due to the radiant2openGL transform // the axes are used differently. Pitch is rotating around y instead of x, for example. Vector3 radiant_eulerXYZ(0, _viewAngles[ui::CAMERA_PITCH], -_viewAngles[ui::CAMERA_YAW]); modelview.rotateByEulerXYZDegrees(radiant_eulerXYZ); // As last step apply the radiant2openGL transform which rotates first around z, then around y modelview.multiplyBy(RADIANT2OPENGL); // To get the translation transform applied first, inverse the whole stack modelview.invert(); return modelview; } void RenderPreview::updateModelViewMatrix() { _modelView = calculateModelViewMatrix(); } void RenderPreview::startPlayback() { if (_timer.IsRunning()) { // Timer is already running, just reset the preview time _renderSystem->setTime(0); } else { // Timer is not enabled, we're paused or stopped _timer.Start(MSEC_PER_FRAME); } wxToolBar* toolbar = findNamedObject(_mainPanel, "RenderPreviewAnimToolbar"); toolbar->EnableTool(getToolBarToolByLabel(toolbar, "pauseTimeButton")->GetId(), true); toolbar->EnableTool(getToolBarToolByLabel(toolbar, "stopTimeButton")->GetId(), true); } void RenderPreview::stopPlayback() { _renderSystem->setTime(0); _timer.Stop(); wxToolBar* toolbar = findNamedObject(_mainPanel, "RenderPreviewAnimToolbar"); toolbar->EnableTool(getToolBarToolByLabel(toolbar, "pauseTimeButton")->GetId(), false); toolbar->EnableTool(getToolBarToolByLabel(toolbar, "stopTimeButton")->GetId(), false); queueDraw(); } bool RenderPreview::onPreRender() { return true; } RenderStateFlags RenderPreview::getRenderFlagsFill() { return RENDER_MASKCOLOUR | RENDER_ALPHATEST | RENDER_BLEND | RENDER_CULLFACE | RENDER_OFFSETLINE | RENDER_VERTEX_COLOUR | RENDER_FILL | RENDER_LIGHTING | RENDER_TEXTURE_2D | RENDER_SMOOTH | RENDER_SCALED | RENDER_FILL | RENDER_TEXTURE_CUBEMAP | RENDER_BUMP | RENDER_PROGRAM; } RenderStateFlags RenderPreview::getRenderFlagsWireframe() { return RENDER_MASKCOLOUR | RENDER_ALPHATEST | RENDER_BLEND | RENDER_CULLFACE | RENDER_OFFSETLINE | RENDER_VERTEX_COLOUR | RENDER_LIGHTING | RENDER_SMOOTH | RENDER_SCALED | RENDER_TEXTURE_CUBEMAP | RENDER_BUMP | RENDER_PROGRAM; } void RenderPreview::drawPreview() { if (_renderingInProgress) return; // avoid double-entering this method if (!_initialised) { initialisePreview(); } util::ScopedBoolLock lock(_renderingInProgress); glViewport(0, 0, _previewWidth, _previewHeight); // Set up the render and clear the drawing area in any case glDepthMask(GL_TRUE); if (getLightingModeEnabled()) { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } else { glClearColor(0.3f, 0.3f, 0.3f, 1.0f); } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Set up the camera Matrix4 projection = getProjectionMatrix(0.1f, 10000, PREVIEW_FOV, _previewWidth, _previewHeight); // Keep the modelview matrix in the volumetest class up to date _volumeTest.setModelView(getModelViewMatrix()); _volumeTest.setProjection(projection); // Pre-Render event if (!onPreRender()) { // a return value of false means to cancel rendering drawTime(); return; } // Set the projection and modelview matrices glMatrixMode(GL_PROJECTION); glLoadMatrixd(projection); glMatrixMode(GL_MODELVIEW); glLoadMatrixd(_volumeTest.GetModelview()); if (_renderGrid) { drawGrid(); } // Front-end render phase, collect OpenGLRenderable objects from the scene getScene()->foreachVisibleNodeInVolume(_volumeTest, _sceneWalker); RenderStateFlags flags = getRenderFlagsFill(); // Hack-inject the model rotation matrix just before the render pass //Matrix4 modelView = _volumeTest.GetModelview().getMultipliedBy(_modelRotation); // Launch the back end rendering _renderSystem->render(flags, _volumeTest.GetModelview(), projection); // Give subclasses an opportunity to render their own on-screen stuff onPostRender(); // Draw the render time drawTime(); } void RenderPreview::renderWireFrame() { RenderStateFlags flags = getRenderFlagsWireframe(); // Set up the camera Matrix4 projection = getProjectionMatrix(0.1f, 10000, PREVIEW_FOV, _previewWidth, _previewHeight); // Front-end render phase, collect OpenGLRenderable objects from the scene getScene()->foreachVisibleNodeInVolume(_volumeTest, _sceneWalker); // Launch the back end rendering _renderSystem->render(flags, _volumeTest.GetModelview(), projection); } void RenderPreview::onGLMouseClick(wxMouseEvent& ev) { _lastX = ev.GetX(); _lastY = ev.GetY(); if (ev.RightDown()) { if (_freezePointer.isCapturing(_glWidget)) { _freezePointer.endCapture(); return; } // Key event processing requires focus _glWidget->SetFocus(); _freezePointer.startCapture(_glWidget, [&](int x, int y, int mouseState) { onGLMotionDelta(x, y, mouseState); }, [&]() {}); // capture is released by FreezePointer } } void RenderPreview::onGLMouseRelease(wxMouseEvent& ev) { } void RenderPreview::onGLMotion(wxMouseEvent& ev) { if (ev.LeftIsDown()) // dragging with mouse button { // Calculate the mouse delta as a vector in the XY plane, and store the // current position for the next event. Vector3 deltaPos(ev.GetX() - _lastX, _lastY - ev.GetY(), 0); _lastX = ev.GetX(); _lastY = ev.GetY(); // Get the rotation axes in model space Vector3 localXRotAxis = _modelView.getInverse().transformDirection(Vector3(1, 0, 0)); Vector3 localZRotAxis = Vector3(0, 0, 1); if (deltaPos.y() != 0) { _modelRotation.premultiplyBy(Matrix4::getRotation(localXRotAxis, degrees_to_radians(deltaPos.y()))); } if (deltaPos.x() != 0) { _modelRotation.premultiplyBy(Matrix4::getRotation(localZRotAxis, degrees_to_radians(-deltaPos.x()))); } // Notify the subclasses to do something with this matrix onModelRotationChanged(); queueDraw(); } } void RenderPreview::onGLMotionDelta(int x, int y, unsigned int mouseState) { const float dtime = 0.1f; const float angleSpeed = 3; // camerasettings::anglespeed _viewAngles[ui::CAMERA_PITCH] += y * dtime * angleSpeed; _viewAngles[ui::CAMERA_YAW] += x * dtime * angleSpeed; if (_viewAngles[ui::CAMERA_PITCH] > 90) _viewAngles[ui::CAMERA_PITCH] = 90; else if (_viewAngles[ui::CAMERA_PITCH] < -90) _viewAngles[ui::CAMERA_PITCH] = -90; if (_viewAngles[ui::CAMERA_YAW] >= 360) _viewAngles[ui::CAMERA_YAW] -= 360; else if (_viewAngles[ui::CAMERA_YAW] <= 0) _viewAngles[ui::CAMERA_YAW] += 360; updateModelViewMatrix(); queueDraw(); } AABB RenderPreview::getSceneBounds() { return AABB(Vector3(0,0,0), Vector3(64,64,64)); } void RenderPreview::resetModelRotation() { _modelRotation = Matrix4::getIdentity(); onModelRotationChanged(); } void RenderPreview::onGLScroll(wxMouseEvent& ev) { // Scroll increment is a fraction of the AABB radius float inc = static_cast(getSceneBounds().getRadius()) * 0.3f; Vector3 forward(_modelView[2], _modelView[6], _modelView[10]); if (ev.GetWheelRotation() > 0) { _viewOrigin -= forward * inc; } else if (ev.GetWheelRotation() < 0) { _viewOrigin += forward * inc; } updateModelViewMatrix(); queueDraw(); } void RenderPreview::onRenderModeChanged(wxCommandEvent& ev) { if (ev.GetInt() == 0) // un-toggled { return; // Don't react on UnToggle events } wxToolBar* toolbar = static_cast(_mainPanel->FindWindow("RenderPreviewRenderModeToolbar")); // This function will be called twice, once for the inactivating button and // once for the activating button if (getToolBarToolByLabel(toolbar, "texturedModeButton")->GetId() == ev.GetId()) { setLightingModeEnabled(false); } else if (getToolBarToolByLabel(toolbar, "lightingModeButton")->GetId() == ev.GetId()) { setLightingModeEnabled(true); } } void RenderPreview::onGridButtonClick(wxCommandEvent& ev) { _renderGrid = (ev.GetInt() != 0); registry::setValue(RKEY_RENDERPREVIEW_SHOWGRID, _renderGrid); queueDraw(); } void RenderPreview::onStartPlaybackClick(wxCommandEvent& ev) { startPlayback(); } void RenderPreview::onStopPlaybackClick(wxCommandEvent& ev) { stopPlayback(); } void RenderPreview::onPausePlaybackClick(wxCommandEvent& ev) { // Disable the button wxToolBar* toolbar = findNamedObject(_mainPanel, "RenderPreviewAnimToolbar"); toolbar->EnableTool(getToolBarToolByLabel(toolbar, "pauseTimeButton")->GetId(), false); if (_timer.IsRunning()) { _timer.Stop(); } else { _timer.Start(MSEC_PER_FRAME); // re-enable playback } } void RenderPreview::onStepForwardClick(wxCommandEvent& ev) { // Disable the button wxToolBar* toolbar = findNamedObject(_mainPanel, "RenderPreviewAnimToolbar"); toolbar->EnableTool(getToolBarToolByLabel(toolbar, "pauseTimeButton")->GetId(), false); if (_timer.IsRunning()) { _timer.Stop(); } _renderSystem->setTime(_renderSystem->getTime() + MSEC_PER_FRAME); queueDraw(); } void RenderPreview::onStepBackClick(wxCommandEvent& ev) { // Disable the button wxToolBar* toolbar = findNamedObject(_mainPanel, "RenderPreviewAnimToolbar"); toolbar->EnableTool(getToolBarToolByLabel(toolbar, "pauseTimeButton")->GetId(), false); if (_timer.IsRunning()) { _timer.Stop(); } if (_renderSystem->getTime() > 0) { _renderSystem->setTime(_renderSystem->getTime() - MSEC_PER_FRAME); } queueDraw(); } void RenderPreview::onSizeAllocate(wxSizeEvent& ev) { _previewWidth = ev.GetSize().GetWidth(); _previewHeight = ev.GetSize().GetHeight(); } void RenderPreview::drawGrid() { static float GRID_MAX_DIM = 512.0f; static float GRID_STEP = 16.0f; glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_1D); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glLineWidth(1); glColor3f(0.7f, 0.7f, 0.7f); glBegin(GL_LINES); for (float x = -GRID_MAX_DIM; x < GRID_MAX_DIM; x += GRID_STEP) { Vector3 start(x, -GRID_MAX_DIM, 0); Vector3 end(x, GRID_MAX_DIM, 0); Vector3 start2(GRID_MAX_DIM, x, 0); Vector3 end2(-GRID_MAX_DIM, x, 0); glVertex2dv(start); glVertex2dv(end); glVertex2dv(start2); glVertex2dv(end2); } glEnd(); } void RenderPreview::drawTime() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, static_cast(_previewWidth), 0, static_cast(_previewHeight), -100, 100); glScalef(1, -1, 1); glTranslatef(0, -static_cast(_previewHeight), 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (GLEW_VERSION_1_3) { glClientActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0); } glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glDisable(GL_COLOR_MATERIAL); glDisable(GL_DEPTH_TEST); glColor3f( 1.f, 1.f, 1.f ); glLineWidth(1); glRasterPos3f(1.0f, static_cast(_previewHeight) - 1.0f, 0.0f); GlobalOpenGL().drawString(fmt::format("{0:.3f} sec.", (_renderSystem->getTime() * 0.001f))); } void RenderPreview::onGLKeyPress(wxKeyEvent& ev) { if (!_freezePointer.isCapturing(_glWidget)) { return; } float inc = static_cast(getSceneBounds().getRadius()) * 0.12f; if (ev.ShiftDown()) { inc *= 4.0f; } Vector3 forward(_modelView[2], _modelView[6], _modelView[10]); Vector3 right(_modelView[0], _modelView[4], _modelView[8]); switch (ev.GetKeyCode()) { case WXK_UP: _viewOrigin -= forward * inc; break; case WXK_DOWN: _viewOrigin += forward * inc; break; case WXK_RIGHT: _viewOrigin += right * inc; break; case WXK_LEFT: _viewOrigin -= right * inc; break; default: ev.Skip(); return; } updateModelViewMatrix(); queueDraw(); } void RenderPreview::_onFrame(wxTimerEvent& ev) { if (!_renderingInProgress) { _renderSystem->setTime(_renderSystem->getTime() + MSEC_PER_FRAME); queueDraw(); } } } // namespace DarkRadiant-2.5.0/libs/wxutil/preview/RenderPreview.h000066400000000000000000000131711321750546400226140ustar00rootroot00000000000000#pragma once #include #include #include #include "math/Matrix4.h" #include "../XmlResourceBasedWidget.h" #include "ifiltermenu.h" #include "iscenegraph.h" #include "irender.h" #include "../FreezePointer.h" #include "render/ShaderStateRenderer.h" #include "render/SceneRenderWalker.h" #include "render/NopVolumeTest.h" class wxToolBarToolBase; namespace wxutil { class GLWidget; /** * greebo: This class acts as base for widgets featuring * a real time openGL render preview. It offers * its own local SceneGraph, backend and frontend renderer * plus all the logic for camera handling and filtering. * * Override the protected methods to have the scene set up * in special ways or add custom toolbar items. * * After construction the local scene graph will be empty. */ class RenderPreview : public wxEvtHandler, public sigc::trackable, private XmlResourceBasedWidget { private: void connectToolbarSignals(); void drawPreview(); void onGLScroll(wxMouseEvent& ev); void onGLMouseClick(wxMouseEvent& ev); void onGLMouseRelease(wxMouseEvent& ev); void onGLMotion(wxMouseEvent& ev); void onGLMotionDelta(int x, int y, unsigned int mouseState); void onGLKeyPress(wxKeyEvent& ev); void onStartPlaybackClick(wxCommandEvent& ev); void onStopPlaybackClick(wxCommandEvent& ev); void onPausePlaybackClick(wxCommandEvent& ev); void onStepForwardClick(wxCommandEvent& ev); void onStepBackClick(wxCommandEvent& ev); void onSizeAllocate(wxSizeEvent& ev); void filtersChanged(); void onRenderModeChanged(wxCommandEvent& ev); void onGridButtonClick(wxCommandEvent& ev); void drawTime(); void drawGrid(); // Called each frame by wxTimer void _onFrame(wxTimerEvent& ev); void updateModelViewMatrix(); void updateActiveRenderModeButton(); void setupToolbars(bool enableAnimation); protected: wxPanel* _mainPanel; private: // The scene we're rendering scene::GraphPtr _scene; // GL widget GLWidget* _glWidget; bool _initialised; FreezePointer _freezePointer; bool _renderGrid; protected: // The backend rendersystem instance RenderSystemPtr _renderSystem; // The front-end renderer, collecting the OpenGLRenderables render::ShaderStateRenderer _renderer; render::NopVolumeTest _volumeTest; // The scene adaptor passing nodes into our front-end renderer render::SceneRenderWalker _sceneWalker; // Current viewer position and view angles Vector3 _viewOrigin; Vector3 _viewAngles; // Current modelview matrix Matrix4 _modelView; // The local model orientation Matrix4 _modelRotation; int _lastX; int _lastY; // Mutex flag to avoid draw call bunching bool _renderingInProgress; wxTimer _timer; int _previewWidth; int _previewHeight; wxSizer* _toolbarSizer; // The filters menu ui::IFilterMenuPtr _filtersMenu; wxToolBarToolBase* _filterTool; protected: const scene::GraphPtr& getScene(); /// Add another one to the toolbar hbox void addToolbar(wxToolBar* toolbar); // Subclasses should at least add a single node as scene root, such that // the rendersystem can be associated. This is called after initialisePreview() virtual void setupSceneGraph(); virtual Matrix4 getProjectionMatrix(float near_z, float far_z, float fieldOfView, int width, int height); virtual const Matrix4& getModelViewMatrix(); virtual Matrix4 calculateModelViewMatrix(); void resetModelRotation(); // When the user requests a model rotation change (by dragging) virtual void onModelRotationChanged() {} virtual void startPlayback(); virtual void stopPlayback(); // Override this to deliver accurate scene bounds, used for mousewheel-zooming virtual AABB getSceneBounds(); // Called right before rendering, returning false will cancel the render algorithm virtual bool onPreRender(); // Called after the render phase, can be used to draw custom stuff on the GL widget virtual void onPostRender() {} // Use this to render a wireframe view of the scene void renderWireFrame(); // Override these to define the flags to render a fill/wireframe scene virtual RenderStateFlags getRenderFlagsFill(); virtual RenderStateFlags getRenderFlagsWireframe(); void associateRenderSystem(); // Can be overridden by subclasses to update their scene/models virtual void onRenderModeChanged() {} /** * \brief * Construct a RenderPreview * * \param enableAnimation * If true, display animation controls in toolbar, otherwise hide the * animation controls. */ RenderPreview(wxWindow* parent, bool enableAnimation = true); virtual ~RenderPreview(); public: wxPanel* getWidget() const { return _mainPanel; } void setSize(int width, int height); /** * Initialise the GL preview. This clears the window and sets up the * initial matrices and lights. */ void initialisePreview(); /// Get the RenderSystem used by the preview const RenderSystemPtr& getRenderSystem() { return _renderSystem; } // Defines the position of the camera void setViewOrigin(const Vector3& origin); // Defines the view angles (euler angles in degrees) void setViewAngles(const Vector3& angles); // Check whether lighting mode is enabled bool getLightingModeEnabled(); // Enable/disable lighting mode void setLightingModeEnabled(bool enabled); /// Schedule a GL widget redraw operation void queueDraw(); }; typedef std::shared_ptr RenderPreviewPtr; } // namespace DarkRadiant-2.5.0/libs/wxutil/window/000077500000000000000000000000001321750546400175055ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/wxutil/window/TransientWindow.cpp000066400000000000000000000055761321750546400233650ustar00rootroot00000000000000#include "TransientWindow.h" #include "iuimanager.h" #include "imainframe.h" #include "iregistry.h" #include namespace wxutil { TransientWindow::TransientWindow(const std::string& title, wxWindow* parent, bool hideOnDelete) : wxFrame(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxSYSTEM_MENU | wxRESIZE_BORDER | wxCLOSE_BOX | wxCAPTION | wxFRAME_TOOL_WINDOW | wxCLIP_CHILDREN | wxFRAME_FLOAT_ON_PARENT | wxFRAME_NO_TASKBAR), _hideOnDelete(hideOnDelete) { Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TransientWindow::_onDelete), NULL, this); Connect(wxEVT_SHOW, wxShowEventHandler(TransientWindow::_onShowHide), NULL, this); CenterOnParent(); // Set the window icon wxIcon appIcon; appIcon.CopyFromBitmap(wxArtProvider::GetBitmap( GlobalUIManager().ArtIdPrefix() + "darkradiant_icon_64x64.png")); SetIcon(appIcon); } bool TransientWindow::Show(bool show) { if (show) { _preShow(); } else { _preHide(); } // Pass the call to base return wxFrame::Show(show); } void TransientWindow::_postHide() { // Bring the mainframe to foreground after closing this Window (#3965) // If we don't do this, some completely different application like Windows Explorer // might get the focus instead. if (GlobalMainFrame().getWxTopLevelWindow() != NULL) { GlobalMainFrame().getWxTopLevelWindow()->SetFocus(); } } void TransientWindow::_onShowHide(wxShowEvent& ev) { ev.Skip(); if (ev.IsShown()) { _postShow(); } else { _postHide(); } } bool TransientWindow::_onDeleteEvent() { if (_hideOnDelete) { Hide(); return true; // veto event } _preDestroy(); Destroy(); _postDestroy(); return false; } void TransientWindow::_preShow() { // Restore the position _windowPosition.applyPosition(); } void TransientWindow::_preHide() { SaveWindowState(); } void TransientWindow::SaveWindowState() { // Save the window position, to make sure _windowPosition.readPosition(); // Tell the position tracker to save the information if (!_windowStateKey.empty()) { _windowPosition.saveToPath(_windowStateKey); } } void TransientWindow::ToggleVisibility() { if (!IsShownOnScreen()) { Show(); } else { Hide(); } } void TransientWindow::InitialiseWindowPosition(int defaultWidth, int defaultHeight, const std::string& windowStateKey) { SetSize(defaultWidth, defaultHeight); Fit(); _windowStateKey = windowStateKey; if (GlobalRegistry().keyExists(_windowStateKey)) { // Connect the window position tracker _windowPosition.loadFromPath(_windowStateKey); } _windowPosition.connect(this); } const std::string& TransientWindow::GetWindowStateKey() const { return _windowStateKey; } void TransientWindow::_onDelete(wxCloseEvent& ev) { if (_onDeleteEvent()) { ev.Veto(); } } void TransientWindow::_onFocus(wxFocusEvent& ev) { _onSetFocus(); ev.Skip(); } } // namespace DarkRadiant-2.5.0/libs/wxutil/window/TransientWindow.h000066400000000000000000000026551321750546400230250ustar00rootroot00000000000000#pragma once #include #include "wxutil/WindowPosition.h" namespace wxutil { class TransientWindow : public wxFrame { private: // Whether this window should be hidden rather than destroyed bool _hideOnDelete; // The window position tracker WindowPosition _windowPosition; // Registry key to load/save window position std::string _windowStateKey; protected: // Customisable virtuals implemented by subclasses virtual void _preShow(); virtual void _postShow() { } virtual void _preHide(); virtual void _postHide(); virtual void _preDestroy() { } virtual void _postDestroy() { } // Return true to prevent the window from being deleted virtual bool _onDeleteEvent(); virtual void _onSetFocus() { } // Set the default size and (if a key is given) load and apply the stored // window position from the registry void InitialiseWindowPosition(int defaultWidth, int defaultHeight, const std::string& windowStateKey); // Returns the registry key the window is saving the state to const std::string& GetWindowStateKey() const; public: TransientWindow(const std::string& title, wxWindow* parent, bool hideOnDelete = false); virtual ~TransientWindow() {} // Override wxWindow::Show virtual bool Show(bool show = true); virtual void ToggleVisibility(); virtual void SaveWindowState(); private: void _onDelete(wxCloseEvent& ev); void _onShowHide(wxShowEvent& ev); void _onFocus(wxFocusEvent& ev); }; } DarkRadiant-2.5.0/libs/xmlutil/000077500000000000000000000000001321750546400163405ustar00rootroot00000000000000DarkRadiant-2.5.0/libs/xmlutil/Document.cpp000066400000000000000000000110771321750546400206300ustar00rootroot00000000000000#include "Document.h" #include "XPathException.h" #include "itextstream.h" #include #include namespace xml { // Construct a wrapper around the provided xmlDocPtr. Document::Document(xmlDocPtr doc): _xmlDoc(doc) {} Document::Document(const std::string& filename) : _xmlDoc(xmlParseFile(filename.c_str())) {} Document::Document(const Document& other) : _xmlDoc(other._xmlDoc) {} Document::~Document() { if (_xmlDoc != nullptr) { // Free the xml document memory xmlFreeDoc(_xmlDoc); } } Document Document::create() { xmlChar* versionStr = xmlCharStrdup("1.0"); // Create a new xmlDocPtr and return the object xmlDocPtr doc = xmlNewDoc(versionStr); xmlFree(versionStr); return Document(doc); } Document Document::clone(const Document& source) { if (source._xmlDoc == nullptr) { // Nothing to clone, create an empty doc return Document(nullptr); } // Create a deep copy of the other doc return Document(xmlCopyDoc(source._xmlDoc, 1)); } void Document::addTopLevelNode(const std::string& name) { std::lock_guard lock(_lock); if (!isValid()) { return; // is not Valid, place an assertion here? } xmlChar* nameStr = xmlCharStrdup(name.c_str()); xmlChar* emptyStr = xmlCharStrdup(""); xmlNodePtr root = xmlNewDocNode(_xmlDoc, NULL, nameStr, emptyStr); xmlNodePtr oldRoot = xmlDocSetRootElement(_xmlDoc, root); if (oldRoot != NULL) { // Old root element, remove it xmlUnlinkNode(oldRoot); xmlFreeNode(oldRoot); } xmlFree(nameStr); xmlFree(emptyStr); } Node Document::getTopLevelNode() const { if (!isValid()) { // Invalid Document, return a NULL node return Node(NULL); } return Node(_xmlDoc->children); } void Document::importDocument(Document& other, Node& importNode) { std::lock_guard lock(_lock); // Locate the top-level node(s) of the other document xml::NodeList topLevelNodes = other.findXPath("/*"); xmlNodePtr targetNode = importNode.getNodePtr(); if (targetNode->name == NULL) { // invalid importnode return; } // greebo: Not all target nodes already have a valid child node, use a modified algorithm // to handle that situation as suggested by malex984 // Add each of the imported nodes to the target importNode for (std::size_t i = 0; i < topLevelNodes.size(); ++i) { if (targetNode->children == NULL) { xmlUnlinkNode(topLevelNodes[i].getNodePtr()); xmlAddChild(targetNode, topLevelNodes[i].getNodePtr()); } else { xmlAddPrevSibling(targetNode->children, topLevelNodes[i].getNodePtr()); } } } void Document::copyNodes(const NodeList& nodeList) { std::lock_guard lock(_lock); if (!isValid() || _xmlDoc->children == NULL) { return; // is not Valid, place an assertion here? } // Copy the child nodes one by one for (std::size_t i = 0; i < nodeList.size(); i++) { // Copy the node xmlNodePtr node = xmlCopyNode(nodeList[i].getNodePtr(), 1); // Add this node to the top level node of this document xmlAddChild(xmlDocGetRootElement(_xmlDoc), node); } } bool Document::isValid() const { return _xmlDoc != nullptr; } // Evaluate an XPath expression and return matching Nodes. NodeList Document::findXPath(const std::string& path) const { std::lock_guard lock(_lock); // Set up the XPath context xmlXPathContextPtr context = xmlXPathNewContext(_xmlDoc); if (context == NULL) { rConsoleError() << "ERROR: xml::findPath() failed to create XPath context " << "when searching for " << path << std::endl; throw XPathException("Failed to create XPath context"); } // Evaluate the expression const xmlChar* xpath = reinterpret_cast(path.c_str()); xmlXPathObjectPtr result = xmlXPathEvalExpression(xpath, context); xmlXPathFreeContext(context); if (result == NULL) { rConsoleError() << "ERROR: xml::findPath() failed to evaluate expression " << path << std::endl; throw XPathException("Failed to evaluate XPath expression"); } // Construct the return vector. This may be empty if the provided XPath // expression does not identify any nodes. NodeList retval; xmlNodeSetPtr nodeset = result->nodesetval; if (nodeset != NULL) { for (int i = 0; i < nodeset->nodeNr; i++) { retval.push_back(Node(nodeset->nodeTab[i])); } } xmlXPathFreeObject(result); return retval; } // Saves the file to the disk via xmlSaveFormatFile void Document::saveToFile(const std::string& filename) const { std::lock_guard lock(_lock); xmlSaveFormatFile(filename.c_str(), _xmlDoc, 1); } } DarkRadiant-2.5.0/libs/xmlutil/Document.h000066400000000000000000000040141321750546400202660ustar00rootroot00000000000000#pragma once #include "Node.h" #include typedef struct _xmlDoc xmlDoc; typedef xmlDoc *xmlDocPtr; #include namespace xml { /* Document * * This is a wrapper class for an xmlDocPtr. It provides a function to * evaluate an XPath expression on the document and return the set of * matching Nodes. * * The contained xmlDocPtr is automatically released on destruction * of this object. */ class Document { private: // Contained xmlDocPtr. xmlDocPtr _xmlDoc; mutable std::mutex _lock; public: // Construct a Document using the provided xmlDocPtr. Document(xmlDocPtr doc); // Construct a xml::Document from the given filename (must be the full path). // Use the isValid() method to check if the load was successful. Document(const std::string& filename); // Copy constructor (note: does not create an actual copy of the internal xmlDoc) Document(const Document& other); // Destructor, frees the xmlDocPtr ~Document(); // Creates a new xml::Document object (allocates a new xmlDoc) static Document create(); // Creates a deep copy of the given Document static Document clone(const Document& source); // Add a new toplevel node with the given name to this Document void addTopLevelNode(const std::string& name); // Returns the top level node (or an empty Node object if none exists) Node getTopLevelNode() const; // Merges the (top-level) nodes of the document into this one. // The insertion point in this Document is specified by . void importDocument(Document& other, Node& importNode); // Copies the given Nodes into this document (a top level node // must be created beforehand) void copyNodes(const NodeList& nodeList); // Returns TRUE if the document is ok and can be queried. bool isValid() const; // Evaluate the given XPath expression and return a NodeList of matching // nodes. NodeList findXPath(const std::string& path) const; // Saves the file to the disk via xmlSaveFormatFile void saveToFile(const std::string& filename) const; }; } DarkRadiant-2.5.0/libs/xmlutil/InvalidNodeException.h000066400000000000000000000006651321750546400225730ustar00rootroot00000000000000#ifndef INVALIDNODEEXCEPTION_H_ #define INVALIDNODEEXCEPTION_H_ #include #include namespace xml { // Exception to indicate an invalid Node was passed to a function. class InvalidNodeException: public std::runtime_error { public: // Constructor. Must initialise the parent. InvalidNodeException(const std::string& what): std::runtime_error(what) {} }; } #endif /*INVALIDNODEEXCEPTION_H_*/ DarkRadiant-2.5.0/libs/xmlutil/Makefile.am000066400000000000000000000003521321750546400203740ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs \ $(XML_CFLAGS) pkglib_LTLIBRARIES = libxmlutil.la libxmlutil_la_LDFLAGS = -release @PACKAGE_VERSION@ $(XML_LIBS) libxmlutil_la_SOURCES = Document.cpp Node.cpp DarkRadiant-2.5.0/libs/xmlutil/MissingXMLNodeException.h000066400000000000000000000006531321750546400231740ustar00rootroot00000000000000#ifndef MISSINGXMLNODEEXCEPTION_H_ #define MISSINGXMLNODEEXCEPTION_H_ #include #include namespace xml { /** * Exception thrown by modules if the values it needs * from the registry cannot be found. */ class MissingXMLNodeException : public std::runtime_error { public: // Constructor MissingXMLNodeException(const std::string& what) : std::runtime_error(what) {} }; } // namespace xml #endif DarkRadiant-2.5.0/libs/xmlutil/Node.cpp000066400000000000000000000074111321750546400177340ustar00rootroot00000000000000#include "Node.h" #include namespace xml { // Construct a Node from the given xmlNodePtr. Node::Node(xmlNodePtr node): _xmlNode(node) {} // Return the actual node ptr xmlNodePtr Node::getNodePtr() const { return _xmlNode; } // Return the name of a node const std::string Node::getName() const { if (_xmlNode) { return std::string( reinterpret_cast(_xmlNode->name) ); } else { return ""; } } // Return a NodeList of all children of this node NodeList Node::getChildren() const { NodeList retval; // Iterate throught the list of children, adding each child node // to the return list if it matches the requested name for (xmlNodePtr child = _xmlNode->children; child != NULL; child = child->next) { retval.push_back(child); } return retval; } // Creates a new child with no content (i.e. ) Node Node::createChild(const std::string& name) { xmlChar* nodeName = xmlCharStrdup(name.c_str()); // Create a new child under the contained node xmlNodePtr newChild = xmlNewChild(_xmlNode, NULL, nodeName, NULL); xmlFree(nodeName); // Create a new xml::Node out of this pointer and return it return Node(newChild); } // Return a NodeList of named children of this node NodeList Node::getNamedChildren(const std::string& name) const { NodeList retval; // Iterate throught the list of children, adding each child node // to the return list if it matches the requested name for (xmlNodePtr child = _xmlNode->children; child != NULL; child = child->next) { if (xmlStrcmp(child->name, reinterpret_cast(name.c_str())) == 0) { retval.push_back(child); } } return retval; } // Set the value of the given attribute void Node::setAttributeValue(const std::string& key, const std::string& value) { xmlChar* k = xmlCharStrdup(key.c_str()); xmlChar* v = xmlCharStrdup(value.c_str()); xmlSetProp(_xmlNode, k, v); xmlFree(k); xmlFree(v); } // Return the value of a given attribute, or throw AttributeNotFoundException // if the attribute does not exist. std::string Node::getAttributeValue(const std::string& key) const { // Iterate through the chain of attributes to find the requested one. for (xmlAttrPtr attr = _xmlNode->properties; attr != NULL; attr = attr->next) { if (xmlStrcmp(attr->name, reinterpret_cast(key.c_str())) == 0) { return reinterpret_cast(attr->children->content); } } // Not found, return an empty string return ""; } // Return the textual content of a given node. This may be an empty string if there is no // content available. std::string Node::getContent() const { if (_xmlNode->children && _xmlNode->children->content) { return std::string(reinterpret_cast(_xmlNode->children->content)); } else { return ""; } } void Node::setContent(const std::string& content) { // Remove all text children first for (xmlNodePtr child = _xmlNode->children; child != nullptr; ) { xmlNodePtr next = child->next; if (child->type == XML_TEXT_NODE) { xmlUnlinkNode(child); xmlFreeNode(child); } child = next; } xmlNodePtr child = xmlNewText(reinterpret_cast(content.c_str())); xmlAddChild(_xmlNode, child); } void Node::addText(const std::string& text) { // Allocate a new text node xmlNodePtr whitespace = xmlNewText( reinterpret_cast(text.c_str()) ); // Add the newly allocated text as sibling of this node xmlAddSibling(_xmlNode, whitespace); } void Node::erase() { // unlink the node from the list first, otherwise: crashes ahead! xmlUnlinkNode(_xmlNode); // All child nodes are freed recursively xmlFreeNode(_xmlNode); } } // namespace xml DarkRadiant-2.5.0/libs/xmlutil/Node.h000066400000000000000000000035421321750546400174020ustar00rootroot00000000000000#pragma once // Forward declaration to avoid including the whole libxml2 headers typedef struct _xmlNode xmlNode; typedef xmlNode *xmlNodePtr; #include #include namespace xml { // Typedefs class Node; typedef std::vector NodeList; /* Node * * A representation of an XML node. This class wraps an xmlNodePtr as used * by libxml2, and provides certain methods to access properties of the node. */ class Node { private: // The contained xmlNodePtr. This points to part of a wider xmlDoc // structure which is not owned by this Node object. xmlNodePtr _xmlNode; public: // Construct a Node from the provided xmlNodePtr. Node(xmlNodePtr node); // Get the actual node pointer to a given node xmlNodePtr getNodePtr() const; // Get the name of the given node const std::string getName() const; // Get a list of nodes which are children of this node NodeList getChildren() const; // Creates a new child under this XML Node Node createChild(const std::string& name); // Get a list of nodes which are children of this node and match the // given name. NodeList getNamedChildren(const std::string& name) const; // Return the value of the given attribute, or an empty string // if the attribute is not present on this Node. std::string getAttributeValue(const std::string& key) const; // Set the value of the given attribute void setAttributeValue(const std::string& key, const std::string& value); /** Return the text content of this node. * * @returns * The text content of this node. */ std::string getContent() const; // Sets the contents of this XML node to the given string void setContent(const std::string& content); void addText(const std::string& text); // Unlink and delete the node and all its children void erase(); }; } // namespace xml DarkRadiant-2.5.0/libs/xmlutil/XPathException.h000066400000000000000000000006211321750546400214130ustar00rootroot00000000000000#ifndef XPATHEXCEPTION_H_ #define XPATHEXCEPTION_H_ #include #include namespace xml { // Exception to indicate failure to process an XPath lookup. class XPathException: public std::runtime_error { public: // Constructor. Must initialise the parent. XPathException(const std::string& what): std::runtime_error(what) {} }; } #endif /*XPATHEXCEPTION_H_*/ DarkRadiant-2.5.0/m4/000077500000000000000000000000001321750546400142315ustar00rootroot00000000000000DarkRadiant-2.5.0/m4/ax_cxx_compile_stdcxx.m4000066400000000000000000000474241321750546400211050ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) # or '14' (for the C++14 standard). # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016 Krzesimir Nowak # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 6 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AX_REQUIRE_DEFINED([AC_MSG_WARN]) AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, ax_cv_cxx_compile_cxx$1, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [ax_cv_cxx_compile_cxx$1=yes], [ax_cv_cxx_compile_cxx$1=no])]) if test x$ax_cv_cxx_compile_cxx$1 = xyes; then ac_success=yes fi m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])]) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) dnl Tests for new features in C++17 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus <= 201402L #error "This is not a C++17 compiler" #else #if defined(__clang__) #define REALLY_CLANG #else #if defined(__GNUC__) #define REALLY_GCC #endif #endif #include #include #include namespace cxx17 { #if !defined(REALLY_CLANG) namespace test_constexpr_lambdas { // TODO: test it with clang++ from git constexpr int foo = [](){return 42;}(); } #endif // !defined(REALLY_CLANG) namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } #if !defined(REALLY_CLANG) namespace test_template_argument_deduction_for_class_templates { // TODO: test it with clang++ from git template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } #endif // !defined(REALLY_CLANG) namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } #if !defined(REALLY_CLANG) namespace test_structured_bindings { // TODO: test it with clang++ from git int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } #endif // !defined(REALLY_CLANG) #if !defined(REALLY_CLANG) namespace test_exception_spec_type_system { // TODO: test it with clang++ from git struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } #endif // !defined(REALLY_CLANG) namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus <= 201402L ]]) DarkRadiant-2.5.0/m4/boost.m4000066400000000000000000001551431321750546400156320ustar00rootroot00000000000000# boost.m4: Locate Boost headers and libraries for autoconf-based projects. # Copyright (C) 2007-2011, 2014 Benoit Sigoure # # 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. # # Additional permission under section 7 of the GNU General Public # License, version 3 ("GPLv3"): # # If you convey this file as part of a work that contains a # configuration script generated by Autoconf, you may do so under # terms of your choice. # # 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 . m4_define([_BOOST_SERIAL], [m4_translit([ # serial 26 ], [# ], [])]) # Original sources can be found at http://github.com/tsuna/boost.m4 # You can fetch the latest version of the script by doing: # wget http://github.com/tsuna/boost.m4/raw/master/build-aux/boost.m4 # ------ # # README # # ------ # # This file provides several macros to use the various Boost libraries. # The first macro is BOOST_REQUIRE. It will simply check if it's possible to # find the Boost headers of a given (optional) minimum version and it will # define BOOST_CPPFLAGS accordingly. It will add an option --with-boost to # your configure so that users can specify non standard locations. # If the user's environment contains BOOST_ROOT and --with-boost was not # specified, --with-boost=$BOOST_ROOT is implicitly used. # For more README and documentation, go to http://github.com/tsuna/boost.m4 # Note: THESE MACROS ASSUME THAT YOU USE LIBTOOL. If you don't, don't worry, # simply read the README, it will show you what to do step by step. m4_pattern_forbid([^_?(BOOST|Boost)_]) # _BOOST_SED_CPP(SED-PROGRAM, PROGRAM, # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # -------------------------------------------------------- # Same as AC_EGREP_CPP, but leave the result in conftest.i. # # SED-PROGRAM is *not* overquoted, as in AC_EGREP_CPP. It is expanded # in double-quotes, so escape your double quotes. # # It could be useful to turn this into a macro which extracts the # value of any macro. m4_define([_BOOST_SED_CPP], [AC_LANG_PUSH([C++])dnl AC_LANG_PREPROC_REQUIRE()dnl AC_REQUIRE([AC_PROG_SED])dnl AC_LANG_CONFTEST([AC_LANG_SOURCE([[$2]])]) AS_IF([dnl eval is necessary to expand ac_cpp. dnl Ultrix and Pyramid sh refuse to redirect output of eval, so use subshell. dnl Beware of Windows end-of-lines, for instance if we are running dnl some Windows programs under Wine. In that case, boost/version.hpp dnl is certainly using "\r\n", but the regular Unix shell will only dnl strip `\n' with backquotes, not the `\r'. This results in dnl boost_cv_lib_version='1_37\r' for instance, which breaks dnl everything else. dnl Cannot use 'dnl' after [$4] because a trailing dnl may break AC_CACHE_CHECK dnl dnl Beware that GCC 5, when expanding macros, may embed # line directives dnl a within single line: dnl dnl # 1 "conftest.cc" dnl # 1 "" dnl # 1 "" dnl # 1 "conftest.cc" dnl # 1 "/opt/local/include/boost/version.hpp" 1 3 dnl # 2 "conftest.cc" 2 dnl boost-lib-version = dnl # 2 "conftest.cc" 3 dnl "1_56" dnl dnl So get rid of the # and empty lines, and glue the remaining ones together. (eval "$ac_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD | grep -v '#' | grep -v '^[[[:space:]]]*$' | tr -d '\r' | tr -s '\n' ' ' | $SED -n -e "$1" >conftest.i 2>&1], [$3], [$4]) rm -rf conftest* AC_LANG_POP([C++])dnl ])# _BOOST_SED_CPP # BOOST_REQUIRE([VERSION], [ACTION-IF-NOT-FOUND]) # ----------------------------------------------- # Look for Boost. If version is given, it must either be a literal of the form # "X.Y.Z" where X, Y and Z are integers (the ".Z" part being optional) or a # variable "$var". # Defines the value BOOST_CPPFLAGS. This macro only checks for headers with # the required version, it does not check for any of the Boost libraries. # On # success, defines HAVE_BOOST. On failure, calls the optional # ACTION-IF-NOT-FOUND action if one was supplied. # Otherwise aborts with an error message. AC_DEFUN_ONCE([BOOST_REQUIRE], [AC_REQUIRE([AC_PROG_CXX])dnl AC_REQUIRE([AC_PROG_GREP])dnl echo "$as_me: this is boost.m4[]_BOOST_SERIAL" >&AS_MESSAGE_LOG_FD boost_save_IFS=$IFS boost_version_req=$1 IFS=. set x $boost_version_req 0 0 0 IFS=$boost_save_IFS shift boost_version_req=`expr "$[1]" '*' 100000 + "$[2]" '*' 100 + "$[3]"` boost_version_req_string=$[1].$[2].$[3] AC_ARG_WITH([boost], [AS_HELP_STRING([--with-boost=DIR], [prefix of Boost $1 @<:@guess@:>@])])dnl AC_ARG_VAR([BOOST_ROOT],[Location of Boost installation])dnl # If BOOST_ROOT is set and the user has not provided a value to # --with-boost, then treat BOOST_ROOT as if it the user supplied it. if test x"$BOOST_ROOT" != x; then if test x"$with_boost" = x; then AC_MSG_NOTICE([Detected BOOST_ROOT; continuing with --with-boost=$BOOST_ROOT]) with_boost=$BOOST_ROOT else AC_MSG_NOTICE([Detected BOOST_ROOT=$BOOST_ROOT, but overridden by --with-boost=$with_boost]) fi fi AC_SUBST([DISTCHECK_CONFIGURE_FLAGS], ["$DISTCHECK_CONFIGURE_FLAGS '--with-boost=$with_boost'"])dnl boost_save_CPPFLAGS=$CPPFLAGS AC_CACHE_CHECK([for Boost headers version >= $boost_version_req_string], [boost_cv_inc_path], [boost_cv_inc_path=no AC_LANG_PUSH([C++])dnl m4_pattern_allow([^BOOST_VERSION$])dnl AC_LANG_CONFTEST([AC_LANG_PROGRAM([[#include #if !defined BOOST_VERSION # error BOOST_VERSION is not defined #elif BOOST_VERSION < $boost_version_req # error Boost headers version < $boost_version_req #endif ]])]) # If the user provided a value to --with-boost, use it and only it. case $with_boost in #( ''|yes) set x '' /opt/local/include /usr/local/include /opt/include \ /usr/include C:/Boost/include;; #( *) set x "$with_boost/include" "$with_boost";; esac shift for boost_dir do # Without --layout=system, Boost (or at least some versions) installs # itself in /include/boost-. This inner loop helps to # find headers in such directories. # # Any ${boost_dir}/boost-x_xx directories are searched in reverse version # order followed by ${boost_dir}. The final '.' is a sentinel for # searching $boost_dir" itself. Entries are whitespace separated. # # I didn't indent this loop on purpose (to avoid over-indented code) boost_layout_system_search_list=`cd "$boost_dir" 2>/dev/null \ && ls -1 | "${GREP}" '^boost-' | sort -rn -t- -k2 \ && echo .` for boost_inc in $boost_layout_system_search_list do if test x"$boost_inc" != x.; then boost_inc="$boost_dir/$boost_inc" else boost_inc="$boost_dir" # Uses sentinel in boost_layout_system_search_list fi if test x"$boost_inc" != x; then # We are going to check whether the version of Boost installed # in $boost_inc is usable by running a compilation that # #includes it. But if we pass a -I/some/path in which Boost # is not installed, the compiler will just skip this -I and # use other locations (either from CPPFLAGS, or from its list # of system include directories). As a result we would use # header installed on the machine instead of the /some/path # specified by the user. So in that precise case (trying # $boost_inc), make sure the version.hpp exists. # # Use test -e as there can be symlinks. test -e "$boost_inc/boost/version.hpp" || continue CPPFLAGS="$CPPFLAGS -I$boost_inc" fi AC_COMPILE_IFELSE([], [boost_cv_inc_path=yes], [boost_cv_version=no]) if test x"$boost_cv_inc_path" = xyes; then if test x"$boost_inc" != x; then boost_cv_inc_path=$boost_inc fi break 2 fi done done AC_LANG_POP([C++])dnl ]) case $boost_cv_inc_path in #( no) boost_errmsg="cannot find Boost headers version >= $boost_version_req_string" m4_if([$2], [], [AC_MSG_ERROR([$boost_errmsg])], [AC_MSG_NOTICE([$boost_errmsg])]) $2 ;;#( yes) BOOST_CPPFLAGS= ;;#( *) AC_SUBST([BOOST_CPPFLAGS], ["-I$boost_cv_inc_path"])dnl ;; esac if test x"$boost_cv_inc_path" != xno; then AC_DEFINE([HAVE_BOOST], [1], [Defined if the requested minimum BOOST version is satisfied]) AC_CACHE_CHECK([for Boost's header version], [boost_cv_lib_version], [m4_pattern_allow([^BOOST_LIB_VERSION$])dnl _BOOST_SED_CPP([[/^boost-lib-version = /{s///;s/[\" ]//g;p;q;}]], [#include boost-lib-version = BOOST_LIB_VERSION], [boost_cv_lib_version=`cat conftest.i`])]) # e.g. "134" for 1_34_1 or "135" for 1_35 boost_major_version=`echo "$boost_cv_lib_version" | sed 's/_//;s/_.*//'` case $boost_major_version in #( '' | *[[!0-9]]*) AC_MSG_ERROR([invalid value: boost_major_version='$boost_major_version']) ;; esac fi CPPFLAGS=$boost_save_CPPFLAGS ])# BOOST_REQUIRE # BOOST_STATIC() # -------------- # Add the "--enable-static-boost" configure argument. If this argument is given # on the command line, static versions of the libraries will be looked up. AC_DEFUN([BOOST_STATIC], [AC_ARG_ENABLE([static-boost], [AS_HELP_STRING([--enable-static-boost], [Prefer the static boost libraries over the shared ones [no]])], [enable_static_boost=yes], [enable_static_boost=no])])# BOOST_STATIC # BOOST_FIND_HEADER([HEADER-NAME], [ACTION-IF-NOT-FOUND], [ACTION-IF-FOUND]) # -------------------------------------------------------------------------- # Wrapper around AC_CHECK_HEADER for Boost headers. Useful to check for # some parts of the Boost library which are only made of headers and don't # require linking (such as Boost.Foreach). # # Default ACTION-IF-NOT-FOUND: Fail with a fatal error unless Boost couldn't be # found in the first place, in which case by default a notice is issued to the # user. Presumably if we haven't died already it's because it's OK to not have # Boost, which is why only a notice is issued instead of a hard error. # # Default ACTION-IF-FOUND: define the preprocessor symbol HAVE_ in # case of success # (where HEADER-NAME is written LIKE_THIS, e.g., # HAVE_BOOST_FOREACH_HPP). AC_DEFUN([BOOST_FIND_HEADER], [AC_REQUIRE([BOOST_REQUIRE])dnl if test x"$boost_cv_inc_path" = xno; then m4_default([$2], [AC_MSG_NOTICE([Boost not available, not searching for $1])]) else AC_LANG_PUSH([C++])dnl boost_save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_CHECK_HEADER([$1], [m4_default([$3], [AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1], [Define to 1 if you have <$1>])])], [m4_default([$2], [AC_MSG_ERROR([cannot find $1])])]) CPPFLAGS=$boost_save_CPPFLAGS AC_LANG_POP([C++])dnl fi ])# BOOST_FIND_HEADER # BOOST_FIND_LIBS([COMPONENT-NAME], [CANDIDATE-LIB-NAMES], # [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], # [CXX-PROLOGUE]) # -------------------------------------------------------------- # Look for the Boost library COMPONENT-NAME (e.g., `thread', for # libboost_thread) under the possible CANDIDATE-LIB-NAMES (e.g., # "thread_win32 thread"). Check that HEADER-NAME works and check that # libboost_LIB-NAME can link with the code CXX-TEST. The optional # argument CXX-PROLOGUE can be used to include some C++ code before # the `main' function. # # Invokes BOOST_FIND_HEADER([HEADER-NAME]) (see above). # # Boost libraries typically come compiled with several flavors (with different # runtime options) so PREFERRED-RT-OPT is the preferred suffix. A suffix is one # or more of the following letters: sgdpn (in that order). s = static # runtime, d = debug build, g = debug/diagnostic runtime, p = STLPort build, # n = (unsure) STLPort build without iostreams from STLPort (it looks like `n' # must always be used along with `p'). Additionally, PREFERRED-RT-OPT can # start with `mt-' to indicate that there is a preference for multi-thread # builds. Some sample values for PREFERRED-RT-OPT: (nothing), mt, d, mt-d, gdp # ... If you want to make sure you have a specific version of Boost # (eg, >= 1.33) you *must* invoke BOOST_REQUIRE before this macro. AC_DEFUN([BOOST_FIND_LIBS], [AC_REQUIRE([BOOST_REQUIRE])dnl AC_REQUIRE([_BOOST_FIND_COMPILER_TAG])dnl AC_REQUIRE([BOOST_STATIC])dnl AC_REQUIRE([_BOOST_GUESS_WHETHER_TO_USE_MT])dnl if test x"$boost_cv_inc_path" = xno; then AC_MSG_NOTICE([Boost not available, not searching for the Boost $1 library]) else dnl The else branch is huge and wasn't intended on purpose. AC_LANG_PUSH([C++])dnl AS_VAR_PUSHDEF([Boost_lib], [boost_cv_lib_$1])dnl AS_VAR_PUSHDEF([Boost_lib_LDFLAGS], [boost_cv_lib_$1_LDFLAGS])dnl AS_VAR_PUSHDEF([Boost_lib_LDPATH], [boost_cv_lib_$1_LDPATH])dnl AS_VAR_PUSHDEF([Boost_lib_LIBS], [boost_cv_lib_$1_LIBS])dnl BOOST_FIND_HEADER([$4]) boost_save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_CACHE_CHECK([for the Boost $1 library], [Boost_lib], [_BOOST_FIND_LIBS($@)]) case $Boost_lib in #( (no) _AC_MSG_LOG_CONFTEST AC_MSG_ERROR([cannot find the flags to link with Boost $1]) ;; esac AC_SUBST(AS_TR_CPP([BOOST_$1_LDFLAGS]), [$Boost_lib_LDFLAGS])dnl AC_SUBST(AS_TR_CPP([BOOST_$1_LDPATH]), [$Boost_lib_LDPATH])dnl AC_SUBST([BOOST_LDPATH], [$Boost_lib_LDPATH])dnl AC_SUBST(AS_TR_CPP([BOOST_$1_LIBS]), [$Boost_lib_LIBS])dnl CPPFLAGS=$boost_save_CPPFLAGS AS_VAR_POPDEF([Boost_lib])dnl AS_VAR_POPDEF([Boost_lib_LDFLAGS])dnl AS_VAR_POPDEF([Boost_lib_LDPATH])dnl AS_VAR_POPDEF([Boost_lib_LIBS])dnl AC_LANG_POP([C++])dnl fi ]) # BOOST_FIND_LIB([LIB-NAME], # [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], # [CXX-PROLOGUE]) # -------------------------------------------------------------- # Backward compatibility wrapper for BOOST_FIND_LIBS. AC_DEFUN([BOOST_FIND_LIB], [BOOST_FIND_LIBS([$1], $@)]) # _BOOST_FIND_LIBS([LIB-NAME], [CANDIDATE-LIB-NAMES], # [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], # [CXX-PROLOGUE]) # -------------------------------------------------------------- # Real implementation of BOOST_FIND_LIBS: rely on these local macros: # Boost_lib, Boost_lib_LDFLAGS, Boost_lib_LDPATH, Boost_lib_LIBS # # The algorithm is as follows: first look for a given library name # according to the user's PREFERRED-RT-OPT. For each library name, we # prefer to use the ones that carry the tag (toolset name). Each # library is searched through the various standard paths were Boost is # usually installed. If we can't find the standard variants, we try # to enforce -mt (for instance on MacOSX, libboost_thread.dylib # doesn't exist but there's -obviously- libboost_thread-mt.dylib). AC_DEFUN([_BOOST_FIND_LIBS], [Boost_lib=no case "$3" in #( (mt | mt-) boost_mt=-mt; boost_rtopt=;; #( (mt* | mt-*) boost_mt=-mt; boost_rtopt=`expr "X$3" : 'Xmt-*\(.*\)'`;; #( (*) boost_mt=; boost_rtopt=$3;; esac if test $enable_static_boost = yes; then boost_rtopt="s$boost_rtopt" fi # Find the proper debug variant depending on what we've been asked to find. case $boost_rtopt in #( (*d*) boost_rt_d=$boost_rtopt;; #( (*[[sgpn]]*) # Insert the `d' at the right place (in between `sg' and `pn') boost_rt_d=`echo "$boost_rtopt" | sed 's/\(s*g*\)\(p*n*\)/\1\2/'`;; #( (*) boost_rt_d='-d';; esac # If the PREFERRED-RT-OPT are not empty, prepend a `-'. test -n "$boost_rtopt" && boost_rtopt="-$boost_rtopt" $boost_guess_use_mt && boost_mt=-mt # Look for the abs path the static archive. # $libext is computed by Libtool but let's make sure it's non empty. test -z "$libext" && AC_MSG_ERROR([the libext variable is empty, did you invoke Libtool?]) boost_save_ac_objext=$ac_objext # Generate the test file. AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <$4> $6], [$5])]) dnl Optimization hacks: compiling C++ is slow, especially with Boost. What dnl we're trying to do here is guess the right combination of link flags dnl (LIBS / LDFLAGS) to use a given library. This can take several dnl iterations before it succeeds and is thus *very* slow. So what we do dnl instead is that we compile the code first (and thus get an object file, dnl typically conftest.o). Then we try various combinations of link flags dnl until we succeed to link conftest.o in an executable. The problem is dnl that the various TRY_LINK / COMPILE_IFELSE macros of Autoconf always dnl remove all the temporary files including conftest.o. So the trick here dnl is to temporarily change the value of ac_objext so that conftest.o is dnl preserved accross tests. This is obviously fragile and I will burn in dnl hell for not respecting Autoconf's documented interfaces, but in the dnl mean time, it optimizes the macro by a factor of 5 to 30. dnl Another small optimization: the first argument of AC_COMPILE_IFELSE left dnl empty because the test file is generated only once above (before we dnl start the for loops). AC_COMPILE_IFELSE([], [ac_objext=do_not_rm_me_plz], [AC_MSG_ERROR([cannot compile a test that uses Boost $1])]) ac_objext=$boost_save_ac_objext boost_failed_libs= # Don't bother to ident the following nested for loops, only the 2 # innermost ones matter. for boost_lib_ in $2; do for boost_tag_ in -$boost_cv_lib_tag ''; do for boost_ver_ in -$boost_cv_lib_version ''; do for boost_mt_ in $boost_mt -mt ''; do for boost_rtopt_ in $boost_rtopt '' -d; do for boost_lib in \ boost_$boost_lib_$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \ boost_$boost_lib_$boost_tag_$boost_rtopt_$boost_ver_ \ boost_$boost_lib_$boost_tag_$boost_mt_$boost_ver_ \ boost_$boost_lib_$boost_tag_$boost_ver_ do # Avoid testing twice the same lib case $boost_failed_libs in #( (*@$boost_lib@*) continue;; esac # If with_boost is empty, we'll search in /lib first, which is not quite # right so instead we'll try to a location based on where the headers are. boost_tmp_lib=$with_boost test x"$with_boost" = x && boost_tmp_lib=${boost_cv_inc_path%/include} for boost_ldpath in "$boost_tmp_lib/lib" '' \ /opt/local/lib* /usr/local/lib* /opt/lib* /usr/lib* \ "$with_boost" C:/Boost/lib /lib* do # Don't waste time with directories that don't exist. if test x"$boost_ldpath" != x && test ! -e "$boost_ldpath"; then continue fi boost_save_LDFLAGS=$LDFLAGS # Are we looking for a static library? case $boost_ldpath:$boost_rtopt_ in #( (*?*:*s*) # Yes (Non empty boost_ldpath + s in rt opt) Boost_lib_LIBS="$boost_ldpath/lib$boost_lib.$libext" test -e "$Boost_lib_LIBS" || continue;; #( (*) # No: use -lboost_foo to find the shared library. Boost_lib_LIBS="-l$boost_lib";; esac boost_save_LIBS=$LIBS LIBS="$Boost_lib_LIBS $LIBS" test x"$boost_ldpath" != x && LDFLAGS="$LDFLAGS -L$boost_ldpath" dnl First argument of AC_LINK_IFELSE left empty because the test file is dnl generated only once above (before we start the for loops). _BOOST_AC_LINK_IFELSE([], [Boost_lib=yes], [Boost_lib=no]) ac_objext=$boost_save_ac_objext LDFLAGS=$boost_save_LDFLAGS LIBS=$boost_save_LIBS if test x"$Boost_lib" = xyes; then # Check or used cached result of whether or not using -R or # -rpath makes sense. Some implementations of ld, such as for # Mac OSX, require -rpath but -R is the flag known to work on # other systems. https://github.com/tsuna/boost.m4/issues/19 AC_CACHE_VAL([boost_cv_rpath_link_ldflag], [case $boost_ldpath in '') # Nothing to do. boost_cv_rpath_link_ldflag= boost_rpath_link_ldflag_found=yes;; *) for boost_cv_rpath_link_ldflag in -Wl,-R, -Wl,-rpath,; do LDFLAGS="$boost_save_LDFLAGS -L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath" LIBS="$boost_save_LIBS $Boost_lib_LIBS" _BOOST_AC_LINK_IFELSE([], [boost_rpath_link_ldflag_found=yes break], [boost_rpath_link_ldflag_found=no]) done ;; esac AS_IF([test "x$boost_rpath_link_ldflag_found" != "xyes"], [AC_MSG_ERROR([Unable to determine whether to use -R or -rpath])]) LDFLAGS=$boost_save_LDFLAGS LIBS=$boost_save_LIBS ]) test x"$boost_ldpath" != x && Boost_lib_LDFLAGS="-L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath" Boost_lib_LDPATH="$boost_ldpath" break 7 else boost_failed_libs="$boost_failed_libs@$boost_lib@" fi done done done done done done done # boost_lib_ rm -f conftest.$ac_objext ]) # --------------------------------------- # # Checks for the various Boost libraries. # # --------------------------------------- # # List of boost libraries: http://www.boost.org/libs/libraries.htm # The page http://beta.boost.org/doc/libs is useful: it gives the first release # version of each library (among other things). # BOOST_DEFUN(LIBRARY, CODE) # -------------------------- # Define BOOST_ as a macro that runs CODE. # # Use indir to avoid the warning on underquoted macro name given to AC_DEFUN. m4_define([BOOST_DEFUN], [m4_indir([AC_DEFUN], m4_toupper([BOOST_$1]), [m4_pushdef([BOOST_Library], [$1])dnl $2 m4_popdef([BOOST_Library])dnl ]) ]) # BOOST_ARRAY() # ------------- # Look for Boost.Array BOOST_DEFUN([Array], [BOOST_FIND_HEADER([boost/array.hpp])]) # BOOST_ASIO() # ------------ # Look for Boost.Asio (new in Boost 1.35). BOOST_DEFUN([Asio], [AC_REQUIRE([BOOST_SYSTEM])dnl BOOST_FIND_HEADER([boost/asio.hpp])]) # BOOST_ASSIGN() # ------------- # Look for Boost.Assign BOOST_DEFUN([Assign], [BOOST_FIND_HEADER([boost/assign.hpp])]) # BOOST_BIND() # ------------ # Look for Boost.Bind. BOOST_DEFUN([Bind], [BOOST_FIND_HEADER([boost/bind.hpp])]) # BOOST_CHRONO() # -------------- # Look for Boost.Chrono. BOOST_DEFUN([Chrono], [# Do we have to check for Boost.System? This link-time dependency was # added as of 1.35.0. If we have a version <1.35, we must not attempt to # find Boost.System as it didn't exist by then. if test $boost_major_version -ge 135; then BOOST_SYSTEM([$1]) fi # end of the Boost.System check. boost_filesystem_save_LIBS=$LIBS boost_filesystem_save_LDFLAGS=$LDFLAGS m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" BOOST_FIND_LIB([chrono], [$1], [boost/chrono.hpp], [boost::chrono::thread_clock d;]) if test $enable_static_boost = yes && test $boost_major_version -ge 135; then BOOST_CHRONO_LIBS="$BOOST_CHRONO_LIBS $BOOST_SYSTEM_LIBS" fi LIBS=$boost_filesystem_save_LIBS LDFLAGS=$boost_filesystem_save_LDFLAGS ])# BOOST_CHRONO # BOOST_CONTEXT([PREFERRED-RT-OPT]) # ----------------------------------- # Look for Boost.Context. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. # # * This library was introduced in Boost 1.51.0 # * The signatures of make_fcontext() and jump_fcontext were changed in 1.56.0 # * A dependency on boost_thread appears in 1.57.0 BOOST_DEFUN([Context], [boost_context_save_LIBS=$LIBS boost_context_save_LDFLAGS=$LDFLAGS if test $boost_major_version -ge 157; then BOOST_THREAD([$1]) m4_pattern_allow([^BOOST_THREAD_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_THREAD_LIBS" LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS" fi BOOST_FIND_LIB([context], [$1], [boost/context/all.hpp],[[ // creates a stack void * stack_pointer = new void*[4096]; std::size_t const size = sizeof(void*[4096]); #if BOOST_VERSION <= 105100 ctx::make_fcontext(&fc, f); return ctx::jump_fcontext(&fcm, &fc, 3) == 6; #else fc = ctx::make_fcontext(stack_pointer, size, f); return ctx::jump_fcontext(&fcm, fc, 3) == 6; #endif ]],[dnl #include #if BOOST_VERSION <= 105100 namespace ctx = boost::ctx; static ctx::fcontext_t fcm, fc; static void f(intptr_t i) { ctx::jump_fcontext(&fc, &fcm, i * 2); } #elif BOOST_VERSION <= 105500 namespace ctx = boost::context; // context static ctx::fcontext_t fcm, *fc; // context-function static void f(intptr_t i) { ctx::jump_fcontext(fc, &fcm, i * 2); } #else namespace ctx = boost::context; // context static ctx::fcontext_t fcm, fc; // context-function static void f(intptr_t i) { ctx::jump_fcontext(&fc, fcm, i * 2); } #endif ]) LIBS=$boost_context_save_LIBS LDFLAGS=$boost_context_save_LDFLAGS ])# BOOST_CONTEXT # BOOST_CONVERSION() # ------------------ # Look for Boost.Conversion (cast / lexical_cast) BOOST_DEFUN([Conversion], [BOOST_FIND_HEADER([boost/cast.hpp]) BOOST_FIND_HEADER([boost/lexical_cast.hpp]) ])# BOOST_CONVERSION # BOOST_COROUTINE([PREFERRED-RT-OPT]) # ----------------------------------- # Look for Boost.Coroutine. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. This library was introduced in Boost # 1.53.0 BOOST_DEFUN([Coroutine], [ boost_coroutine_save_LIBS=$LIBS boost_coroutine_save_LDFLAGS=$LDFLAGS # Link-time dependency from coroutine to context BOOST_CONTEXT([$1]) # Starting from Boost 1.55 a dependency on Boost.System is added if test $boost_major_version -ge 155; then BOOST_SYSTEM([$1]) fi m4_pattern_allow([^BOOST_(CONTEXT|SYSTEM)_(LIBS|LDFLAGS)]) LIBS="$LIBS $BOOST_CONTEXT_LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_CONTEXT_LDFLAGS" # in 1.53 coroutine was a header only library if test $boost_major_version -eq 153; then BOOST_FIND_HEADER([boost/coroutine/coroutine.hpp]) else BOOST_FIND_LIB([coroutine], [$1], [boost/coroutine/coroutine.hpp], [ #include #if BOOST_VERSION <= 105500 boost::coroutines::coroutine coro; coro.get(); #else boost::coroutines::asymmetric_coroutine::pull_type coro; coro.get(); #endif ]) fi # Link-time dependency from coroutine to context, existed only in 1.53, in 1.54 # coroutine doesn't use context from its headers but from its library. if test $boost_major_version -eq 153 || test $enable_static_boost = yes && test $boost_major_version -ge 154; then BOOST_COROUTINE_LIBS="$BOOST_COROUTINE_LIBS $BOOST_CONTEXT_LIBS" BOOST_COROUTINE_LDFLAGS="$BOOST_COROUTINE_LDFLAGS $BOOST_CONTEXT_LDFLAGS" fi if test $enable_static_boost = yes && test $boost_major_version -ge 155; then BOOST_COROUTINE_LIBS="$BOOST_COROUTINE_LIBS $BOOST_SYSTEM_LIBS" BOOST_COROUTINE_LDFLAGS="$BOOST_COROUTINE_LDFLAGS $BOOST_SYSTEM_LDFLAGS" fi LIBS=$boost_coroutine_save_LIBS LDFLAGS=$boost_coroutine_save_LDFLAGS ])# BOOST_COROUTINE # BOOST_CRC() # ----------- # Look for Boost.CRC BOOST_DEFUN([CRC], [BOOST_FIND_HEADER([boost/crc.hpp]) ])# BOOST_CRC # BOOST_DATE_TIME([PREFERRED-RT-OPT]) # ----------------------------------- # Look for Boost.Date_Time. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Date_Time], [BOOST_FIND_LIB([date_time], [$1], [boost/date_time/posix_time/posix_time.hpp], [boost::posix_time::ptime t;]) ])# BOOST_DATE_TIME # BOOST_FILESYSTEM([PREFERRED-RT-OPT]) # ------------------------------------ # Look for Boost.Filesystem. For the documentation of PREFERRED-RT-OPT, see # the documentation of BOOST_FIND_LIB above. # Do not check for boost/filesystem.hpp because this file was introduced in # 1.34. BOOST_DEFUN([Filesystem], [# Do we have to check for Boost.System? This link-time dependency was # added as of 1.35.0. If we have a version <1.35, we must not attempt to # find Boost.System as it didn't exist by then. if test $boost_major_version -ge 135; then BOOST_SYSTEM([$1]) fi # end of the Boost.System check. boost_filesystem_save_LIBS=$LIBS boost_filesystem_save_LDFLAGS=$LDFLAGS m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" BOOST_FIND_LIB([filesystem], [$1], [boost/filesystem/path.hpp], [boost::filesystem::path p;]) if test $enable_static_boost = yes && test $boost_major_version -ge 135; then BOOST_FILESYSTEM_LIBS="$BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" fi LIBS=$boost_filesystem_save_LIBS LDFLAGS=$boost_filesystem_save_LDFLAGS ])# BOOST_FILESYSTEM # BOOST_FLYWEIGHT() # ----------------- # Look for Boost.Flyweight. BOOST_DEFUN([Flyweight], [dnl There's a hidden dependency on pthreads. AC_REQUIRE([_BOOST_PTHREAD_FLAG])dnl BOOST_FIND_HEADER([boost/flyweight.hpp]) AC_SUBST([BOOST_FLYWEIGHT_LIBS], [$boost_cv_pthread_flag]) ]) # BOOST_FOREACH() # --------------- # Look for Boost.Foreach. BOOST_DEFUN([Foreach], [BOOST_FIND_HEADER([boost/foreach.hpp])]) # BOOST_FORMAT() # -------------- # Look for Boost.Format. # Note: we can't check for boost/format/format_fwd.hpp because the header isn't # standalone. It can't be compiled because it triggers the following error: # boost/format/detail/config_macros.hpp:88: error: 'locale' in namespace 'std' # does not name a type BOOST_DEFUN([Format], [BOOST_FIND_HEADER([boost/format.hpp])]) # BOOST_FUNCTION() # ---------------- # Look for Boost.Function BOOST_DEFUN([Function], [BOOST_FIND_HEADER([boost/function.hpp])]) # BOOST_GEOMETRY() # ---------------- # Look for Boost.Geometry (new since 1.47.0). BOOST_DEFUN([Geometry], [BOOST_FIND_HEADER([boost/geometry.hpp]) ])# BOOST_GEOMETRY # BOOST_GRAPH([PREFERRED-RT-OPT]) # ------------------------------- # Look for Boost.Graphs. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Graph], [boost_graph_save_LIBS=$LIBS boost_graph_save_LDFLAGS=$LDFLAGS # Link-time dependency from graph to regex was added as of 1.40.0. if test $boost_major_version -ge 140; then BOOST_REGEX([$1]) m4_pattern_allow([^BOOST_REGEX_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_REGEX_LIBS" LDFLAGS="$LDFLAGS $BOOST_REGEX_LDFLAGS" fi BOOST_FIND_LIB([graph], [$1], [boost/graph/adjacency_list.hpp], [boost::adjacency_list<> g;]) LIBS=$boost_graph_save_LIBS LDFLAGS=$boost_graph_save_LDFLAGS ])# BOOST_GRAPH # BOOST_IOSTREAMS([PREFERRED-RT-OPT]) # ----------------------------------- # Look for Boost.IOStreams. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([IOStreams], [BOOST_FIND_LIB([iostreams], [$1], [boost/iostreams/device/file_descriptor.hpp], [boost::iostreams::file_descriptor fd; fd.close();]) ])# BOOST_IOSTREAMS # BOOST_HASH() # ------------ # Look for Boost.Functional/Hash BOOST_DEFUN([Hash], [BOOST_FIND_HEADER([boost/functional/hash.hpp])]) # BOOST_LAMBDA() # -------------- # Look for Boost.Lambda BOOST_DEFUN([Lambda], [BOOST_FIND_HEADER([boost/lambda/lambda.hpp])]) # BOOST_LOCALE() # -------------- # Look for Boost.Locale BOOST_DEFUN([Locale], [ boost_locale_save_LIBS=$LIBS boost_locale_save_LDFLAGS=$LDFLAGS # require SYSTEM for boost-1.50.0 and up if test $boost_major_version -ge 150; then BOOST_SYSTEM([$1]) m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" fi # end of the Boost.System check. BOOST_FIND_LIB([locale], [$1], [boost/locale.hpp], [[boost::locale::generator gen; std::locale::global(gen(""));]]) LIBS=$boost_locale_save_LIBS LDFLAGS=$boost_locale_save_LDFLAGS ])# BOOST_LOCALE # BOOST_LOG([PREFERRED-RT-OPT]) # ----------------------------- # Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Log], [boost_log_save_LIBS=$LIBS boost_log_save_LDFLAGS=$LDFLAGS BOOST_SYSTEM([$1]) BOOST_FILESYSTEM([$1]) BOOST_DATE_TIME([$1]) m4_pattern_allow([^BOOST_(SYSTEM|FILESYSTEM|DATE_TIME)_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_DATE_TIME_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_DATE_TIME_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS $BOOST_SYSTEM_LDFLAGS" BOOST_FIND_LIB([log], [$1], [boost/log/core/core.hpp], [boost::log::attribute a; a.get_value();]) LIBS=$boost_log_save_LIBS LDFLAGS=$boost_log_save_LDFLAGS ])# BOOST_LOG # BOOST_LOG_SETUP([PREFERRED-RT-OPT]) # ----------------------------------- # Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Log_Setup], [boost_log_setup_save_LIBS=$LIBS boost_log_setup_save_LDFLAGS=$LDFLAGS BOOST_LOG([$1]) m4_pattern_allow([^BOOST_LOG_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_LOG_LIBS" LDFLAGS="$LDFLAGS $BOOST_LOG_LDFLAGS" BOOST_FIND_LIB([log_setup], [$1], [boost/log/utility/setup/from_settings.hpp], [boost::log::basic_settings bs; bs.empty();]) LIBS=$boost_log_setup_save_LIBS LDFLAGS=$boost_log_setup_save_LDFLAGS ])# BOOST_LOG_SETUP # BOOST_MATH() # ------------ # Look for Boost.Math # TODO: This library isn't header-only but it comes in multiple different # flavors that don't play well with BOOST_FIND_LIB (e.g, libboost_math_c99, # libboost_math_c99f, libboost_math_c99l, libboost_math_tr1, # libboost_math_tr1f, libboost_math_tr1l). This macro must be fixed to do the # right thing anyway. BOOST_DEFUN([Math], [BOOST_FIND_HEADER([boost/math/special_functions.hpp])]) # BOOST_MPI([PREFERRED-RT-OPT]) # ------------------------------- # Look for Boost MPI. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. Uses MPICXX variable if it is # set, otherwise tries CXX # BOOST_DEFUN([MPI], [boost_save_CXX=${CXX} boost_save_CXXCPP=${CXXCPP} if test x"${MPICXX}" != x; then CXX=${MPICXX} CXXCPP="${MPICXX} -E" fi BOOST_FIND_LIB([mpi], [$1], [boost/mpi.hpp], [int argc = 0; char **argv = 0; boost::mpi::environment env(argc,argv);]) CXX=${boost_save_CXX} CXXCPP=${boost_save_CXXCPP} ])# BOOST_MPI # BOOST_MULTIARRAY() # ------------------ # Look for Boost.MultiArray BOOST_DEFUN([MultiArray], [BOOST_FIND_HEADER([boost/multi_array.hpp])]) # BOOST_NUMERIC_UBLAS() # -------------------------- # Look for Boost.NumericUblas (Basic Linear Algebra) BOOST_DEFUN([Numeric_Ublas], [BOOST_FIND_HEADER([boost/numeric/ublas/vector.hpp]) ])# BOOST_NUMERIC_UBLAS # BOOST_NUMERIC_CONVERSION() # -------------------------- # Look for Boost.NumericConversion (policy-based numeric conversion) BOOST_DEFUN([Numeric_Conversion], [BOOST_FIND_HEADER([boost/numeric/conversion/converter.hpp]) ])# BOOST_NUMERIC_CONVERSION # BOOST_OPTIONAL() # ---------------- # Look for Boost.Optional BOOST_DEFUN([Optional], [BOOST_FIND_HEADER([boost/optional.hpp])]) # BOOST_PREPROCESSOR() # -------------------- # Look for Boost.Preprocessor BOOST_DEFUN([Preprocessor], [BOOST_FIND_HEADER([boost/preprocessor/repeat.hpp])]) # BOOST_RANGE() # -------------------- # Look for Boost.Range BOOST_DEFUN([Range], [BOOST_FIND_HEADER([boost/range/adaptors.hpp])]) # BOOST_UNORDERED() # ----------------- # Look for Boost.Unordered BOOST_DEFUN([Unordered], [BOOST_FIND_HEADER([boost/unordered_map.hpp])]) # BOOST_UUID() # ------------ # Look for Boost.Uuid BOOST_DEFUN([Uuid], [BOOST_FIND_HEADER([boost/uuid/uuid.hpp])]) # BOOST_PROGRAM_OPTIONS([PREFERRED-RT-OPT]) # ----------------------------------------- # Look for Boost.Program_options. For the documentation of PREFERRED-RT-OPT, # see the documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Program_Options], [BOOST_FIND_LIB([program_options], [$1], [boost/program_options.hpp], [boost::program_options::options_description d("test");]) ])# BOOST_PROGRAM_OPTIONS # _BOOST_PYTHON_CONFIG(VARIABLE, FLAG) # ------------------------------------ # Save VARIABLE, and define it via `python-config --FLAG`. # Substitute BOOST_PYTHON_VARIABLE. m4_define([_BOOST_PYTHON_CONFIG], [AC_SUBST([BOOST_PYTHON_$1], [`python-config --$2 2>/dev/null`])dnl boost_python_save_$1=$$1 $1="$$1 $BOOST_PYTHON_$1"]) # BOOST_PYTHON([PREFERRED-RT-OPT]) # -------------------------------- # Look for Boost.Python. For the documentation of PREFERRED-RT-OPT, # see the documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Python], [_BOOST_PYTHON_CONFIG([CPPFLAGS], [includes]) _BOOST_PYTHON_CONFIG([LDFLAGS], [ldflags]) _BOOST_PYTHON_CONFIG([LIBS], [libs]) m4_pattern_allow([^BOOST_PYTHON_MODULE$])dnl BOOST_FIND_LIBS([python], [python python3], [$1], [boost/python.hpp], [], [BOOST_PYTHON_MODULE(empty) {}]) CPPFLAGS=$boost_python_save_CPPFLAGS LDFLAGS=$boost_python_save_LDFLAGS LIBS=$boost_python_save_LIBS ])# BOOST_PYTHON # BOOST_REF() # ----------- # Look for Boost.Ref BOOST_DEFUN([Ref], [BOOST_FIND_HEADER([boost/ref.hpp])]) # BOOST_REGEX([PREFERRED-RT-OPT]) # ------------------------------- # Look for Boost.Regex. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Regex], [BOOST_FIND_LIB([regex], [$1], [boost/regex.hpp], [boost::regex exp("*"); boost::regex_match("foo", exp);]) ])# BOOST_REGEX # BOOST_SERIALIZATION([PREFERRED-RT-OPT]) # --------------------------------------- # Look for Boost.Serialization. For the documentation of PREFERRED-RT-OPT, see # the documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Serialization], [BOOST_FIND_LIB([serialization], [$1], [boost/archive/text_oarchive.hpp], [std::ostream* o = 0; // Cheap way to get an ostream... boost::archive::text_oarchive t(*o);]) ])# BOOST_SERIALIZATION # BOOST_SIGNALS([PREFERRED-RT-OPT]) # --------------------------------- # Look for Boost.Signals. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Signals], [BOOST_FIND_LIB([signals], [$1], [boost/signal.hpp], [boost::signal s;]) ])# BOOST_SIGNALS # BOOST_SIGNALS2() # ---------------- # Look for Boost.Signals2 (new since 1.39.0). BOOST_DEFUN([Signals2], [BOOST_FIND_HEADER([boost/signals2.hpp]) ])# BOOST_SIGNALS2 # BOOST_SMART_PTR() # ----------------- # Look for Boost.SmartPtr BOOST_DEFUN([Smart_Ptr], [BOOST_FIND_HEADER([boost/scoped_ptr.hpp]) BOOST_FIND_HEADER([boost/shared_ptr.hpp]) ]) # BOOST_STATICASSERT() # -------------------- # Look for Boost.StaticAssert BOOST_DEFUN([StaticAssert], [BOOST_FIND_HEADER([boost/static_assert.hpp])]) # BOOST_STRING_ALGO() # ------------------- # Look for Boost.StringAlgo BOOST_DEFUN([String_Algo], [BOOST_FIND_HEADER([boost/algorithm/string.hpp]) ]) # BOOST_SYSTEM([PREFERRED-RT-OPT]) # -------------------------------- # Look for Boost.System. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. This library was introduced in Boost # 1.35.0. BOOST_DEFUN([System], [BOOST_FIND_LIB([system], [$1], [boost/system/error_code.hpp], [boost::system::error_code e; e.clear();]) ])# BOOST_SYSTEM # BOOST_TEST([PREFERRED-RT-OPT]) # ------------------------------ # Look for Boost.Test. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Test], [m4_pattern_allow([^BOOST_CHECK$])dnl BOOST_FIND_LIB([unit_test_framework], [$1], [boost/test/unit_test.hpp], [BOOST_CHECK(2 == 2);], [using boost::unit_test::test_suite; test_suite* init_unit_test_suite(int argc, char ** argv) { return NULL; }]) ])# BOOST_TEST # BOOST_THREAD([PREFERRED-RT-OPT]) # --------------------------------- # Look for Boost.Thread. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Thread], [dnl Having the pthread flag is required at least on GCC3 where dnl boost/thread.hpp would complain if we try to compile without dnl -pthread on GNU/Linux. AC_REQUIRE([_BOOST_PTHREAD_FLAG])dnl boost_thread_save_LIBS=$LIBS boost_thread_save_LDFLAGS=$LDFLAGS boost_thread_save_CPPFLAGS=$CPPFLAGS # Link-time dependency from thread to system was added as of 1.49.0. if test $boost_major_version -ge 149; then BOOST_SYSTEM([$1]) fi # end of the Boost.System check. m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" CPPFLAGS="$CPPFLAGS $boost_cv_pthread_flag" # When compiling for the Windows platform, the threads library is named # differently. This suffix doesn't exist in new versions of Boost, or # possibly new versions of GCC on mingw I am assuming it's Boost's change for # now and I am setting version to 1.48, for lack of knowledge as to when this # change occurred. if test $boost_major_version -lt 148; then case $host_os in (*mingw*) boost_thread_lib_ext=_win32;; esac fi BOOST_FIND_LIBS([thread], [thread$boost_thread_lib_ext], [$1], [boost/thread.hpp], [boost::thread t; boost::mutex m;]) case $host_os in (*mingw*) boost_thread_w32_socket_link=-lws2_32;; esac BOOST_THREAD_LIBS="$BOOST_THREAD_LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag $boost_thread_w32_socket_link" BOOST_THREAD_LDFLAGS="$BOOST_SYSTEM_LDFLAGS" BOOST_CPPFLAGS="$BOOST_CPPFLAGS $boost_cv_pthread_flag" LIBS=$boost_thread_save_LIBS LDFLAGS=$boost_thread_save_LDFLAGS CPPFLAGS=$boost_thread_save_CPPFLAGS ])# BOOST_THREAD AU_ALIAS([BOOST_THREADS], [BOOST_THREAD]) # BOOST_TOKENIZER() # ----------------- # Look for Boost.Tokenizer BOOST_DEFUN([Tokenizer], [BOOST_FIND_HEADER([boost/tokenizer.hpp])]) # BOOST_TRIBOOL() # --------------- # Look for Boost.Tribool BOOST_DEFUN([Tribool], [BOOST_FIND_HEADER([boost/logic/tribool_fwd.hpp]) BOOST_FIND_HEADER([boost/logic/tribool.hpp]) ]) # BOOST_TUPLE() # ------------- # Look for Boost.Tuple BOOST_DEFUN([Tuple], [BOOST_FIND_HEADER([boost/tuple/tuple.hpp])]) # BOOST_TYPETRAITS() # -------------------- # Look for Boost.TypeTraits BOOST_DEFUN([TypeTraits], [BOOST_FIND_HEADER([boost/type_traits.hpp])]) # BOOST_UTILITY() # --------------- # Look for Boost.Utility (noncopyable, result_of, base-from-member idiom, # etc.) BOOST_DEFUN([Utility], [BOOST_FIND_HEADER([boost/utility.hpp])]) # BOOST_VARIANT() # --------------- # Look for Boost.Variant. BOOST_DEFUN([Variant], [BOOST_FIND_HEADER([boost/variant/variant_fwd.hpp]) BOOST_FIND_HEADER([boost/variant.hpp])]) # BOOST_POINTER_CONTAINER() # ------------------------ # Look for Boost.PointerContainer BOOST_DEFUN([Pointer_Container], [BOOST_FIND_HEADER([boost/ptr_container/ptr_deque.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_list.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_vector.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_array.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_set.hpp]) BOOST_FIND_HEADER([boost/ptr_container/ptr_map.hpp]) ])# BOOST_POINTER_CONTAINER # BOOST_WAVE([PREFERRED-RT-OPT]) # ------------------------------ # NOTE: If you intend to use Wave/Spirit with thread support, make sure you # call BOOST_THREAD first. # Look for Boost.Wave. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Wave], [AC_REQUIRE([BOOST_FILESYSTEM])dnl AC_REQUIRE([BOOST_DATE_TIME])dnl boost_wave_save_LIBS=$LIBS boost_wave_save_LDFLAGS=$LDFLAGS m4_pattern_allow([^BOOST_((FILE)?SYSTEM|DATE_TIME|THREAD)_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_DATE_TIME_LIBS \ $BOOST_THREAD_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS \ $BOOST_DATE_TIME_LDFLAGS $BOOST_THREAD_LDFLAGS" BOOST_FIND_LIB([wave], [$1], [boost/wave.hpp], [boost::wave::token_id id; get_token_name(id);]) LIBS=$boost_wave_save_LIBS LDFLAGS=$boost_wave_save_LDFLAGS ])# BOOST_WAVE # BOOST_XPRESSIVE() # ----------------- # Look for Boost.Xpressive (new since 1.36.0). BOOST_DEFUN([Xpressive], [BOOST_FIND_HEADER([boost/xpressive/xpressive.hpp])]) # ----------------- # # Internal helpers. # # ----------------- # # _BOOST_PTHREAD_FLAG() # --------------------- # Internal helper for BOOST_THREAD. Computes boost_cv_pthread_flag # which must be used in CPPFLAGS and LIBS. # # Yes, we *need* to put the -pthread thing in CPPFLAGS because with GCC3, # boost/thread.hpp will trigger a #error if -pthread isn't used: # boost/config/requires_threads.hpp:47:5: #error "Compiler threading support # is not turned on. Please set the correct command line options for # threading: -pthread (Linux), -pthreads (Solaris) or -mthreads (Mingw32)" # # Based on ACX_PTHREAD: http://autoconf-archive.cryp.to/acx_pthread.html AC_DEFUN([_BOOST_PTHREAD_FLAG], [AC_REQUIRE([AC_PROG_CXX])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_LANG_PUSH([C++])dnl AC_CACHE_CHECK([for the flags needed to use pthreads], [boost_cv_pthread_flag], [ boost_cv_pthread_flag= # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # (none): in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -lpthreads: AIX (must check this before -lpthread) # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # -llthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: GNU Linux/GCC (kernel threads), BSD/GCC (userland threads) # -pthreads: Solaris/GCC # -mthreads: MinGW32/GCC, Lynx/GCC # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # -lpthread: GNU Linux, etc. # --thread-safe: KAI C++ case $host_os in #( *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: boost_pthread_flags="-pthreads -lpthread -mt -pthread";; #( *) boost_pthread_flags="-lpthreads -Kthread -kthread -llthread -pthread \ -pthreads -mthreads -lpthread --thread-safe -mt";; esac # Generate the test file. AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0);])]) for boost_pthread_flag in '' $boost_pthread_flags; do boost_pthread_ok=false dnl Re-use the test file already generated. boost_pthreads__save_LIBS=$LIBS LIBS="$LIBS $boost_pthread_flag" AC_LINK_IFELSE([], [if grep ".*$boost_pthread_flag" conftest.err; then echo "This flag seems to have triggered warnings" >&AS_MESSAGE_LOG_FD else boost_pthread_ok=:; boost_cv_pthread_flag=$boost_pthread_flag fi]) LIBS=$boost_pthreads__save_LIBS $boost_pthread_ok && break done ]) AC_LANG_POP([C++])dnl ])# _BOOST_PTHREAD_FLAG # _BOOST_gcc_test(MAJOR, MINOR) # ----------------------------- # Internal helper for _BOOST_FIND_COMPILER_TAG. m4_define([_BOOST_gcc_test], ["defined __GNUC__ && __GNUC__ == $1 && __GNUC_MINOR__ == $2 && !defined __ICC @ gcc$1$2"])dnl # _BOOST_mingw_test(MAJOR, MINOR) # ----------------------------- # Internal helper for _BOOST_FIND_COMPILER_TAG. m4_define([_BOOST_mingw_test], ["defined __GNUC__ && __GNUC__ == $1 && __GNUC_MINOR__ == $2 && !defined __ICC && \ (defined WIN32 || defined WINNT || defined _WIN32 || defined __WIN32 \ || defined __WIN32__ || defined __WINNT || defined __WINNT__) @ mgw$1$2"])dnl # _BOOST_FIND_COMPILER_TAG() # -------------------------- # Internal. When Boost is installed without --layout=system, each library # filename will hold a suffix that encodes the compiler used during the # build. The Boost build system seems to call this a `tag'. AC_DEFUN([_BOOST_FIND_COMPILER_TAG], [AC_REQUIRE([AC_PROG_CXX])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_CACHE_CHECK([for the toolset name used by Boost for $CXX], [boost_cv_lib_tag], [boost_cv_lib_tag=unknown if test x$boost_cv_inc_path != xno; then AC_LANG_PUSH([C++])dnl # The following tests are mostly inspired by boost/config/auto_link.hpp # The list is sorted to most recent/common to oldest compiler (in order # to increase the likelihood of finding the right compiler with the # least number of compilation attempt). # Beware that some tests are sensible to the order (for instance, we must # look for MinGW before looking for GCC3). # I used one compilation test per compiler with a #error to recognize # each compiler so that it works even when cross-compiling (let me know # if you know a better approach). # Known missing tags (known from Boost's tools/build/v2/tools/common.jam): # como, edg, kcc, bck, mp, sw, tru, xlc # I'm not sure about my test for `il' (be careful: Intel's ICC pre-defines # the same defines as GCC's). for i in \ _BOOST_mingw_test(6, 2) \ _BOOST_gcc_test(6, 2) \ _BOOST_mingw_test(6, 1) \ _BOOST_gcc_test(6, 1) \ _BOOST_mingw_test(6, 0) \ _BOOST_gcc_test(6, 0) \ _BOOST_mingw_test(5, 3) \ _BOOST_gcc_test(5, 3) \ _BOOST_mingw_test(5, 2) \ _BOOST_gcc_test(5, 2) \ _BOOST_mingw_test(5, 1) \ _BOOST_gcc_test(5, 1) \ _BOOST_mingw_test(5, 0) \ _BOOST_gcc_test(5, 0) \ _BOOST_mingw_test(4, 10) \ _BOOST_gcc_test(4, 10) \ _BOOST_mingw_test(4, 9) \ _BOOST_gcc_test(4, 9) \ _BOOST_mingw_test(4, 8) \ _BOOST_gcc_test(4, 8) \ _BOOST_mingw_test(4, 7) \ _BOOST_gcc_test(4, 7) \ _BOOST_mingw_test(4, 6) \ _BOOST_gcc_test(4, 6) \ _BOOST_mingw_test(4, 5) \ _BOOST_gcc_test(4, 5) \ _BOOST_mingw_test(4, 4) \ _BOOST_gcc_test(4, 4) \ _BOOST_mingw_test(4, 3) \ _BOOST_gcc_test(4, 3) \ _BOOST_mingw_test(4, 2) \ _BOOST_gcc_test(4, 2) \ _BOOST_mingw_test(4, 1) \ _BOOST_gcc_test(4, 1) \ _BOOST_mingw_test(4, 0) \ _BOOST_gcc_test(4, 0) \ "defined __GNUC__ && __GNUC__ == 3 && !defined __ICC \ && (defined WIN32 || defined WINNT || defined _WIN32 || defined __WIN32 \ || defined __WIN32__ || defined __WINNT || defined __WINNT__) @ mgw" \ _BOOST_gcc_test(3, 4) \ _BOOST_gcc_test(3, 3) \ "defined _MSC_VER && _MSC_VER >= 1500 @ vc90" \ "defined _MSC_VER && _MSC_VER == 1400 @ vc80" \ _BOOST_gcc_test(3, 2) \ "defined _MSC_VER && _MSC_VER == 1310 @ vc71" \ _BOOST_gcc_test(3, 1) \ _BOOST_gcc_test(3, 0) \ "defined __BORLANDC__ @ bcb" \ "defined __ICC && (defined __unix || defined __unix__) @ il" \ "defined __ICL @ iw" \ "defined _MSC_VER && _MSC_VER == 1300 @ vc7" \ _BOOST_gcc_test(2, 95) \ "defined __MWERKS__ && __MWERKS__ <= 0x32FF @ cw9" \ "defined _MSC_VER && _MSC_VER < 1300 && !defined UNDER_CE @ vc6" \ "defined _MSC_VER && _MSC_VER < 1300 && defined UNDER_CE @ evc4" \ "defined __MWERKS__ && __MWERKS__ <= 0x31FF @ cw8" do boost_tag_test=`expr "X$i" : 'X\([[^@]]*\) @ '` boost_tag=`expr "X$i" : 'X[[^@]]* @ \(.*\)'` AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if $boost_tag_test /* OK */ #else # error $boost_tag_test #endif ]])], [boost_cv_lib_tag=$boost_tag; break], []) done AC_LANG_POP([C++])dnl case $boost_cv_lib_tag in #( # Some newer (>= 1.35?) versions of Boost seem to only use "gcc" as opposed # to "gcc41" for instance. *-gcc | *'-gcc ') :;; #( Don't re-add -gcc: it's already in there. gcc*) boost_tag_x= case $host_os in #( darwin*) if test $boost_major_version -ge 136; then # The `x' added in r46793 of Boost. boost_tag_x=x fi;; esac # We can specify multiple tags in this variable because it's used by # BOOST_FIND_LIB that does a `for tag in -$boost_cv_lib_tag' ... boost_cv_lib_tag="$boost_tag_x$boost_cv_lib_tag -${boost_tag_x}gcc" ;; #( unknown) AC_MSG_WARN([[could not figure out which toolset name to use for $CXX]]) boost_cv_lib_tag= ;; esac fi])dnl end of AC_CACHE_CHECK ])# _BOOST_FIND_COMPILER_TAG # _BOOST_GUESS_WHETHER_TO_USE_MT() # -------------------------------- # Compile a small test to try to guess whether we should favor MT (Multi # Thread) flavors of Boost. Sets boost_guess_use_mt accordingly. AC_DEFUN([_BOOST_GUESS_WHETHER_TO_USE_MT], [# Check whether we do better use `mt' even though we weren't ask to. AC_LANG_PUSH([C++])dnl AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if defined _REENTRANT || defined _MT || defined __MT__ /* use -mt */ #else # error MT not needed #endif ]])], [boost_guess_use_mt=:], [boost_guess_use_mt=false]) AC_LANG_POP([C++])dnl ]) # _BOOST_AC_LINK_IFELSE(PROGRAM, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # ------------------------------------------------------------------- # Fork of _AC_LINK_IFELSE that preserves conftest.o across calls. Fragile, # will break when Autoconf changes its internals. Requires that you manually # rm -f conftest.$ac_objext in between to really different tests, otherwise # you will try to link a conftest.o left behind by a previous test. # Used to aggressively optimize BOOST_FIND_LIB (see the big comment in this # macro). # # Don't use "break" in the actions, as it would short-circuit some code # this macro runs after the actions. m4_define([_BOOST_AC_LINK_IFELSE], [m4_ifvaln([$1], [AC_LANG_CONFTEST([$1])])dnl rm -f conftest$ac_exeext boost_save_ac_ext=$ac_ext boost_use_source=: # If we already have a .o, re-use it. We change $ac_ext so that $ac_link # tries to link the existing object file instead of compiling from source. test -f conftest.$ac_objext && ac_ext=$ac_objext && boost_use_source=false && _AS_ECHO_LOG([re-using the existing conftest.$ac_objext]) AS_IF([_AC_DO_STDERR($ac_link) && { test -z "$ac_[]_AC_LANG_ABBREV[]_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_executable_p conftest$ac_exeext dnl FIXME: use AS_TEST_X instead when 2.61 is widespread enough. }], [$2], [if $boost_use_source; then _AC_MSG_LOG_CONFTEST fi $3]) ac_objext=$boost_save_ac_objext ac_ext=$boost_save_ac_ext dnl Delete also the IPA/IPO (Inter Procedural Analysis/Optimization) dnl information created by the PGI compiler (conftest_ipa8_conftest.oo), dnl as it would interfere with the next link command. rm -f core conftest.err conftest_ipa8_conftest.oo \ conftest$ac_exeext m4_ifval([$1], [conftest.$ac_ext])[]dnl ])# _BOOST_AC_LINK_IFELSE # Local Variables: # mode: autoconf # End: DarkRadiant-2.5.0/m4/gettext.m4000066400000000000000000000367631321750546400161760ustar00rootroot00000000000000# gettext.m4 serial 68 (gettext-0.19.8) dnl Copyright (C) 1995-2014, 2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl This file can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1995-2000. dnl Bruno Haible , 2000-2006, 2008-2010. dnl Macro to add for using GNU gettext. dnl Usage: AM_GNU_GETTEXT([INTLSYMBOL], [NEEDSYMBOL], [INTLDIR]). dnl INTLSYMBOL can be one of 'external', 'no-libtool', 'use-libtool'. The dnl default (if it is not specified or empty) is 'no-libtool'. dnl INTLSYMBOL should be 'external' for packages with no intl directory, dnl and 'no-libtool' or 'use-libtool' for packages with an intl directory. dnl If INTLSYMBOL is 'use-libtool', then a libtool library dnl $(top_builddir)/intl/libintl.la will be created (shared and/or static, dnl depending on --{enable,disable}-{shared,static} and on the presence of dnl AM-DISABLE-SHARED). If INTLSYMBOL is 'no-libtool', a static library dnl $(top_builddir)/intl/libintl.a will be created. dnl If NEEDSYMBOL is specified and is 'need-ngettext', then GNU gettext dnl implementations (in libc or libintl) without the ngettext() function dnl will be ignored. If NEEDSYMBOL is specified and is dnl 'need-formatstring-macros', then GNU gettext implementations that don't dnl support the ISO C 99 formatstring macros will be ignored. dnl INTLDIR is used to find the intl libraries. If empty, dnl the value '$(top_builddir)/intl/' is used. dnl dnl The result of the configuration is one of three cases: dnl 1) GNU gettext, as included in the intl subdirectory, will be compiled dnl and used. dnl Catalog format: GNU --> install in $(datadir) dnl Catalog extension: .mo after installation, .gmo in source tree dnl 2) GNU gettext has been found in the system's C library. dnl Catalog format: GNU --> install in $(datadir) dnl Catalog extension: .mo after installation, .gmo in source tree dnl 3) No internationalization, always use English msgid. dnl Catalog format: none dnl Catalog extension: none dnl If INTLSYMBOL is 'external', only cases 2 and 3 can occur. dnl The use of .gmo is historical (it was needed to avoid overwriting the dnl GNU format catalogs when building on a platform with an X/Open gettext), dnl but we keep it in order not to force irrelevant filename changes on the dnl maintainers. dnl AC_DEFUN([AM_GNU_GETTEXT], [ dnl Argument checking. ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , [ifelse([$1], [use-libtool], , [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT ])])])])]) ifelse(ifelse([$1], [], [old])[]ifelse([$1], [no-libtool], [old]), [old], [AC_DIAGNOSE([obsolete], [Use of AM_GNU_GETTEXT without [external] argument is deprecated.])]) ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], [need-formatstring-macros], , [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT ])])])]) define([gt_included_intl], ifelse([$1], [external], ifdef([AM_GNU_GETTEXT_][INTL_SUBDIR], [yes], [no]), [yes])) define([gt_libtool_suffix_prefix], ifelse([$1], [use-libtool], [l], [])) gt_NEEDS_INIT AM_GNU_GETTEXT_NEED([$2]) AC_REQUIRE([AM_PO_SUBDIRS])dnl ifelse(gt_included_intl, yes, [ AC_REQUIRE([AM_INTL_SUBDIR])dnl ]) dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) dnl Sometimes libintl requires libiconv, so first search for libiconv. dnl Ideally we would do this search only after the dnl if test "$USE_NLS" = "yes"; then dnl if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then dnl tests. But if configure.in invokes AM_ICONV after AM_GNU_GETTEXT dnl the configure script would need to contain the same shell code dnl again, outside any 'if'. There are two solutions: dnl - Invoke AM_ICONV_LINKFLAGS_BODY here, outside any 'if'. dnl - Control the expansions in more detail using AC_PROVIDE_IFELSE. dnl Since AC_PROVIDE_IFELSE is only in autoconf >= 2.52 and not dnl documented, we avoid it. ifelse(gt_included_intl, yes, , [ AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) ]) dnl Sometimes, on Mac OS X, libintl requires linking with CoreFoundation. gt_INTL_MACOSX dnl Set USE_NLS. AC_REQUIRE([AM_NLS]) ifelse(gt_included_intl, yes, [ BUILD_INCLUDED_LIBINTL=no USE_INCLUDED_LIBINTL=no ]) LIBINTL= LTLIBINTL= POSUB= dnl Add a version number to the cache macros. case " $gt_needs " in *" need-formatstring-macros "*) gt_api_version=3 ;; *" need-ngettext "*) gt_api_version=2 ;; *) gt_api_version=1 ;; esac gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc" gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl" dnl If we use NLS figure out what method if test "$USE_NLS" = "yes"; then gt_use_preinstalled_gnugettext=no ifelse(gt_included_intl, yes, [ AC_MSG_CHECKING([whether included gettext is requested]) AC_ARG_WITH([included-gettext], [ --with-included-gettext use the GNU gettext library included here], nls_cv_force_use_gnu_gettext=$withval, nls_cv_force_use_gnu_gettext=no) AC_MSG_RESULT([$nls_cv_force_use_gnu_gettext]) nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext" if test "$nls_cv_force_use_gnu_gettext" != "yes"; then ]) dnl User does not insist on using GNU NLS library. Figure out what dnl to use. If GNU gettext is available we use this. Else we have dnl to fall back to GNU NLS library. if test $gt_api_version -ge 3; then gt_revision_test_code=' #ifndef __GNU_GETTEXT_SUPPORTED_REVISION #define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) #endif changequote(,)dnl typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; changequote([,])dnl ' else gt_revision_test_code= fi if test $gt_api_version -ge 2; then gt_expression_test_code=' + * ngettext ("", "", 0)' else gt_expression_test_code= fi AC_CACHE_CHECK([for GNU gettext in libc], [$gt_func_gnugettext_libc], [AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[ #include #ifndef __GNU_GETTEXT_SUPPORTED_REVISION extern int _nl_msg_cat_cntr; extern int *_nl_domain_bindings; #define __GNU_GETTEXT_SYMBOL_EXPRESSION (_nl_msg_cat_cntr + *_nl_domain_bindings) #else #define __GNU_GETTEXT_SYMBOL_EXPRESSION 0 #endif $gt_revision_test_code ]], [[ bindtextdomain ("", ""); return * gettext ("")$gt_expression_test_code + __GNU_GETTEXT_SYMBOL_EXPRESSION ]])], [eval "$gt_func_gnugettext_libc=yes"], [eval "$gt_func_gnugettext_libc=no"])]) if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then dnl Sometimes libintl requires libiconv, so first search for libiconv. ifelse(gt_included_intl, yes, , [ AM_ICONV_LINK ]) dnl Search for libintl and define LIBINTL, LTLIBINTL and INCINTL dnl accordingly. Don't use AC_LIB_LINKFLAGS_BODY([intl],[iconv]) dnl because that would add "-liconv" to LIBINTL and LTLIBINTL dnl even if libiconv doesn't exist. AC_LIB_LINKFLAGS_BODY([intl]) AC_CACHE_CHECK([for GNU gettext in libintl], [$gt_func_gnugettext_libintl], [gt_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $INCINTL" gt_save_LIBS="$LIBS" LIBS="$LIBS $LIBINTL" dnl Now see whether libintl exists and does not depend on libiconv. AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[ #include #ifndef __GNU_GETTEXT_SUPPORTED_REVISION extern int _nl_msg_cat_cntr; extern #ifdef __cplusplus "C" #endif const char *_nl_expand_alias (const char *); #define __GNU_GETTEXT_SYMBOL_EXPRESSION (_nl_msg_cat_cntr + *_nl_expand_alias ("")) #else #define __GNU_GETTEXT_SYMBOL_EXPRESSION 0 #endif $gt_revision_test_code ]], [[ bindtextdomain ("", ""); return * gettext ("")$gt_expression_test_code + __GNU_GETTEXT_SYMBOL_EXPRESSION ]])], [eval "$gt_func_gnugettext_libintl=yes"], [eval "$gt_func_gnugettext_libintl=no"]) dnl Now see whether libintl exists and depends on libiconv. if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then LIBS="$LIBS $LIBICONV" AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[ #include #ifndef __GNU_GETTEXT_SUPPORTED_REVISION extern int _nl_msg_cat_cntr; extern #ifdef __cplusplus "C" #endif const char *_nl_expand_alias (const char *); #define __GNU_GETTEXT_SYMBOL_EXPRESSION (_nl_msg_cat_cntr + *_nl_expand_alias ("")) #else #define __GNU_GETTEXT_SYMBOL_EXPRESSION 0 #endif $gt_revision_test_code ]], [[ bindtextdomain ("", ""); return * gettext ("")$gt_expression_test_code + __GNU_GETTEXT_SYMBOL_EXPRESSION ]])], [LIBINTL="$LIBINTL $LIBICONV" LTLIBINTL="$LTLIBINTL $LTLIBICONV" eval "$gt_func_gnugettext_libintl=yes" ]) fi CPPFLAGS="$gt_save_CPPFLAGS" LIBS="$gt_save_LIBS"]) fi dnl If an already present or preinstalled GNU gettext() is found, dnl use it. But if this macro is used in GNU gettext, and GNU dnl gettext is already preinstalled in libintl, we update this dnl libintl. (Cf. the install rule in intl/Makefile.in.) if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \ || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \ && test "$PACKAGE" != gettext-runtime \ && test "$PACKAGE" != gettext-tools; }; then gt_use_preinstalled_gnugettext=yes else dnl Reset the values set by searching for libintl. LIBINTL= LTLIBINTL= INCINTL= fi ifelse(gt_included_intl, yes, [ if test "$gt_use_preinstalled_gnugettext" != "yes"; then dnl GNU gettext is not found in the C library. dnl Fall back on included GNU gettext library. nls_cv_use_gnu_gettext=yes fi fi if test "$nls_cv_use_gnu_gettext" = "yes"; then dnl Mark actions used to generate GNU NLS library. BUILD_INCLUDED_LIBINTL=yes USE_INCLUDED_LIBINTL=yes LIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LIBICONV $LIBTHREAD" LTLIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LTLIBICONV $LTLIBTHREAD" LIBS=`echo " $LIBS " | sed -e 's/ -lintl / /' -e 's/^ //' -e 's/ $//'` fi CATOBJEXT= if test "$gt_use_preinstalled_gnugettext" = "yes" \ || test "$nls_cv_use_gnu_gettext" = "yes"; then dnl Mark actions to use GNU gettext tools. CATOBJEXT=.gmo fi ]) if test -n "$INTL_MACOSX_LIBS"; then if test "$gt_use_preinstalled_gnugettext" = "yes" \ || test "$nls_cv_use_gnu_gettext" = "yes"; then dnl Some extra flags are needed during linking. LIBINTL="$LIBINTL $INTL_MACOSX_LIBS" LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS" fi fi if test "$gt_use_preinstalled_gnugettext" = "yes" \ || test "$nls_cv_use_gnu_gettext" = "yes"; then AC_DEFINE([ENABLE_NLS], [1], [Define to 1 if translation of program messages to the user's native language is requested.]) else USE_NLS=no fi fi AC_MSG_CHECKING([whether to use NLS]) AC_MSG_RESULT([$USE_NLS]) if test "$USE_NLS" = "yes"; then AC_MSG_CHECKING([where the gettext function comes from]) if test "$gt_use_preinstalled_gnugettext" = "yes"; then if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then gt_source="external libintl" else gt_source="libc" fi else gt_source="included intl directory" fi AC_MSG_RESULT([$gt_source]) fi if test "$USE_NLS" = "yes"; then if test "$gt_use_preinstalled_gnugettext" = "yes"; then if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then AC_MSG_CHECKING([how to link with libintl]) AC_MSG_RESULT([$LIBINTL]) AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL]) fi dnl For backward compatibility. Some packages may be using this. AC_DEFINE([HAVE_GETTEXT], [1], [Define if the GNU gettext() function is already present or preinstalled.]) AC_DEFINE([HAVE_DCGETTEXT], [1], [Define if the GNU dcgettext() function is already present or preinstalled.]) fi dnl We need to process the po/ directory. POSUB=po fi ifelse(gt_included_intl, yes, [ dnl If this is used in GNU gettext we have to set BUILD_INCLUDED_LIBINTL dnl to 'yes' because some of the testsuite requires it. if test "$PACKAGE" = gettext-runtime || test "$PACKAGE" = gettext-tools; then BUILD_INCLUDED_LIBINTL=yes fi dnl Make all variables we use known to autoconf. AC_SUBST([BUILD_INCLUDED_LIBINTL]) AC_SUBST([USE_INCLUDED_LIBINTL]) AC_SUBST([CATOBJEXT]) dnl For backward compatibility. Some configure.ins may be using this. nls_cv_header_intl= nls_cv_header_libgt= dnl For backward compatibility. Some Makefiles may be using this. DATADIRNAME=share AC_SUBST([DATADIRNAME]) dnl For backward compatibility. Some Makefiles may be using this. INSTOBJEXT=.mo AC_SUBST([INSTOBJEXT]) dnl For backward compatibility. Some Makefiles may be using this. GENCAT=gencat AC_SUBST([GENCAT]) dnl For backward compatibility. Some Makefiles may be using this. INTLOBJS= if test "$USE_INCLUDED_LIBINTL" = yes; then INTLOBJS="\$(GETTOBJS)" fi AC_SUBST([INTLOBJS]) dnl Enable libtool support if the surrounding package wishes it. INTL_LIBTOOL_SUFFIX_PREFIX=gt_libtool_suffix_prefix AC_SUBST([INTL_LIBTOOL_SUFFIX_PREFIX]) ]) dnl For backward compatibility. Some Makefiles may be using this. INTLLIBS="$LIBINTL" AC_SUBST([INTLLIBS]) dnl Make all documented variables known to autoconf. AC_SUBST([LIBINTL]) AC_SUBST([LTLIBINTL]) AC_SUBST([POSUB]) ]) dnl gt_NEEDS_INIT ensures that the gt_needs variable is initialized. m4_define([gt_NEEDS_INIT], [ m4_divert_text([DEFAULTS], [gt_needs=]) m4_define([gt_NEEDS_INIT], []) ]) dnl Usage: AM_GNU_GETTEXT_NEED([NEEDSYMBOL]) AC_DEFUN([AM_GNU_GETTEXT_NEED], [ m4_divert_text([INIT_PREPARE], [gt_needs="$gt_needs $1"]) ]) dnl Usage: AM_GNU_GETTEXT_VERSION([gettext-version]) AC_DEFUN([AM_GNU_GETTEXT_VERSION], []) dnl Usage: AM_GNU_GETTEXT_REQUIRE_VERSION([gettext-version]) AC_DEFUN([AM_GNU_GETTEXT_REQUIRE_VERSION], []) DarkRadiant-2.5.0/m4/iconv.m4000066400000000000000000000165371321750546400156250ustar00rootroot00000000000000# iconv.m4 serial 11 (gettext-0.18.1) dnl Copyright (C) 2000-2002, 2007-2010 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], [ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_LIB_LINKFLAGS_BODY([iconv]) ]) AC_DEFUN([AM_ICONV_LINK], [ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and dnl those with the standalone portable GNU libiconv installed). AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) dnl Add $INCICONV to CPPFLAGS before performing the following checks, dnl because if the user has installed libiconv and not disabled its use dnl via --without-libiconv-prefix, he wants to use it. The first dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed. am_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) AC_CACHE_CHECK([for iconv], [am_cv_func_iconv], [ am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no AC_TRY_LINK([#include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], [am_cv_func_iconv=yes]) if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $LIBICONV" AC_TRY_LINK([#include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], [am_cv_lib_iconv=yes] [am_cv_func_iconv=yes]) LIBS="$am_save_LIBS" fi ]) if test "$am_cv_func_iconv" = yes; then AC_CACHE_CHECK([for working iconv], [am_cv_func_iconv_works], [ dnl This tests against bugs in AIX 5.1, HP-UX 11.11, Solaris 10. am_save_LIBS="$LIBS" if test $am_cv_lib_iconv = yes; then LIBS="$LIBS $LIBICONV" fi AC_TRY_RUN([ #include #include int main () { /* Test against AIX 5.1 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); if (cd_utf8_to_88591 != (iconv_t)(-1)) { static const char input[] = "\342\202\254"; /* EURO SIGN */ char buf[10]; const char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_utf8_to_88591, (char **) &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) return 1; } } /* Test against Solaris 10 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); if (cd_ascii_to_88591 != (iconv_t)(-1)) { static const char input[] = "\263"; char buf[10]; const char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_ascii_to_88591, (char **) &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) return 1; } } #if 0 /* This bug could be worked around by the caller. */ /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ { iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); if (cd_88591_to_utf8 != (iconv_t)(-1)) { static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; char buf[50]; const char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_88591_to_utf8, (char **) &inptr, &inbytesleft, &outptr, &outbytesleft); if ((int)res > 0) return 1; } } #endif /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is provided. */ if (/* Try standardized names. */ iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) /* Try IRIX, OSF/1 names. */ && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) /* Try AIX names. */ && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) /* Try HP-UX names. */ && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) return 1; return 0; }], [am_cv_func_iconv_works=yes], [am_cv_func_iconv_works=no], [case "$host_os" in aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; *) am_cv_func_iconv_works="guessing yes" ;; esac]) LIBS="$am_save_LIBS" ]) case "$am_cv_func_iconv_works" in *no) am_func_iconv=no am_cv_lib_iconv=no ;; *) am_func_iconv=yes ;; esac else am_func_iconv=no am_cv_lib_iconv=no fi if test "$am_func_iconv" = yes; then AC_DEFINE([HAVE_ICONV], [1], [Define if you have the iconv() function and it works.]) fi if test "$am_cv_lib_iconv" = yes; then AC_MSG_CHECKING([how to link with libiconv]) AC_MSG_RESULT([$LIBICONV]) else dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV dnl either. CPPFLAGS="$am_save_CPPFLAGS" LIBICONV= LTLIBICONV= fi AC_SUBST([LIBICONV]) AC_SUBST([LTLIBICONV]) ]) dnl Define AM_ICONV using AC_DEFUN_ONCE for Autoconf >= 2.64, in order to dnl avoid warnings like dnl "warning: AC_REQUIRE: `AM_ICONV' was expanded before it was required". dnl This is tricky because of the way 'aclocal' is implemented: dnl - It requires defining an auxiliary macro whose name ends in AC_DEFUN. dnl Otherwise aclocal's initial scan pass would miss the macro definition. dnl - It requires a line break inside the AC_DEFUN_ONCE and AC_DEFUN expansions. dnl Otherwise aclocal would emit many "Use of uninitialized value $1" dnl warnings. m4_define([gl_iconv_AC_DEFUN], m4_version_prereq([2.64], [[AC_DEFUN_ONCE( [$1], [$2])]], [[AC_DEFUN( [$1], [$2])]])) gl_iconv_AC_DEFUN([AM_ICONV], [ AM_ICONV_LINK if test "$am_cv_func_iconv" = yes; then AC_MSG_CHECKING([for iconv declaration]) AC_CACHE_VAL([am_cv_proto_iconv], [ AC_TRY_COMPILE([ #include #include extern #ifdef __cplusplus "C" #endif #if defined(__STDC__) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif ], [], [am_cv_proto_iconv_arg1=""], [am_cv_proto_iconv_arg1="const"]) am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` AC_MSG_RESULT([ $am_cv_proto_iconv]) AC_DEFINE_UNQUOTED([ICONV_CONST], [$am_cv_proto_iconv_arg1], [Define as const if the declaration of iconv() needs const.]) fi ]) DarkRadiant-2.5.0/m4/lib-ld.m4000066400000000000000000000066031321750546400156430ustar00rootroot00000000000000# lib-ld.m4 serial 4 (gettext-0.18) dnl Copyright (C) 1996-2003, 2009-2010 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Subroutines of libtool.m4, dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision dnl with libtool.m4. dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no. AC_DEFUN([AC_LIB_PROG_LD_GNU], [AC_CACHE_CHECK([if the linker ($LD) is GNU ld], [acl_cv_prog_gnu_ld], [# I'd rather use --version here, but apparently some GNU ld's only accept -v. case `$LD -v 2>&1 conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by GCC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]* | [A-Za-z]:[\\/]*)] [re_direlt='/[^/][^/]*/\.\./'] # Canonicalize the path of ld ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL([acl_cv_path_LD], [if test -z "$LD"; then IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" for ac_dir in $PATH; do test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then acl_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some GNU ld's only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in *GNU* | *'with BFD'*) test "$with_gnu_ld" != no && break ;; *) test "$with_gnu_ld" != yes && break ;; esac fi done IFS="$ac_save_ifs" else acl_cv_path_LD="$LD" # Let the user override the test with a path. fi]) LD="$acl_cv_path_LD" if test -n "$LD"; then AC_MSG_RESULT([$LD]) else AC_MSG_RESULT([no]) fi test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) AC_LIB_PROG_LD_GNU ]) DarkRadiant-2.5.0/m4/lib-link.m4000066400000000000000000001002021321750546400161670ustar00rootroot00000000000000# lib-link.m4 serial 21 (gettext-0.18) dnl Copyright (C) 2001-2010 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. AC_PREREQ([2.54]) dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and dnl augments the CPPFLAGS variable. dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) pushdef([Name],[translit([$1],[./-], [___])]) pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ AC_LIB_LINKFLAGS_BODY([$1], [$2]) ac_cv_lib[]Name[]_libs="$LIB[]NAME" ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" ac_cv_lib[]Name[]_cppflags="$INC[]NAME" ac_cv_lib[]Name[]_prefix="$LIB[]NAME[]_PREFIX" ]) LIB[]NAME="$ac_cv_lib[]Name[]_libs" LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" INC[]NAME="$ac_cv_lib[]Name[]_cppflags" LIB[]NAME[]_PREFIX="$ac_cv_lib[]Name[]_prefix" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) AC_SUBST([LIB]NAME[_PREFIX]) dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the dnl results of this search when this library appears as a dependency. HAVE_LIB[]NAME=yes popdef([NAME]) popdef([Name]) ]) dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode, [missing-message]) dnl searches for libname and the libraries corresponding to explicit and dnl implicit dependencies, together with the specified include files and dnl the ability to compile and link the specified testcode. The missing-message dnl defaults to 'no' and may contain additional hints for the user. dnl If found, it sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} dnl and LTLIB${NAME} variables and augments the CPPFLAGS variable, and dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) pushdef([Name],[translit([$1],[./-], [___])]) pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME dnl accordingly. AC_LIB_LINKFLAGS_BODY([$1], [$2]) dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, dnl because if the user has installed lib[]Name and not disabled its use dnl via --without-lib[]Name-prefix, he wants to use it. ac_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ ac_save_LIBS="$LIBS" dnl If $LIB[]NAME contains some -l options, add it to the end of LIBS, dnl because these -l options might require -L options that are present in dnl LIBS. -l options benefit only from the -L options listed before it. dnl Otherwise, add it to the front of LIBS, because it may be a static dnl library that depends on another static library that is present in LIBS. dnl Static libraries benefit only from the static libraries listed after dnl it. case " $LIB[]NAME" in *" -l"*) LIBS="$LIBS $LIB[]NAME" ;; *) LIBS="$LIB[]NAME $LIBS" ;; esac AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name='m4_if([$5], [], [no], [[$5]])']) LIBS="$ac_save_LIBS" ]) if test "$ac_cv_lib[]Name" = yes; then HAVE_LIB[]NAME=yes AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the lib][$1 library.]) AC_MSG_CHECKING([how to link with lib[]$1]) AC_MSG_RESULT([$LIB[]NAME]) else HAVE_LIB[]NAME=no dnl If $LIB[]NAME didn't lead to a usable library, we don't need dnl $INC[]NAME either. CPPFLAGS="$ac_save_CPPFLAGS" LIB[]NAME= LTLIB[]NAME= LIB[]NAME[]_PREFIX= fi AC_SUBST([HAVE_LIB]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) AC_SUBST([LIB]NAME[_PREFIX]) popdef([NAME]) popdef([Name]) ]) dnl Determine the platform dependent parameters needed to use rpath: dnl acl_libext, dnl acl_shlibext, dnl acl_hardcode_libdir_flag_spec, dnl acl_hardcode_libdir_separator, dnl acl_hardcode_direct, dnl acl_hardcode_minus_L. AC_DEFUN([AC_LIB_RPATH], [ dnl Tell automake >= 1.10 to complain if config.rpath is missing. m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir AC_CACHE_CHECK([for shared library run path origin], [acl_cv_rpath], [ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh . ./conftest.sh rm -f ./conftest.sh acl_cv_rpath=done ]) wl="$acl_cv_wl" acl_libext="$acl_cv_libext" acl_shlibext="$acl_cv_shlibext" acl_libname_spec="$acl_cv_libname_spec" acl_library_names_spec="$acl_cv_library_names_spec" acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" acl_hardcode_direct="$acl_cv_hardcode_direct" acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" dnl Determine whether the user wants rpath handling at all. AC_ARG_ENABLE([rpath], [ --disable-rpath do not hardcode runtime library paths], :, enable_rpath=yes) ]) dnl AC_LIB_FROMPACKAGE(name, package) dnl declares that libname comes from the given package. The configure file dnl will then not have a --with-libname-prefix option but a dnl --with-package-prefix option. Several libraries can come from the same dnl package. This declaration must occur before an AC_LIB_LINKFLAGS or similar dnl macro call that searches for libname. AC_DEFUN([AC_LIB_FROMPACKAGE], [ pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) define([acl_frompackage_]NAME, [$2]) popdef([NAME]) pushdef([PACK],[$2]) pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) define([acl_libsinpackage_]PACKUP, m4_ifdef([acl_libsinpackage_]PACKUP, [acl_libsinpackage_]PACKUP[[, ]],)[lib$1]) popdef([PACKUP]) popdef([PACK]) ]) dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_LINKFLAGS_BODY], [ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) pushdef([PACK],[m4_ifdef([acl_frompackage_]NAME, [acl_frompackage_]NAME, lib[$1])]) pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) pushdef([PACKLIBS],[m4_ifdef([acl_frompackage_]NAME, [acl_libsinpackage_]PACKUP, lib[$1])]) dnl Autoconf >= 2.61 supports dots in --with options. pushdef([P_A_C_K],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[translit(PACK,[.],[_])],PACK)]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_ARG_WITH(P_A_C_K[-prefix], [[ --with-]]P_A_C_K[[-prefix[=DIR] search for ]PACKLIBS[ in DIR/include and DIR/lib --without-]]P_A_C_K[[-prefix don't search for ]PACKLIBS[ in includedir and libdir]], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" if test "$acl_libdirstem2" != "$acl_libdirstem" \ && ! test -d "$withval/$acl_libdirstem"; then additional_libdir="$withval/$acl_libdirstem2" fi fi fi ]) dnl Search the library and its dependencies in $additional_libdir and dnl $LDFLAGS. Using breadth-first-seach. LIB[]NAME= LTLIB[]NAME= INC[]NAME= LIB[]NAME[]_PREFIX= dnl HAVE_LIB${NAME} is an indicator that LIB${NAME}, LTLIB${NAME} have been dnl computed. So it has to be reset here. HAVE_LIB[]NAME= rpathdirs= ltrpathdirs= names_already_handled= names_next_round='$1 $2' while test -n "$names_next_round"; do names_this_round="$names_next_round" names_next_round= for name in $names_this_round; do already_handled= for n in $names_already_handled; do if test "$n" = "$name"; then already_handled=yes break fi done if test -z "$already_handled"; then names_already_handled="$names_already_handled $name" dnl See if it was already located by an earlier AC_LIB_LINKFLAGS dnl or AC_LIB_HAVE_LINKFLAGS call. uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` eval value=\"\$HAVE_LIB$uppername\" if test -n "$value"; then if test "$value" = yes; then eval value=\"\$LIB$uppername\" test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" eval value=\"\$LTLIB$uppername\" test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" else dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined dnl that this library doesn't exist. So just drop it. : fi else dnl Search the library lib$name in $additional_libdir and $LDFLAGS dnl and the already constructed $LIBNAME/$LTLIBNAME. found_dir= found_la= found_so= found_a= eval libname=\"$acl_libname_spec\" # typically: libname=lib$name if test -n "$acl_shlibext"; then shrext=".$acl_shlibext" # typically: shrext=.so else shrext= fi if test $use_additional = yes; then dir="$additional_libdir" dnl The same code as in the loop below: dnl First look for a shared library. if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi dnl Then look for a static library. if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi fi if test "X$found_dir" = "X"; then for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) case "$x" in -L*) dir=`echo "X$x" | sed -e 's/^X-L//'` dnl First look for a shared library. if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi dnl Then look for a static library. if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi ;; esac if test "X$found_dir" != "X"; then break fi done fi if test "X$found_dir" != "X"; then dnl Found the library. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" if test "X$found_so" != "X"; then dnl Linking with a shared library. We attempt to hardcode its dnl directory into the executable's runpath, unless it's the dnl standard /usr/lib. if test "$enable_rpath" = no \ || test "X$found_dir" = "X/usr/$acl_libdirstem" \ || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then dnl No hardcoding is needed. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl Use an explicit option to hardcode DIR into the resulting dnl binary. dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $found_dir" fi dnl The hardcoding into $LIBNAME is system dependent. if test "$acl_hardcode_direct" = yes; then dnl Using DIR/libNAME.so during linking hardcodes DIR into the dnl resulting binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then dnl Use an explicit option to hardcode DIR into the resulting dnl binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $found_dir" fi else dnl Rely on "-L$found_dir". dnl But don't add it if it's already contained in the LDFLAGS dnl or the already constructed $LIBNAME haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" fi if test "$acl_hardcode_minus_L" != no; then dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH dnl here, because this doesn't fit in flags passed to the dnl compiler. So give up. No hardcoding. This affects only dnl very old systems. dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" fi fi fi fi else if test "X$found_a" != "X"; then dnl Linking with a static library. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" else dnl We shouldn't come here, but anyway it's good to have a dnl fallback. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" fi fi dnl Assume the include files are nearby. additional_includedir= case "$found_dir" in */$acl_libdirstem | */$acl_libdirstem/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` if test "$name" = '$1'; then LIB[]NAME[]_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; */$acl_libdirstem2 | */$acl_libdirstem2/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` if test "$name" = '$1'; then LIB[]NAME[]_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; esac if test "X$additional_includedir" != "X"; then dnl Potentially add $additional_includedir to $INCNAME. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's /usr/local/include and we are using GCC on Linux, dnl 3. if it's already present in $CPPFLAGS or the already dnl constructed $INCNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then for x in $CPPFLAGS $INC[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $INCNAME. INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" fi fi fi fi fi dnl Look for dependencies. if test -n "$found_la"; then dnl Read the .la file. It defines the variables dnl dlname, library_names, old_library, dependency_libs, current, dnl age, revision, installed, dlopen, dlpreopen, libdir. save_libdir="$libdir" case "$found_la" in */* | *\\*) . "$found_la" ;; *) . "./$found_la" ;; esac libdir="$save_libdir" dnl We use only dependency_libs. for dep in $dependency_libs; do case "$dep" in -L*) additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's /usr/local/lib and we are using GCC on Linux, dnl 3. if it's already present in $LDFLAGS or the already dnl constructed $LIBNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \ && test "X$additional_libdir" != "X/usr/$acl_libdirstem2"; then haveit= if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem" \ || test "X$additional_libdir" = "X/usr/local/$acl_libdirstem2"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LIBNAME. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" fi fi haveit= for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LTLIBNAME. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" fi fi fi fi ;; -R*) dir=`echo "X$dep" | sed -e 's/^X-R//'` if test "$enable_rpath" != no; then dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $dir" fi dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $dir" fi fi ;; -l*) dnl Handle this in the next round. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` ;; *.la) dnl Handle this in the next round. Throw away the .la's dnl directory; it is already contained in a preceding -L dnl option. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` ;; *) dnl Most likely an immediate library name. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" ;; esac done fi else dnl Didn't find the library; assume it is in the system directories dnl known to the linker and runtime loader. (All the system dnl directories known to the linker should also be known to the dnl runtime loader, otherwise the system is severely misconfigured.) LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" fi fi fi done done if test "X$rpathdirs" != "X"; then if test -n "$acl_hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user must dnl pass all path elements in one option. We can arrange that for a dnl single library, but not when more than one $LIBNAMEs are used. alldirs= for found_dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" done dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl. acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" else dnl The -rpath options are cumulative. for found_dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$found_dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" done fi fi if test "X$ltrpathdirs" != "X"; then dnl When using libtool, the option that works for both libraries and dnl executables is -R. The -R options are cumulative. for found_dir in $ltrpathdirs; do LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" done fi popdef([P_A_C_K]) popdef([PACKLIBS]) popdef([PACKUP]) popdef([PACK]) popdef([NAME]) ]) dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, dnl unless already present in VAR. dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes dnl contains two or three consecutive elements that belong together. AC_DEFUN([AC_LIB_APPENDTOVAR], [ for element in [$2]; do haveit= for x in $[$1]; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X$element"; then haveit=yes break fi done if test -z "$haveit"; then [$1]="${[$1]}${[$1]:+ }$element" fi done ]) dnl For those cases where a variable contains several -L and -l options dnl referring to unknown libraries and directories, this macro determines the dnl necessary additional linker options for the runtime path. dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL]) dnl sets LDADDVAR to linker options needed together with LIBSVALUE. dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed, dnl otherwise linking without libtool is assumed. AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], [ AC_REQUIRE([AC_LIB_RPATH]) AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) $1= if test "$enable_rpath" != no; then if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then dnl Use an explicit option to hardcode directories into the resulting dnl binary. rpathdirs= next= for opt in $2; do if test -n "$next"; then dir="$next" dnl No need to hardcode the standard /usr/lib. if test "X$dir" != "X/usr/$acl_libdirstem" \ && test "X$dir" != "X/usr/$acl_libdirstem2"; then rpathdirs="$rpathdirs $dir" fi next= else case $opt in -L) next=yes ;; -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'` dnl No need to hardcode the standard /usr/lib. if test "X$dir" != "X/usr/$acl_libdirstem" \ && test "X$dir" != "X/usr/$acl_libdirstem2"; then rpathdirs="$rpathdirs $dir" fi next= ;; *) next= ;; esac fi done if test "X$rpathdirs" != "X"; then if test -n ""$3""; then dnl libtool is used for linking. Use -R options. for dir in $rpathdirs; do $1="${$1}${$1:+ }-R$dir" done else dnl The linker is used for linking directly. if test -n "$acl_hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user dnl must pass all path elements in one option. alldirs= for dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir" done acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" $1="$flag" else dnl The -rpath options are cumulative. for dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" $1="${$1}${$1:+ }$flag" done fi fi fi fi fi AC_SUBST([$1]) ]) DarkRadiant-2.5.0/m4/lib-prefix.m4000066400000000000000000000204221321750546400165340ustar00rootroot00000000000000# lib-prefix.m4 serial 7 (gettext-0.18) dnl Copyright (C) 2001-2005, 2008-2010 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't dnl require excessive bracketing. ifdef([AC_HELP_STRING], [AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], [AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed dnl to access previously installed libraries. The basic assumption is that dnl a user will want packages to use other packages he previously installed dnl with the same --prefix option. dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate dnl libraries, but is otherwise very convenient. AC_DEFUN([AC_LIB_PREFIX], [ AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_LIB_ARG_WITH([lib-prefix], [ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib --without-lib-prefix don't search for libraries in includedir and libdir], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" fi fi ]) if test $use_additional = yes; then dnl Potentially add $additional_includedir to $CPPFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's already present in $CPPFLAGS, dnl 3. if it's /usr/local/include and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= for x in $CPPFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $CPPFLAGS. CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" fi fi fi fi dnl Potentially add $additional_libdir to $LDFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's already present in $LDFLAGS, dnl 3. if it's /usr/local/lib and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then haveit= for x in $LDFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LDFLAGS. LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" fi fi fi fi fi ]) dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, dnl acl_final_exec_prefix, containing the values to which $prefix and dnl $exec_prefix will expand at the end of the configure script. AC_DEFUN([AC_LIB_PREPARE_PREFIX], [ dnl Unfortunately, prefix and exec_prefix get only finally determined dnl at the end of configure. if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" ]) dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the dnl variables prefix and exec_prefix bound to the values they will have dnl at the end of the configure script. AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], [ acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" $1 exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" ]) dnl AC_LIB_PREPARE_MULTILIB creates dnl - a variable acl_libdirstem, containing the basename of the libdir, either dnl "lib" or "lib64" or "lib/64", dnl - a variable acl_libdirstem2, as a secondary possible value for dnl acl_libdirstem, either the same as acl_libdirstem or "lib/sparcv9" or dnl "lib/amd64". AC_DEFUN([AC_LIB_PREPARE_MULTILIB], [ dnl There is no formal standard regarding lib and lib64. dnl On glibc systems, the current practice is that on a system supporting dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under dnl $prefix/lib64 and 32-bit libraries go under $prefix/lib. We determine dnl the compiler's default mode by looking at the compiler's library search dnl path. If at least one of its elements ends in /lib64 or points to a dnl directory whose absolute pathname ends in /lib64, we assume a 64-bit ABI. dnl Otherwise we use the default, namely "lib". dnl On Solaris systems, the current practice is that on a system supporting dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under dnl $prefix/lib/64 (which is a symlink to either $prefix/lib/sparcv9 or dnl $prefix/lib/amd64) and 32-bit libraries go under $prefix/lib. AC_REQUIRE([AC_CANONICAL_HOST]) acl_libdirstem=lib acl_libdirstem2= case "$host_os" in solaris*) dnl See Solaris 10 Software Developer Collection > Solaris 64-bit Developer's Guide > The Development Environment dnl . dnl "Portable Makefiles should refer to any library directories using the 64 symbolic link." dnl But we want to recognize the sparcv9 or amd64 subdirectory also if the dnl symlink is missing, so we set acl_libdirstem2 too. AC_CACHE_CHECK([for 64-bit host], [gl_cv_solaris_64bit], [AC_EGREP_CPP([sixtyfour bits], [ #ifdef _LP64 sixtyfour bits #endif ], [gl_cv_solaris_64bit=yes], [gl_cv_solaris_64bit=no]) ]) if test $gl_cv_solaris_64bit = yes; then acl_libdirstem=lib/64 case "$host_cpu" in sparc*) acl_libdirstem2=lib/sparcv9 ;; i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; esac fi ;; *) searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` if test -n "$searchpath"; then acl_save_IFS="${IFS= }"; IFS=":" for searchdir in $searchpath; do if test -d "$searchdir"; then case "$searchdir" in */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; */../ | */.. ) # Better ignore directories of this form. They are misleading. ;; *) searchdir=`cd "$searchdir" && pwd` case "$searchdir" in */lib64 ) acl_libdirstem=lib64 ;; esac ;; esac fi done IFS="$acl_save_IFS" fi ;; esac test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" ]) DarkRadiant-2.5.0/m4/nls.m4000066400000000000000000000023151321750546400152700ustar00rootroot00000000000000# nls.m4 serial 5 (gettext-0.18) dnl Copyright (C) 1995-2003, 2005-2006, 2008-2010 Free Software Foundation, dnl Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl This file can can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1995-2000. dnl Bruno Haible , 2000-2003. AC_PREREQ([2.50]) AC_DEFUN([AM_NLS], [ AC_MSG_CHECKING([whether NLS is requested]) dnl Default is enabled NLS AC_ARG_ENABLE([nls], [ --disable-nls do not use Native Language Support], USE_NLS=$enableval, USE_NLS=yes) AC_MSG_RESULT([$USE_NLS]) AC_SUBST([USE_NLS]) ]) DarkRadiant-2.5.0/m4/po.m4000066400000000000000000000450311321750546400151140ustar00rootroot00000000000000# po.m4 serial 24 (gettext-0.19) dnl Copyright (C) 1995-2014, 2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl This file can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1995-2000. dnl Bruno Haible , 2000-2003. AC_PREREQ([2.60]) dnl Checks for all prerequisites of the po subdirectory. AC_DEFUN([AM_PO_SUBDIRS], [ AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl AC_REQUIRE([AC_PROG_SED])dnl AC_REQUIRE([AM_NLS])dnl dnl Release version of the gettext macros. This is used to ensure that dnl the gettext macros and po/Makefile.in.in are in sync. AC_SUBST([GETTEXT_MACRO_VERSION], [0.19]) dnl Perform the following tests also if --disable-nls has been given, dnl because they are needed for "make dist" to work. dnl Search for GNU msgfmt in the PATH. dnl The first test excludes Solaris msgfmt and early GNU msgfmt versions. dnl The second test excludes FreeBSD msgfmt. AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, [$ac_dir/$ac_word --statistics /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 && (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], :) AC_PATH_PROG([GMSGFMT], [gmsgfmt], [$MSGFMT]) dnl Test whether it is GNU msgfmt >= 0.15. changequote(,)dnl case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;; *) MSGFMT_015=$MSGFMT ;; esac changequote([,])dnl AC_SUBST([MSGFMT_015]) changequote(,)dnl case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;; *) GMSGFMT_015=$GMSGFMT ;; esac changequote([,])dnl AC_SUBST([GMSGFMT_015]) dnl Search for GNU xgettext 0.12 or newer in the PATH. dnl The first test excludes Solaris xgettext and early GNU xgettext versions. dnl The second test excludes FreeBSD xgettext. AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 && (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], :) dnl Remove leftover from FreeBSD xgettext call. rm -f messages.po dnl Test whether it is GNU xgettext >= 0.15. changequote(,)dnl case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;; *) XGETTEXT_015=$XGETTEXT ;; esac changequote([,])dnl AC_SUBST([XGETTEXT_015]) dnl Search for GNU msgmerge 0.11 or newer in the PATH. AM_PATH_PROG_WITH_TEST(MSGMERGE, msgmerge, [$ac_dir/$ac_word --update -q /dev/null /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1], :) dnl Installation directories. dnl Autoconf >= 2.60 defines localedir. For older versions of autoconf, we dnl have to define it here, so that it can be used in po/Makefile. test -n "$localedir" || localedir='${datadir}/locale' AC_SUBST([localedir]) dnl Support for AM_XGETTEXT_OPTION. test -n "${XGETTEXT_EXTRA_OPTIONS+set}" || XGETTEXT_EXTRA_OPTIONS= AC_SUBST([XGETTEXT_EXTRA_OPTIONS]) AC_CONFIG_COMMANDS([po-directories], [[ for ac_file in $CONFIG_FILES; do # Support "outfile[:infile[:infile...]]" case "$ac_file" in *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; esac # PO directories have a Makefile.in generated from Makefile.in.in. case "$ac_file" in */Makefile.in) # Adjust a relative srcdir. ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` ac_dir_suffix=/`echo "$ac_dir"|sed 's%^\./%%'` ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` # In autoconf-2.13 it is called $ac_given_srcdir. # In autoconf-2.50 it is called $srcdir. test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" case "$ac_given_srcdir" in .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; /*) top_srcdir="$ac_given_srcdir" ;; *) top_srcdir="$ac_dots$ac_given_srcdir" ;; esac # Treat a directory as a PO directory if and only if it has a # POTFILES.in file. This allows packages to have multiple PO # directories under different names or in different locations. if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then rm -f "$ac_dir/POTFILES" test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES" gt_tab=`printf '\t'` cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ${gt_tab}]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES" POMAKEFILEDEPS="POTFILES.in" # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend # on $ac_dir but don't depend on user-specified configuration # parameters. if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then # The LINGUAS file contains the set of available languages. if test -n "$OBSOLETE_ALL_LINGUAS"; then test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete" fi ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"` # Hide the ALL_LINGUAS assignment from automake < 1.5. eval 'ALL_LINGUAS''=$ALL_LINGUAS_' POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS" else # The set of available languages was given in configure.in. # Hide the ALL_LINGUAS assignment from automake < 1.5. eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS' fi # Compute POFILES # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po) # Compute UPDATEPOFILES # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update) # Compute DUMMYPOFILES # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop) # Compute GMOFILES # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo) case "$ac_given_srcdir" in .) srcdirpre= ;; *) srcdirpre='$(srcdir)/' ;; esac POFILES= UPDATEPOFILES= DUMMYPOFILES= GMOFILES= for lang in $ALL_LINGUAS; do POFILES="$POFILES $srcdirpre$lang.po" UPDATEPOFILES="$UPDATEPOFILES $lang.po-update" DUMMYPOFILES="$DUMMYPOFILES $lang.nop" GMOFILES="$GMOFILES $srcdirpre$lang.gmo" done # CATALOGS depends on both $ac_dir and the user's LINGUAS # environment variable. INST_LINGUAS= if test -n "$ALL_LINGUAS"; then for presentlang in $ALL_LINGUAS; do useit=no if test "%UNSET%" != "$LINGUAS"; then desiredlanguages="$LINGUAS" else desiredlanguages="$ALL_LINGUAS" fi for desiredlang in $desiredlanguages; do # Use the presentlang catalog if desiredlang is # a. equal to presentlang, or # b. a variant of presentlang (because in this case, # presentlang can be used as a fallback for messages # which are not translated in the desiredlang catalog). case "$desiredlang" in "$presentlang"*) useit=yes;; esac done if test $useit = yes; then INST_LINGUAS="$INST_LINGUAS $presentlang" fi done fi CATALOGS= if test -n "$INST_LINGUAS"; then for lang in $INST_LINGUAS; do CATALOGS="$CATALOGS $lang.gmo" done fi test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile" sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile" for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do if test -f "$f"; then case "$f" in *.orig | *.bak | *~) ;; *) cat "$f" >> "$ac_dir/Makefile" ;; esac fi done fi ;; esac done]], [# Capture the value of obsolete ALL_LINGUAS because we need it to compute # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it # from automake < 1.5. eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"' # Capture the value of LINGUAS because we need it to compute CATALOGS. LINGUAS="${LINGUAS-%UNSET%}" ]) ]) dnl Postprocesses a Makefile in a directory containing PO files. AC_DEFUN([AM_POSTPROCESS_PO_MAKEFILE], [ # When this code is run, in config.status, two variables have already been # set: # - OBSOLETE_ALL_LINGUAS is the value of LINGUAS set in configure.in, # - LINGUAS is the value of the environment variable LINGUAS at configure # time. changequote(,)dnl # Adjust a relative srcdir. ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` ac_dir_suffix=/`echo "$ac_dir"|sed 's%^\./%%'` ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` # In autoconf-2.13 it is called $ac_given_srcdir. # In autoconf-2.50 it is called $srcdir. test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" case "$ac_given_srcdir" in .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; /*) top_srcdir="$ac_given_srcdir" ;; *) top_srcdir="$ac_dots$ac_given_srcdir" ;; esac # Find a way to echo strings without interpreting backslash. if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then gt_echo='echo' else if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then gt_echo='printf %s\n' else echo_func () { cat < "$ac_file.tmp" tab=`printf '\t'` if grep -l '@TCLCATALOGS@' "$ac_file" > /dev/null; then # Add dependencies that cannot be formulated as a simple suffix rule. for lang in $ALL_LINGUAS; do frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` cat >> "$ac_file.tmp" < /dev/null; then # Add dependencies that cannot be formulated as a simple suffix rule. for lang in $ALL_LINGUAS; do frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'` cat >> "$ac_file.tmp" <> "$ac_file.tmp" <, 1996. AC_PREREQ([2.50]) # Search path for a program which passes the given test. dnl AM_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) AC_DEFUN([AM_PATH_PROG_WITH_TEST], [ # Prepare PATH_SEPARATOR. # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Find out how to test for executable files. Don't use a zero-byte file, # as systems may use methods other than mode bits to determine executability. cat >conf$$.file <<_ASEOF #! /bin/sh exit 0 _ASEOF chmod +x conf$$.file if test -x conf$$.file >/dev/null 2>&1; then ac_executable_p="test -x" else ac_executable_p="test -f" fi rm -f conf$$.file # Extract the first word of "$2", so it can be a program name with args. set dummy $2; ac_word=[$]2 AC_MSG_CHECKING([for $ac_word]) AC_CACHE_VAL([ac_cv_path_$1], [case "[$]$1" in [[\\/]]* | ?:[[\\/]]*) ac_cv_path_$1="[$]$1" # Let the user override the test with a path. ;; *) ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in ifelse([$5], , $PATH, [$5]); do IFS="$ac_save_IFS" test -z "$ac_dir" && ac_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then echo "$as_me: trying $ac_dir/$ac_word..." >&AS_MESSAGE_LOG_FD if [$3]; then ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext" break 2 fi fi done done IFS="$ac_save_IFS" dnl If no 4th arg is given, leave the cache variable unset, dnl so AC_PATH_PROGS will keep looking. ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" ])dnl ;; esac])dnl $1="$ac_cv_path_$1" if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then AC_MSG_RESULT([$][$1]) else AC_MSG_RESULT([no]) fi AC_SUBST([$1])dnl ]) DarkRadiant-2.5.0/m4/wxwin.m4000077500000000000000000001171611321750546400156610ustar00rootroot00000000000000dnl --------------------------------------------------------------------------- dnl Author: wxWidgets development team, dnl Francesco Montorsi, dnl Bob McCown (Mac-testing) dnl Creation date: 24/11/2001 dnl --------------------------------------------------------------------------- dnl =========================================================================== dnl Table of Contents of this macro file: dnl ------------------------------------- dnl dnl SECTION A: wxWidgets main macros dnl - WX_CONFIG_OPTIONS dnl - WX_CONFIG_CHECK dnl - WXRC_CHECK dnl - WX_STANDARD_OPTIONS dnl - WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS dnl - WX_DETECT_STANDARD_OPTION_VALUES dnl dnl SECTION B: wxWidgets-related utilities dnl - WX_LIKE_LIBNAME dnl - WX_ARG_ENABLE_YESNOAUTO dnl - WX_ARG_WITH_YESNOAUTO dnl dnl SECTION C: messages to the user dnl - WX_STANDARD_OPTIONS_SUMMARY_MSG dnl - WX_STANDARD_OPTIONS_SUMMARY_MSG_BEGIN dnl - WX_STANDARD_OPTIONS_SUMMARY_MSG_END dnl - WX_BOOLOPT_SUMMARY dnl dnl The special "WX_DEBUG_CONFIGURE" variable can be set to 1 to enable extra dnl debug output on stdout from these macros. dnl =========================================================================== dnl --------------------------------------------------------------------------- dnl Macros for wxWidgets detection. Typically used in configure.in as: dnl dnl AC_ARG_ENABLE(...) dnl AC_ARG_WITH(...) dnl ... dnl WX_CONFIG_OPTIONS dnl ... dnl ... dnl WX_CONFIG_CHECK([2.6.0], [wxWin=1]) dnl if test "$wxWin" != 1; then dnl AC_MSG_ERROR([ dnl wxWidgets must be installed on your system dnl but wx-config script couldn't be found. dnl dnl Please check that wx-config is in path, the directory dnl where wxWidgets libraries are installed (returned by dnl 'wx-config --libs' command) is in LD_LIBRARY_PATH or dnl equivalent variable and wxWidgets version is 2.3.4 or above. dnl ]) dnl fi dnl CPPFLAGS="$CPPFLAGS $WX_CPPFLAGS" dnl CXXFLAGS="$CXXFLAGS $WX_CXXFLAGS_ONLY" dnl CFLAGS="$CFLAGS $WX_CFLAGS_ONLY" dnl dnl LIBS="$LIBS $WX_LIBS" dnl dnl If you want to support standard --enable-debug/unicode/shared options, you dnl may do the following: dnl dnl ... dnl AC_CANONICAL_SYSTEM dnl dnl # define configure options dnl WX_CONFIG_OPTIONS dnl WX_STANDARD_OPTIONS([debug,unicode,shared,toolkit,wxshared]) dnl dnl # basic configure checks dnl ... dnl dnl # we want to always have DEBUG==WX_DEBUG and UNICODE==WX_UNICODE dnl WX_DEBUG=$DEBUG dnl WX_UNICODE=$UNICODE dnl dnl WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS dnl WX_CONFIG_CHECK([2.8.0], [wxWin=1],,[html,core,net,base],[$WXCONFIG_FLAGS]) dnl WX_DETECT_STANDARD_OPTION_VALUES dnl dnl # write the output files dnl AC_CONFIG_FILES([Makefile ...]) dnl AC_OUTPUT dnl dnl # optional: just to show a message to the user dnl WX_STANDARD_OPTIONS_SUMMARY_MSG dnl dnl --------------------------------------------------------------------------- dnl --------------------------------------------------------------------------- dnl WX_CONFIG_OPTIONS dnl dnl adds support for --wx-prefix, --wx-exec-prefix, --with-wxdir and dnl --wx-config command line options dnl --------------------------------------------------------------------------- AC_DEFUN([WX_CONFIG_OPTIONS], [ AC_ARG_WITH(wxdir, [ --with-wxdir=PATH Use uninstalled version of wxWidgets in PATH], [ wx_config_name="$withval/wx-config" wx_config_args="--inplace"]) AC_ARG_WITH(wx-config, [ --with-wx-config=CONFIG wx-config script to use (optional)], wx_config_name="$withval" ) AC_ARG_WITH(wx-prefix, [ --with-wx-prefix=PREFIX Prefix where wxWidgets is installed (optional)], wx_config_prefix="$withval", wx_config_prefix="") AC_ARG_WITH(wx-exec-prefix, [ --with-wx-exec-prefix=PREFIX Exec prefix where wxWidgets is installed (optional)], wx_config_exec_prefix="$withval", wx_config_exec_prefix="") ]) dnl Helper macro for checking if wx version is at least $1.$2.$3, set's dnl wx_ver_ok=yes if it is: AC_DEFUN([_WX_PRIVATE_CHECK_VERSION], [ wx_ver_ok="" if test "x$WX_VERSION" != x ; then if test $wx_config_major_version -gt $1; then wx_ver_ok=yes else if test $wx_config_major_version -eq $1; then if test $wx_config_minor_version -gt $2; then wx_ver_ok=yes else if test $wx_config_minor_version -eq $2; then if test $wx_config_micro_version -ge $3; then wx_ver_ok=yes fi fi fi fi fi fi ]) dnl --------------------------------------------------------------------------- dnl WX_CONFIG_CHECK(VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND dnl [, WX-LIBS [, ADDITIONAL-WX-CONFIG-FLAGS]]]]) dnl dnl Test for wxWidgets, and define WX_C*FLAGS, WX_LIBS and WX_LIBS_STATIC dnl (the latter is for static linking against wxWidgets). Set WX_CONFIG_NAME dnl environment variable to override the default name of the wx-config script dnl to use. Set WX_CONFIG_PATH to specify the full path to wx-config - in this dnl case the macro won't even waste time on tests for its existence. dnl dnl Optional WX-LIBS argument contains comma- or space-separated list of dnl wxWidgets libraries to link against. If it is not specified then WX_LIBS dnl and WX_LIBS_STATIC will contain flags to link with all of the core dnl wxWidgets libraries. dnl dnl Optional ADDITIONAL-WX-CONFIG-FLAGS argument is appended to wx-config dnl invocation command in present. It can be used to fine-tune lookup of dnl best wxWidgets build available. dnl dnl Example use: dnl WX_CONFIG_CHECK([2.6.0], [wxWin=1], [wxWin=0], [html,core,net] dnl [--unicode --debug]) dnl --------------------------------------------------------------------------- dnl dnl Get the cflags and libraries from the wx-config script dnl AC_DEFUN([WX_CONFIG_CHECK], [ dnl do we have wx-config name: it can be wx-config or wxd-config or ... if test x${WX_CONFIG_NAME+set} != xset ; then WX_CONFIG_NAME=wx-config fi if test "x$wx_config_name" != x ; then WX_CONFIG_NAME="$wx_config_name" fi dnl deal with optional prefixes if test x$wx_config_exec_prefix != x ; then wx_config_args="$wx_config_args --exec-prefix=$wx_config_exec_prefix" WX_LOOKUP_PATH="$wx_config_exec_prefix/bin" fi if test x$wx_config_prefix != x ; then wx_config_args="$wx_config_args --prefix=$wx_config_prefix" WX_LOOKUP_PATH="$WX_LOOKUP_PATH:$wx_config_prefix/bin" fi if test "$cross_compiling" = "yes"; then wx_config_args="$wx_config_args --host=$host_alias" fi dnl don't search the PATH if WX_CONFIG_NAME is absolute filename if test -x "$WX_CONFIG_NAME" ; then AC_MSG_CHECKING(for wx-config) WX_CONFIG_PATH="$WX_CONFIG_NAME" AC_MSG_RESULT($WX_CONFIG_PATH) else AC_PATH_PROG(WX_CONFIG_PATH, $WX_CONFIG_NAME, no, "$WX_LOOKUP_PATH:$PATH") fi if test "$WX_CONFIG_PATH" != "no" ; then WX_VERSION="" min_wx_version=ifelse([$1], ,2.2.1,$1) if test -z "$5" ; then AC_MSG_CHECKING([for wxWidgets version >= $min_wx_version]) else AC_MSG_CHECKING([for wxWidgets version >= $min_wx_version ($5)]) fi dnl don't add the libraries ($4) to this variable as this would result in dnl an error when it's used with --version below WX_CONFIG_WITH_ARGS="$WX_CONFIG_PATH $wx_config_args $5" WX_VERSION=`$WX_CONFIG_WITH_ARGS --version 2>/dev/null` wx_config_major_version=`echo $WX_VERSION | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` wx_config_minor_version=`echo $WX_VERSION | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` wx_config_micro_version=`echo $WX_VERSION | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` wx_requested_major_version=`echo $min_wx_version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` wx_requested_minor_version=`echo $min_wx_version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` wx_requested_micro_version=`echo $min_wx_version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` _WX_PRIVATE_CHECK_VERSION([$wx_requested_major_version], [$wx_requested_minor_version], [$wx_requested_micro_version]) if test -n "$wx_ver_ok"; then AC_MSG_RESULT(yes (version $WX_VERSION)) WX_LIBS=`$WX_CONFIG_WITH_ARGS --libs $4` dnl is this even still appropriate? --static is a real option now dnl and WX_CONFIG_WITH_ARGS is likely to contain it if that is dnl what the user actually wants, making this redundant at best. dnl For now keep it in case anyone actually used it in the past. AC_MSG_CHECKING([for wxWidgets static library]) WX_LIBS_STATIC=`$WX_CONFIG_WITH_ARGS --static --libs $4 2>/dev/null` if test "x$WX_LIBS_STATIC" = "x"; then AC_MSG_RESULT(no) else AC_MSG_RESULT(yes) fi dnl starting with version 2.2.6 wx-config has --cppflags argument wx_has_cppflags="" if test $wx_config_major_version -gt 2; then wx_has_cppflags=yes else if test $wx_config_major_version -eq 2; then if test $wx_config_minor_version -gt 2; then wx_has_cppflags=yes else if test $wx_config_minor_version -eq 2; then if test $wx_config_micro_version -ge 6; then wx_has_cppflags=yes fi fi fi fi fi dnl starting with version 2.7.0 wx-config has --rescomp option wx_has_rescomp="" if test $wx_config_major_version -gt 2; then wx_has_rescomp=yes else if test $wx_config_major_version -eq 2; then if test $wx_config_minor_version -ge 7; then wx_has_rescomp=yes fi fi fi if test "x$wx_has_rescomp" = x ; then dnl cannot give any useful info for resource compiler WX_RESCOMP= else WX_RESCOMP=`$WX_CONFIG_WITH_ARGS --rescomp` fi if test "x$wx_has_cppflags" = x ; then dnl no choice but to define all flags like CFLAGS WX_CFLAGS=`$WX_CONFIG_WITH_ARGS --cflags $4` WX_CPPFLAGS=$WX_CFLAGS WX_CXXFLAGS=$WX_CFLAGS WX_CFLAGS_ONLY=$WX_CFLAGS WX_CXXFLAGS_ONLY=$WX_CFLAGS else dnl we have CPPFLAGS included in CFLAGS included in CXXFLAGS WX_CPPFLAGS=`$WX_CONFIG_WITH_ARGS --cppflags $4` WX_CXXFLAGS=`$WX_CONFIG_WITH_ARGS --cxxflags $4` WX_CFLAGS=`$WX_CONFIG_WITH_ARGS --cflags $4` WX_CFLAGS_ONLY=`echo $WX_CFLAGS | sed "s@^$WX_CPPFLAGS *@@"` WX_CXXFLAGS_ONLY=`echo $WX_CXXFLAGS | sed "s@^$WX_CFLAGS *@@"` fi ifelse([$2], , :, [$2]) else if test "x$WX_VERSION" = x; then dnl no wx-config at all AC_MSG_RESULT(no) else AC_MSG_RESULT(no (version $WX_VERSION is not new enough)) fi WX_CFLAGS="" WX_CPPFLAGS="" WX_CXXFLAGS="" WX_LIBS="" WX_LIBS_STATIC="" WX_RESCOMP="" if test ! -z "$5"; then wx_error_message=" The configuration you asked for $PACKAGE_NAME requires a wxWidgets build with the following settings: $5 but such build is not available. To see the wxWidgets builds available on this system, please use 'wx-config --list' command. To use the default build, returned by 'wx-config --selected-config', use the options with their 'auto' default values." fi wx_error_message=" The requested wxWidgets build couldn't be found. $wx_error_message If you still get this error, then check that 'wx-config' is in path, the directory where wxWidgets libraries are installed (returned by 'wx-config --libs' command) is in LD_LIBRARY_PATH or equivalent variable and wxWidgets version is $1 or above." ifelse([$3], , AC_MSG_ERROR([$wx_error_message]), [$3]) fi else WX_CFLAGS="" WX_CPPFLAGS="" WX_CXXFLAGS="" WX_LIBS="" WX_LIBS_STATIC="" WX_RESCOMP="" ifelse([$3], , :, [$3]) fi AC_SUBST(WX_CPPFLAGS) AC_SUBST(WX_CFLAGS) AC_SUBST(WX_CXXFLAGS) AC_SUBST(WX_CFLAGS_ONLY) AC_SUBST(WX_CXXFLAGS_ONLY) AC_SUBST(WX_LIBS) AC_SUBST(WX_LIBS_STATIC) AC_SUBST(WX_VERSION) AC_SUBST(WX_RESCOMP) dnl need to export also WX_VERSION_MINOR and WX_VERSION_MAJOR symbols dnl to support wxpresets bakefiles (we export also WX_VERSION_MICRO for completeness): WX_VERSION_MAJOR="$wx_config_major_version" WX_VERSION_MINOR="$wx_config_minor_version" WX_VERSION_MICRO="$wx_config_micro_version" AC_SUBST(WX_VERSION_MAJOR) AC_SUBST(WX_VERSION_MINOR) AC_SUBST(WX_VERSION_MICRO) ]) dnl --------------------------------------------------------------------------- dnl Get information on the wxrc program for making C++, Python and xrs dnl resource files. dnl dnl AC_ARG_ENABLE(...) dnl AC_ARG_WITH(...) dnl ... dnl WX_CONFIG_OPTIONS dnl ... dnl WX_CONFIG_CHECK(2.6.0, wxWin=1) dnl if test "$wxWin" != 1; then dnl AC_MSG_ERROR([ dnl wxWidgets must be installed on your system dnl but wx-config script couldn't be found. dnl dnl Please check that wx-config is in path, the directory dnl where wxWidgets libraries are installed (returned by dnl 'wx-config --libs' command) is in LD_LIBRARY_PATH or dnl equivalent variable and wxWidgets version is 2.6.0 or above. dnl ]) dnl fi dnl dnl WXRC_CHECK([HAVE_WXRC=1], [HAVE_WXRC=0]) dnl if test "x$HAVE_WXRC" != x1; then dnl AC_MSG_ERROR([ dnl The wxrc program was not installed or not found. dnl dnl Please check the wxWidgets installation. dnl ]) dnl fi dnl dnl CPPFLAGS="$CPPFLAGS $WX_CPPFLAGS" dnl CXXFLAGS="$CXXFLAGS $WX_CXXFLAGS_ONLY" dnl CFLAGS="$CFLAGS $WX_CFLAGS_ONLY" dnl dnl LDFLAGS="$LDFLAGS $WX_LIBS" dnl --------------------------------------------------------------------------- dnl --------------------------------------------------------------------------- dnl WXRC_CHECK([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) dnl dnl Test for wxWidgets' wxrc program for creating either C++, Python or XRS dnl resources. The variable WXRC will be set and substituted in the configure dnl script and Makefiles. dnl dnl Example use: dnl WXRC_CHECK([wxrc=1], [wxrc=0]) dnl --------------------------------------------------------------------------- dnl dnl wxrc program from the wx-config script dnl AC_DEFUN([WXRC_CHECK], [ AC_ARG_VAR([WXRC], [Path to wxWidget's wxrc resource compiler]) if test "x$WX_CONFIG_NAME" = x; then AC_MSG_ERROR([The wxrc tests must run after wxWidgets test.]) else AC_MSG_CHECKING([for wxrc]) if test "x$WXRC" = x ; then dnl wx-config --utility is a new addition to wxWidgets: _WX_PRIVATE_CHECK_VERSION(2,5,3) if test -n "$wx_ver_ok"; then WXRC=`$WX_CONFIG_WITH_ARGS --utility=wxrc` fi fi if test "x$WXRC" = x ; then AC_MSG_RESULT([not found]) ifelse([$2], , :, [$2]) else AC_MSG_RESULT([$WXRC]) ifelse([$1], , :, [$1]) fi AC_SUBST(WXRC) fi ]) dnl --------------------------------------------------------------------------- dnl WX_LIKE_LIBNAME([output-var] [prefix], [name]) dnl dnl Sets the "output-var" variable to the name of a library named with same dnl wxWidgets rule. dnl E.g. for output-var=='lib', name=='test', prefix='mine', sets dnl the $lib variable to: dnl 'mine_gtk2ud_test-2.8' dnl if WX_PORT=gtk2, WX_UNICODE=1, WX_DEBUG=1 and WX_RELEASE=28 dnl --------------------------------------------------------------------------- AC_DEFUN([WX_LIKE_LIBNAME], [ wx_temp="$2""_""$WX_PORT" dnl add the [u][d] string if test "$WX_UNICODE" = "1"; then wx_temp="$wx_temp""u" fi if test "$WX_DEBUG" = "1"; then wx_temp="$wx_temp""d" fi dnl complete the name of the lib wx_temp="$wx_temp""_""$3""-$WX_VERSION_MAJOR.$WX_VERSION_MINOR" dnl save it in the user's variable $1=$wx_temp ]) dnl --------------------------------------------------------------------------- dnl WX_ARG_ENABLE_YESNOAUTO/WX_ARG_WITH_YESNOAUTO dnl dnl Two little custom macros which define the ENABLE/WITH configure arguments. dnl Macro arguments: dnl $1 = the name of the --enable / --with feature dnl $2 = the name of the variable associated dnl $3 = the description of that feature dnl $4 = the default value for that feature dnl $5 = additional action to do in case option is given with "yes" value dnl --------------------------------------------------------------------------- AC_DEFUN([WX_ARG_ENABLE_YESNOAUTO], [AC_ARG_ENABLE($1, AC_HELP_STRING([--enable-$1], [$3 (default is $4)]), [], [enableval="$4"]) dnl Show a message to the user about this option AC_MSG_CHECKING([for the --enable-$1 option]) if test "$enableval" = "yes" ; then AC_MSG_RESULT([yes]) $2=1 $5 elif test "$enableval" = "no" ; then AC_MSG_RESULT([no]) $2=0 elif test "$enableval" = "auto" ; then AC_MSG_RESULT([will be automatically detected]) $2="auto" else AC_MSG_ERROR([ Unrecognized option value (allowed values: yes, no, auto) ]) fi ]) AC_DEFUN([WX_ARG_WITH_YESNOAUTO], [AC_ARG_WITH($1, AC_HELP_STRING([--with-$1], [$3 (default is $4)]), [], [withval="$4"]) dnl Show a message to the user about this option AC_MSG_CHECKING([for the --with-$1 option]) if test "$withval" = "yes" ; then AC_MSG_RESULT([yes]) $2=1 $5 dnl NB: by default we don't allow --with-$1=no option dnl since it does not make much sense ! elif test "$6" = "1" -a "$withval" = "no" ; then AC_MSG_RESULT([no]) $2=0 elif test "$withval" = "auto" ; then AC_MSG_RESULT([will be automatically detected]) $2="auto" else AC_MSG_ERROR([ Unrecognized option value (allowed values: yes, auto) ]) fi ]) dnl --------------------------------------------------------------------------- dnl WX_STANDARD_OPTIONS([options-to-add]) dnl dnl Adds to the configure script one or more of the following options: dnl --enable-[debug|unicode|shared|wxshared|wxdebug] dnl --with-[gtk|msw|motif|x11|mac|dfb] dnl --with-wxversion dnl Then checks for their presence and eventually set the DEBUG, UNICODE, SHARED, dnl PORT, WX_SHARED, WX_DEBUG, variables to one of the "yes", "no", "auto" values. dnl dnl Note that e.g. UNICODE != WX_UNICODE; the first is the value of the dnl --enable-unicode option (in boolean format) while the second indicates dnl if wxWidgets was built in Unicode mode (and still is in boolean format). dnl --------------------------------------------------------------------------- AC_DEFUN([WX_STANDARD_OPTIONS], [ dnl the following lines will expand to WX_ARG_ENABLE_YESNOAUTO calls if and only if dnl the $1 argument contains respectively the debug,unicode or shared options. dnl be careful here not to set debug flag if only "wxdebug" was specified ifelse(regexp([$1], [\bdebug]), [-1],, [WX_ARG_ENABLE_YESNOAUTO([debug], [DEBUG], [Build in debug mode], [auto])]) ifelse(index([$1], [unicode]), [-1],, [WX_ARG_ENABLE_YESNOAUTO([unicode], [UNICODE], [Build in Unicode mode], [auto])]) ifelse(regexp([$1], [\bshared]), [-1],, [WX_ARG_ENABLE_YESNOAUTO([shared], [SHARED], [Build as shared library], [auto])]) dnl WX_ARG_WITH_YESNOAUTO cannot be used for --with-toolkit since it's an option dnl which must be able to accept the auto|gtk1|gtk2|msw|... values ifelse(index([$1], [toolkit]), [-1],, [ AC_ARG_WITH([toolkit], AC_HELP_STRING([--with-toolkit], [Build against a specific wxWidgets toolkit (default is auto)]), [], [withval="auto"]) dnl Show a message to the user about this option AC_MSG_CHECKING([for the --with-toolkit option]) if test "$withval" = "auto" ; then AC_MSG_RESULT([will be automatically detected]) TOOLKIT="auto" else TOOLKIT="$withval" dnl PORT must be one of the allowed values if test "$TOOLKIT" != "gtk1" -a "$TOOLKIT" != "gtk2" -a \ "$TOOLKIT" != "msw" -a "$TOOLKIT" != "motif" -a \ "$TOOLKIT" != "osx_carbon" -a "$TOOLKIT" != "osx_cocoa" -a \ "$TOOLKIT" != "dfb" -a "$TOOLKIT" != "x11"; then AC_MSG_ERROR([ Unrecognized option value (allowed values: auto, gtk1, gtk2, msw, motif, osx_carbon, osx_cocoa, dfb, x11) ]) fi AC_MSG_RESULT([$TOOLKIT]) fi ]) dnl ****** IMPORTANT ******* dnl Unlike for the UNICODE setting, you can build your program in dnl shared mode against a static build of wxWidgets. Thus we have the dnl following option which allows these mixtures. E.g. dnl dnl ./configure --disable-shared --with-wxshared dnl dnl will build your library in static mode against the first available dnl shared build of wxWidgets. dnl dnl Note that's not possible to do the viceversa: dnl dnl ./configure --enable-shared --without-wxshared dnl dnl Doing so you would try to build your library in shared mode against a static dnl build of wxWidgets. This is not possible (you would mix PIC and non PIC code) ! dnl A check for this combination of options is in WX_DETECT_STANDARD_OPTION_VALUES dnl (where we know what 'auto' should be expanded to). dnl dnl If you try to build something in ANSI mode against a UNICODE build dnl of wxWidgets or in RELEASE mode against a DEBUG build of wxWidgets, dnl then at best you'll get ton of linking errors ! dnl ************************ ifelse(index([$1], [wxshared]), [-1],, [ WX_ARG_WITH_YESNOAUTO( [wxshared], [WX_SHARED], [Force building against a shared build of wxWidgets, even if --disable-shared is given], [auto], [], [1]) ]) dnl Just like for SHARED and WX_SHARED it may happen that some adventurous dnl peoples will want to mix a wxWidgets release build with a debug build of dnl his app/lib. So, we have both DEBUG and WX_DEBUG variables. ifelse(index([$1], [wxdebug]), [-1],, [ WX_ARG_WITH_YESNOAUTO( [wxdebug], [WX_DEBUG], [Force building against a debug build of wxWidgets, even if --disable-debug is given], [auto], [], [1]) ]) dnl WX_ARG_WITH_YESNOAUTO cannot be used for --with-wxversion since it's an option dnl which accepts the "auto|2.6|2.7|2.8|2.9|3.0" etc etc values ifelse(index([$1], [wxversion]), [-1],, [ AC_ARG_WITH([wxversion], AC_HELP_STRING([--with-wxversion], [Build against a specific version of wxWidgets (default is auto)]), [], [withval="auto"]) dnl Show a message to the user about this option AC_MSG_CHECKING([for the --with-wxversion option]) if test "$withval" = "auto" ; then AC_MSG_RESULT([will be automatically detected]) WX_RELEASE="auto" else wx_requested_major_version=`echo $withval | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).*/\1/'` wx_requested_minor_version=`echo $withval | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).*/\2/'` dnl both vars above must be exactly 1 digit if test "${#wx_requested_major_version}" != "1" -o \ "${#wx_requested_minor_version}" != "1" ; then AC_MSG_ERROR([ Unrecognized option value (allowed values: auto, 2.6, 2.7, 2.8, 2.9, 3.0) ]) fi WX_RELEASE="$wx_requested_major_version"".""$wx_requested_minor_version" AC_MSG_RESULT([$WX_RELEASE]) fi ]) if test "$WX_DEBUG_CONFIGURE" = "1"; then echo "[[dbg]] DEBUG: $DEBUG, WX_DEBUG: $WX_DEBUG" echo "[[dbg]] UNICODE: $UNICODE, WX_UNICODE: $WX_UNICODE" echo "[[dbg]] SHARED: $SHARED, WX_SHARED: $WX_SHARED" echo "[[dbg]] TOOLKIT: $TOOLKIT, WX_TOOLKIT: $WX_TOOLKIT" echo "[[dbg]] VERSION: $VERSION, WX_RELEASE: $WX_RELEASE" fi ]) dnl --------------------------------------------------------------------------- dnl WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS dnl dnl Sets the WXCONFIG_FLAGS string using the SHARED,DEBUG,UNICODE variable values dnl which are different from "auto". dnl Thus this macro needs to be called only once all options have been set. dnl --------------------------------------------------------------------------- AC_DEFUN([WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS], [ if test "$WX_SHARED" = "1" ; then WXCONFIG_FLAGS="--static=no " elif test "$WX_SHARED" = "0" ; then WXCONFIG_FLAGS="--static=yes " fi if test "$WX_DEBUG" = "1" ; then WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--debug=yes " elif test "$WX_DEBUG" = "0" ; then WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--debug=no " fi dnl The user should have set WX_UNICODE=UNICODE if test "$WX_UNICODE" = "1" ; then WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--unicode=yes " elif test "$WX_UNICODE" = "0" ; then WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--unicode=no " fi if test "$TOOLKIT" != "auto" ; then WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--toolkit=$TOOLKIT " fi if test "$WX_RELEASE" != "auto" ; then WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--version=$WX_RELEASE " fi dnl strip out the last space of the string WXCONFIG_FLAGS=${WXCONFIG_FLAGS% } if test "$WX_DEBUG_CONFIGURE" = "1"; then echo "[[dbg]] WXCONFIG_FLAGS: $WXCONFIG_FLAGS" fi ]) dnl --------------------------------------------------------------------------- dnl _WX_SELECTEDCONFIG_CHECKFOR([RESULTVAR], [STRING], [MSG] dnl [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) dnl dnl Outputs the given MSG. Then searches the given STRING in the wxWidgets dnl additional CPP flags and put the result of the search in WX_$RESULTVAR dnl also adding the "yes" or "no" message result to MSG. dnl --------------------------------------------------------------------------- AC_DEFUN([_WX_SELECTEDCONFIG_CHECKFOR], [ if test "$$1" = "auto" ; then dnl The user does not have particular preferences for this option; dnl so we will detect the wxWidgets relative build setting and use it AC_MSG_CHECKING([$3]) dnl set WX_$1 variable to 1 if the $WX_SELECTEDCONFIG contains the $2 dnl string or to 0 otherwise. dnl NOTE: 'expr match STRING REGEXP' cannot be used since on Mac it dnl doesn't work; we use 'expr STRING : REGEXP' instead WX_$1=$(expr "$WX_SELECTEDCONFIG" : ".*$2.*") if test "$WX_$1" != "0"; then WX_$1=1 AC_MSG_RESULT([yes]) ifelse([$4], , :, [$4]) else WX_$1=0 AC_MSG_RESULT([no]) ifelse([$5], , :, [$5]) fi else dnl Use the setting given by the user WX_$1=$$1 fi ]) dnl --------------------------------------------------------------------------- dnl WX_DETECT_STANDARD_OPTION_VALUES dnl dnl Detects the values of the following variables: dnl 1) WX_RELEASE dnl 2) WX_UNICODE dnl 3) WX_DEBUG dnl 4) WX_SHARED (and also WX_STATIC) dnl 5) WX_PORT dnl from the previously selected wxWidgets build; this macro in fact must be dnl called *after* calling the WX_CONFIG_CHECK macro. dnl dnl Note that the WX_VERSION_MAJOR, WX_VERSION_MINOR symbols are already set dnl by WX_CONFIG_CHECK macro dnl --------------------------------------------------------------------------- AC_DEFUN([WX_DETECT_STANDARD_OPTION_VALUES], [ dnl IMPORTANT: WX_VERSION contains all three major.minor.micro digits, dnl while WX_RELEASE only the major.minor ones. WX_RELEASE="$WX_VERSION_MAJOR""$WX_VERSION_MINOR" if test $WX_RELEASE -lt 26 ; then AC_MSG_ERROR([ Cannot detect the wxWidgets configuration for the selected wxWidgets build since its version is $WX_VERSION < 2.6.0; please install a newer version of wxWidgets. ]) fi dnl The wx-config we are using understands the "--selected_config" dnl option which returns an easy-parseable string ! WX_SELECTEDCONFIG=$($WX_CONFIG_WITH_ARGS --selected_config) if test "$WX_DEBUG_CONFIGURE" = "1"; then echo "[[dbg]] Using wx-config --selected-config" echo "[[dbg]] WX_SELECTEDCONFIG: $WX_SELECTEDCONFIG" fi dnl we could test directly for WX_SHARED with a line like: dnl _WX_SELECTEDCONFIG_CHECKFOR([SHARED], [shared], dnl [if wxWidgets was built in SHARED mode]) dnl but wx-config --selected-config DOES NOT outputs the 'shared' dnl word when wx was built in shared mode; it rather outputs the dnl 'static' word when built in static mode. if test $WX_SHARED = "1"; then STATIC=0 elif test $WX_SHARED = "0"; then STATIC=1 elif test $WX_SHARED = "auto"; then STATIC="auto" fi dnl Now set the WX_UNICODE, WX_DEBUG, WX_STATIC variables _WX_SELECTEDCONFIG_CHECKFOR([UNICODE], [unicode], [if wxWidgets was built with UNICODE enabled]) _WX_SELECTEDCONFIG_CHECKFOR([DEBUG], [debug], [if wxWidgets was built in DEBUG mode]) _WX_SELECTEDCONFIG_CHECKFOR([STATIC], [static], [if wxWidgets was built in STATIC mode]) dnl init WX_SHARED from WX_STATIC if test "$WX_STATIC" != "0"; then WX_SHARED=0 else WX_SHARED=1 fi AC_SUBST(WX_UNICODE) AC_SUBST(WX_DEBUG) AC_SUBST(WX_SHARED) dnl detect the WX_PORT to use if test "$TOOLKIT" = "auto" ; then dnl The user does not have particular preferences for this option; dnl so we will detect the wxWidgets relative build setting and use it AC_MSG_CHECKING([which wxWidgets toolkit was selected]) WX_GTKPORT1=$(expr "$WX_SELECTEDCONFIG" : ".*gtk1.*") WX_GTKPORT2=$(expr "$WX_SELECTEDCONFIG" : ".*gtk2.*") WX_MSWPORT=$(expr "$WX_SELECTEDCONFIG" : ".*msw.*") WX_MOTIFPORT=$(expr "$WX_SELECTEDCONFIG" : ".*motif.*") WX_OSXCOCOAPORT=$(expr "$WX_SELECTEDCONFIG" : ".*osx_cocoa.*") WX_OSXCARBONPORT=$(expr "$WX_SELECTEDCONFIG" : ".*osx_carbon.*") WX_X11PORT=$(expr "$WX_SELECTEDCONFIG" : ".*x11.*") WX_DFBPORT=$(expr "$WX_SELECTEDCONFIG" : ".*dfb.*") WX_PORT="unknown" if test "$WX_GTKPORT1" != "0"; then WX_PORT="gtk1"; fi if test "$WX_GTKPORT2" != "0"; then WX_PORT="gtk2"; fi if test "$WX_MSWPORT" != "0"; then WX_PORT="msw"; fi if test "$WX_MOTIFPORT" != "0"; then WX_PORT="motif"; fi if test "$WX_OSXCOCOAPORT" != "0"; then WX_PORT="osx_cocoa"; fi if test "$WX_OSXCARBONPORT" != "0"; then WX_PORT="osx_carbon"; fi if test "$WX_X11PORT" != "0"; then WX_PORT="x11"; fi if test "$WX_DFBPORT" != "0"; then WX_PORT="dfb"; fi dnl NOTE: backward-compatible check for wx2.8; in wx2.9 the mac dnl ports are called 'osx_cocoa' and 'osx_carbon' (see above) WX_MACPORT=$(expr "$WX_SELECTEDCONFIG" : ".*mac.*") if test "$WX_MACPORT" != "0"; then WX_PORT="mac"; fi dnl check at least one of the WX_*PORT has been set ! if test "$WX_PORT" = "unknown" ; then AC_MSG_ERROR([ Cannot detect the currently installed wxWidgets port ! Please check your 'wx-config --cxxflags'... ]) fi AC_MSG_RESULT([$WX_PORT]) else dnl Use the setting given by the user if test -z "$TOOLKIT" ; then WX_PORT=$TOOLKIT else dnl try with PORT WX_PORT=$PORT fi fi AC_SUBST(WX_PORT) if test "$WX_DEBUG_CONFIGURE" = "1"; then echo "[[dbg]] Values of all WX_* options after final detection:" echo "[[dbg]] WX_DEBUG: $WX_DEBUG" echo "[[dbg]] WX_UNICODE: $WX_UNICODE" echo "[[dbg]] WX_SHARED: $WX_SHARED" echo "[[dbg]] WX_RELEASE: $WX_RELEASE" echo "[[dbg]] WX_PORT: $WX_PORT" fi dnl Avoid problem described in the WX_STANDARD_OPTIONS which happens when dnl the user gives the options: dnl ./configure --enable-shared --without-wxshared dnl or just do dnl ./configure --enable-shared dnl but there is only a static build of wxWidgets available. if test "$WX_SHARED" = "0" -a "$SHARED" = "1"; then AC_MSG_ERROR([ Cannot build shared library against a static build of wxWidgets ! This error happens because the wxWidgets build which was selected has been detected as static while you asked to build $PACKAGE_NAME as shared library and this is not possible. Use the '--disable-shared' option to build $PACKAGE_NAME as static library or '--with-wxshared' to use wxWidgets as shared library. ]) fi dnl now we can finally update the DEBUG,UNICODE,SHARED options dnl to their final values if they were set to 'auto' if test "$DEBUG" = "auto"; then DEBUG=$WX_DEBUG fi if test "$UNICODE" = "auto"; then UNICODE=$WX_UNICODE fi if test "$SHARED" = "auto"; then SHARED=$WX_SHARED fi if test "$TOOLKIT" = "auto"; then TOOLKIT=$WX_PORT fi dnl in case the user needs a BUILD=debug/release var... if test "$DEBUG" = "1"; then BUILD="debug" elif test "$DEBUG" = "0" -o "$DEBUG" = ""; then BUILD="release" fi dnl respect the DEBUG variable adding the optimize/debug flags dnl NOTE: the CXXFLAGS are merged together with the CPPFLAGS so we dnl don't need to set them, too if test "$DEBUG" = "1"; then CXXFLAGS="$CXXFLAGS -g -O0" CFLAGS="$CFLAGS -g -O0" else CXXFLAGS="$CXXFLAGS -O2" CFLAGS="$CFLAGS -O2" fi ]) dnl --------------------------------------------------------------------------- dnl WX_BOOLOPT_SUMMARY([name of the boolean variable to show summary for], dnl [what to print when var is 1], dnl [what to print when var is 0]) dnl dnl Prints $2 when variable $1 == 1 and prints $3 when variable $1 == 0. dnl This macro mainly exists just to make configure.ac scripts more readable. dnl dnl NOTE: you need to use the [" my message"] syntax for 2nd and 3rd arguments dnl if you want that m4 avoid to throw away the spaces prefixed to the dnl argument value. dnl --------------------------------------------------------------------------- AC_DEFUN([WX_BOOLOPT_SUMMARY], [ if test "x$$1" = "x1" ; then echo $2 elif test "x$$1" = "x0" ; then echo $3 else echo "$1 is $$1" fi ]) dnl --------------------------------------------------------------------------- dnl WX_STANDARD_OPTIONS_SUMMARY_MSG dnl dnl Shows a summary message to the user about the WX_* variable contents. dnl This macro is used typically at the end of the configure script. dnl --------------------------------------------------------------------------- AC_DEFUN([WX_STANDARD_OPTIONS_SUMMARY_MSG], [ echo echo " The wxWidgets build which will be used by $PACKAGE_NAME $PACKAGE_VERSION" echo " has the following settings:" WX_BOOLOPT_SUMMARY([WX_DEBUG], [" - DEBUG build"], [" - RELEASE build"]) WX_BOOLOPT_SUMMARY([WX_UNICODE], [" - UNICODE mode"], [" - ANSI mode"]) WX_BOOLOPT_SUMMARY([WX_SHARED], [" - SHARED mode"], [" - STATIC mode"]) echo " - VERSION: $WX_VERSION" echo " - PORT: $WX_PORT" ]) dnl --------------------------------------------------------------------------- dnl WX_STANDARD_OPTIONS_SUMMARY_MSG_BEGIN, WX_STANDARD_OPTIONS_SUMMARY_MSG_END dnl dnl Like WX_STANDARD_OPTIONS_SUMMARY_MSG macro but these two macros also gives info dnl about the configuration of the package which used the wxpresets. dnl dnl Typical usage: dnl WX_STANDARD_OPTIONS_SUMMARY_MSG_BEGIN dnl echo " - Package setting 1: $SETTING1" dnl echo " - Package setting 2: $SETTING1" dnl ... dnl WX_STANDARD_OPTIONS_SUMMARY_MSG_END dnl dnl --------------------------------------------------------------------------- AC_DEFUN([WX_STANDARD_OPTIONS_SUMMARY_MSG_BEGIN], [ echo echo " ----------------------------------------------------------------" echo " Configuration for $PACKAGE_NAME $PACKAGE_VERSION successfully completed." echo " Summary of main configuration settings for $PACKAGE_NAME:" WX_BOOLOPT_SUMMARY([DEBUG], [" - DEBUG build"], [" - RELEASE build"]) WX_BOOLOPT_SUMMARY([UNICODE], [" - UNICODE mode"], [" - ANSI mode"]) WX_BOOLOPT_SUMMARY([SHARED], [" - SHARED mode"], [" - STATIC mode"]) ]) AC_DEFUN([WX_STANDARD_OPTIONS_SUMMARY_MSG_END], [ WX_STANDARD_OPTIONS_SUMMARY_MSG echo echo " Now, just run make." echo " ----------------------------------------------------------------" echo ]) dnl --------------------------------------------------------------------------- dnl Deprecated macro wrappers dnl --------------------------------------------------------------------------- AC_DEFUN([AM_OPTIONS_WXCONFIG], [WX_CONFIG_OPTIONS]) AC_DEFUN([AM_PATH_WXCONFIG], [ WX_CONFIG_CHECK([$1],[$2],[$3],[$4],[$5]) ]) AC_DEFUN([AM_PATH_WXRC], [WXRC_CHECK([$1],[$2])]) DarkRadiant-2.5.0/man/000077500000000000000000000000001321750546400144645ustar00rootroot00000000000000DarkRadiant-2.5.0/man/darkradiant.1000066400000000000000000000034221321750546400170330ustar00rootroot00000000000000.TH DarkRadiant 1 2010-10-31 .ad l .SH NAME DarkRadiant \- the level editor for The Dark Mod .SH SYNOPSIS \fBdarkradiant\ \fR[\ options\ ]\ \fIfilename .SH DESCRIPTION DarkRadiant is a level editor for The Dark Mod. It loads and saves Doom 3-format *.map files, making use of assets within a game installation tree. .SH OPTIONS .TP 6 .B --disable-sound Disable the sound manager module. This may be useful if there are problems with sound devices on the system. .TP .BI fs_game= game Override the configured fs_game value. .TP .BI fs_game_base= game_base Override the configured fs_game_base value. .IP In order to allow maximum customisability by after-market mods, the \fBid software\fR game engines use a three-directory system: the .I base directory, which is where the core game assets are installed, and then two mod-specific directories known as .I fs_game and .IR fs_game_base . The intention is that .I fs_game_base is used for the common mod assets, and .I fs_game is available for individual map authors to further customise assets used in their own releases. .IP When editing Dark Mod missions, typically .I fs_game_base would be set to "darkmod", with .I fs_game either left blank or set to the value associated with a particular map author's own release. .IP These values are normally configured in preferences, but they can be overridden on the command line if required. .TP .I filename Path to a map file to load. This may be either an absolute filesystem path, or a mod-local path. .SH BUGS The lighting render mode is very limited compared to the Doom 3 rendering system. In particular, there are no stencil shadows, foglights or any other complex lighting effects. It's too big and too slow. .SH AUTHOR .nh DarkRadiant is developed by the Dark Mod team, at \fIhttp://www.thedarkmod.com .hy DarkRadiant-2.5.0/missing000077500000000000000000000153301321750546400153120ustar00rootroot00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2013-10-28.13; # UTC # Copyright (C) 1996-2014 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # 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 2, 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 . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: DarkRadiant-2.5.0/plugins/000077500000000000000000000000001321750546400153725ustar00rootroot00000000000000DarkRadiant-2.5.0/plugins/Makefile.am000066400000000000000000000012661321750546400174330ustar00rootroot00000000000000SUBDIRS = eventmanager \ archivezip \ commandsystem \ eclassmgr \ eclasstree \ entity \ entitylist \ filetypes \ filters \ fonts \ image \ mapdoom3 \ md5model \ model \ particles \ scenegraph \ shaders \ skins \ uimanager \ undo \ vfspk3 \ xmlregistry # Conditional modules SUBDIRS += @sound_module@ @script_module@ # Optional plugins SUBDIRS += @sr_plugin@ \ @obj_plugin@ \ @difficulty_plugin@ \ @conv_plugin@ \ @gui_plugin@ \ @editing_plugin@ DarkRadiant-2.5.0/plugins/archivezip/000077500000000000000000000000001321750546400175365ustar00rootroot00000000000000DarkRadiant-2.5.0/plugins/archivezip/DeflatedArchiveFile.h000066400000000000000000000020721321750546400235220ustar00rootroot00000000000000#pragma once #include "iarchive.h" #include "stream/FileInputStream.h" #include "DeflatedInputStream.h" namespace archive { class DeflatedArchiveFile : public ArchiveFile { private: std::string _name; stream::FileInputStream _istream; stream::SubFileInputStream _substream; // provides a subset of _istream DeflatedInputStream _zipstream; // inflates data from _subStream stream::FileInputStream::size_type _size; public: typedef stream::FileInputStream::size_type size_type; typedef stream::FileInputStream::position_type position_type; DeflatedArchiveFile(const std::string& name, const std::string& archiveName, // path to the ZIP file position_type position, size_type stream_size, size_type file_size) : _name(name), _istream(archiveName), _substream(_istream, position, stream_size), _zipstream(_substream), _size(file_size) {} size_type size() const override { return _size; } const std::string& getName() const override { return _name; } InputStream& getInputStream() override { return _zipstream; } }; } DarkRadiant-2.5.0/plugins/archivezip/DeflatedArchiveTextFile.h000066400000000000000000000031351321750546400243700ustar00rootroot00000000000000#pragma once #include "iarchive.h" #include "iregistry.h" #include "stream/BinaryToTextInputStream.h" namespace archive { /** * ArchiveFile stored in a ZIP in DEFLATE format. */ class DeflatedArchiveTextFile : public ArchiveTextFile { private: std::string _name; stream::FileInputStream _istream; stream::SubFileInputStream _substream; // reads subset of _istream DeflatedInputStream _zipstream; // inflates data from _substream stream::BinaryToTextInputStream _textStream; // converts data from _zipstream // Mod directory containing this file const std::string _modName; public: typedef stream::FileInputStream::size_type size_type; typedef stream::FileInputStream::position_type position_type; /** * Constructor. * * @param modDir * The name of the mod directory this file's archive is located in. */ DeflatedArchiveTextFile(const std::string& name, const std::string& archiveName, // full path to ZIP file const std::string& modName, position_type position, size_type stream_size) : _name(name), _istream(archiveName), _substream(_istream, position, stream_size), _zipstream(_substream), _textStream(_zipstream), _modName(modName) {} TextInputStream& getInputStream() override { return _textStream; } const std::string& getName() const override { return _name; } /** * Return mod directory of this file. */ std::string getModName() const override { return _modName; } }; } DarkRadiant-2.5.0/plugins/archivezip/DeflatedInputStream.cpp000066400000000000000000000020401321750546400241420ustar00rootroot00000000000000#include "DeflatedInputStream.h" #include namespace archive { DeflatedInputStream::DeflatedInputStream(InputStream& istream) : _istream(istream), _zipStream(new z_stream) { _zipStream->zalloc = 0; _zipStream->zfree = 0; _zipStream->opaque = 0; _zipStream->avail_in = 0; inflateInit2(_zipStream.get(), -MAX_WBITS); } DeflatedInputStream::~DeflatedInputStream() { inflateEnd(_zipStream.get()); } DeflatedInputStream::size_type DeflatedInputStream::read(byte_type* buffer, size_type length) { // Tell inflate() to load the data directly to the given buffer _zipStream->next_out = buffer; _zipStream->avail_out = static_cast(length); while (_zipStream->avail_out != 0) { if (_zipStream->avail_in == 0) { // Load some data from the wrapped buffer and point z_stream to it _zipStream->next_in = _buffer; _zipStream->avail_in = static_cast(_istream.read(_buffer, sizeof(_buffer))); } if (inflate(_zipStream.get(), Z_SYNC_FLUSH) != Z_OK) { break; } } return length - _zipStream->avail_out; } } DarkRadiant-2.5.0/plugins/archivezip/DeflatedInputStream.h000066400000000000000000000013311321750546400236110ustar00rootroot00000000000000#pragma once #include "idatastream.h" #include // Forward decl. struct z_stream_s; typedef z_stream_s z_stream; namespace archive { /// \brief A wrapper around an InputStream of data compressed with the zlib deflate algorithm. /// /// - Uses z_stream to decompress the data stream on the fly. /// - Uses a buffer to reduce the number of times the wrapped stream must be read. class DeflatedInputStream : public InputStream { private: InputStream& _istream; std::unique_ptr _zipStream; unsigned char _buffer[1024]; public: DeflatedInputStream(InputStream& istream); virtual ~DeflatedInputStream(); // InputStream implementation size_type read(byte_type* buffer, size_type length) override; }; } DarkRadiant-2.5.0/plugins/archivezip/GenericFileSystem.h000066400000000000000000000075731321750546400233040ustar00rootroot00000000000000#pragma once #include "iarchive.h" #include "string/string.h" #include "os/path.h" #include #include namespace archive { namespace { // Returns the depth of a given path string, basically counting // the number of parts in between forward slashes // "" => 0 // "dds/" => 1 // "dds/textures/" => 2 // "dds/textures/darkmod/" => 3 // "dds/textures/darkmod/wood/" => 4 // "dds/textures/darkmod/wood/boards/" => 5 // "dds/textures/darkmod/wood/boards/dark_rough.dds" => 6 inline unsigned int getPathDepth(const char* path) { unsigned int depth = 0; while (path != 0 && path[0] != '\0') { path = strchr(path, '/'); if (path != 0) { ++path; } ++depth; } return depth; } } /// \brief A generic unix-style file-system which maps paths to files and directories. /// Provides average O(log n) find and insert methods. /// \param file_type The data type which represents a file. template class GenericFileSystem { class Path { private: std::string _path; unsigned int _depth; public: Path(const std::string& path) : _path(path), _depth(getPathDepth(_path.c_str())) {} Path(const char* start, std::size_t length) : Path(std::string(start, length)) {} bool operator<(const Path& other) const { return string_less_nocase(c_str(), other.c_str()); } unsigned int depth() const { return _depth; } const char* c_str() const { return _path.c_str(); } const std::string& string() const { return _path; } }; public: class Entry { std::shared_ptr _record; public: Entry() {} Entry(const std::shared_ptr& record) : _record(record) {} std::shared_ptr& getRecord() { return _record; } bool isDirectory() const { return !_record; } }; private: typedef std::map Entries; Entries _entries; public: typedef typename Entries::iterator iterator; typedef typename Entries::value_type value_type; typedef Entry entry_type; iterator begin() { return _entries.begin(); } iterator end() { return _entries.end(); } void clear() { _entries.clear(); } /// \brief Returns the file at \p path. /// Creates all directories below \p path if they do not exist. /// O(log n) on average. entry_type& operator[](const Path& path) { const char* start = path.c_str(); const char* end = path_remove_directory(path.c_str()); while (end[0] != '\0') { // greebo: Take the substring from start to end Path dir(start, end - start); // And insert it as directory _entries.insert(value_type(dir, Entry())); end = path_remove_directory(end); } return _entries[path]; } /// \brief Returns the file at \p path or end() if not found. iterator find(const Path& path) { return _entries.find(path); } /// \brief Performs a depth-first traversal of the file-system subtree rooted at \p root. /// Traverses the entire tree if \p root is "". /// Calls \p visitor.file() with the path to each file relative to the filesystem root. /// Calls \p visitor.directory() with the path to each directory relative to the filesystem root. void traverse(Archive::Visitor& visitor, const std::string& root) { unsigned int start_depth = getPathDepth(root.c_str()); unsigned int skip_depth = 0; for (iterator i = begin(root); i != end() && i->first.depth() > start_depth; ++i) { if (i->first.depth() == skip_depth) { skip_depth = 0; } if (skip_depth == 0) { if (!i->second.isDirectory()) { visitor.visitFile(i->first.string()); } else if (visitor.visitDirectory(i->first.string(), i->first.depth() - start_depth)) { skip_depth = i->first.depth(); } } } } private: iterator begin(const std::string& root) { if (root.empty()) { return _entries.begin(); } iterator i = _entries.find(root); if (i == _entries.end()) { return i; } return ++i; } }; } DarkRadiant-2.5.0/plugins/archivezip/Makefile.am000066400000000000000000000004741321750546400215770ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs $(LIBSIGC_CFLAGS) modulesdir = $(pkglibdir)/modules modules_LTLIBRARIES = archivezip.la archivezip_la_LDFLAGS = -module -avoid-version $(Z_LIBS) $(LIBSIGC_LIBS) $(FILESYSTEM_LIBS) archivezip_la_SOURCES = ZipArchive.cpp plugin.cpp DeflatedInputStream.cpp DarkRadiant-2.5.0/plugins/archivezip/StoredArchiveFile.h000066400000000000000000000020071321750546400232500ustar00rootroot00000000000000#pragma once #include "iarchive.h" namespace archive { /// \brief An ArchiveFile which is stored uncompressed as part of a larger archive file. class StoredArchiveFile : public ArchiveFile { private: std::string _name; stream::FileInputStream _filestream; stream::SubFileInputStream _substream; // provides a subset of _filestream stream::FileInputStream::size_type _size; public: typedef stream::FileInputStream::size_type size_type; typedef stream::FileInputStream::position_type position_type; StoredArchiveFile(const std::string& name, const std::string& archiveName, // full path to the archive file position_type position, size_type stream_size, size_type file_size) : _name(name), _filestream(archiveName), _substream(_filestream, position, stream_size), _size(file_size) {} size_type size() const override { return _size; } const std::string& getName() const override { return _name; } InputStream& getInputStream() override { return _substream; } }; } DarkRadiant-2.5.0/plugins/archivezip/StoredArchiveTextFile.h000066400000000000000000000025041321750546400241170ustar00rootroot00000000000000#pragma once #include "iarchive.h" #include "stream/BinaryToTextInputStream.h" namespace archive { /// \brief An ArchiveTextFile which is stored uncompressed as part of a larger archive file. class StoredArchiveTextFile : public ArchiveTextFile { private: std::string _name; stream::FileInputStream _filestream; stream::SubFileInputStream _substream; // provides a subset of _filestream stream::BinaryToTextInputStream _textStream; // converts data from _substream // Mod directory std::string _modName; public: typedef stream::FileInputStream::size_type size_type; typedef stream::FileInputStream::position_type position_type; /** * Constructor. * * @param modDir * Name of the mod directory containing this file. */ StoredArchiveTextFile(const std::string& name, const std::string& archiveName, const std::string& modName, position_type position, size_type stream_size) : _name(name), _filestream(archiveName), _substream(_filestream, position, stream_size), _textStream(_substream), _modName(modName) {} const std::string& getName() const override { return _name; } TextInputStream& getInputStream() override { return _textStream; } /** * Return mod directory. */ std::string getModName() const override { return _modName; } }; } DarkRadiant-2.5.0/plugins/archivezip/ZipArchive.cpp000066400000000000000000000145341321750546400223150ustar00rootroot00000000000000#include "ZipArchive.h" #include #include "itextstream.h" #include "iarchive.h" #include "gamelib.h" #include #include "os/fs.h" #include "os/path.h" #include "ZipStreamUtils.h" #include "DeflatedArchiveFile.h" #include "DeflatedArchiveTextFile.h" #include "StoredArchiveFile.h" #include "StoredArchiveTextFile.h" namespace archive { // Thrown by the zip reader methods below class ZipFailureException : public std::runtime_error { public: ZipFailureException(const char* msg) : std::runtime_error(msg) {} }; ZipArchive::ZipArchive(const std::string& fullPath) : _fullPath(fullPath), _containingFolder(os::standardPathWithSlash(fs::path(_fullPath).remove_filename())), _modName(game::current::getModPath(_containingFolder)), _istream(_fullPath) { if (_istream.failed()) { rError() << "Cannot open Zip file stream: " << _fullPath << std::endl; return; } try { // Try loading the zip file, this will throw exceptoions on any problem loadZipFile(); } catch (ZipFailureException& ex) { rError() << "Cannot read Zip file " << _fullPath << ": " << ex.what() << std::endl; } } ZipArchive::~ZipArchive() { _filesystem.clear(); } ArchiveFilePtr ZipArchive::openFile(const std::string& name) { ZipFileSystem::iterator i = _filesystem.find(name); if (i != _filesystem.end() && !i->second.isDirectory()) { const std::shared_ptr& file = i->second.getRecord(); stream::FileInputStream::size_type position = 0; { // Guard against concurrent access std::lock_guard lock(_streamLock); _istream.seek(file->position); ZipFileHeader header; stream::readZipFileHeader(_istream, header); position = _istream.tell(); if (header.magic != ZIP_MAGIC_FILE_HEADER) { rError() << "Error reading zip file " << _fullPath << std::endl; return ArchiveFilePtr(); } } switch (file->mode) { case ZipRecord::eStored: return std::make_shared(name, _fullPath, position, file->stream_size, file->file_size); case ZipRecord::eDeflated: return std::make_shared(name, _fullPath, position, file->stream_size, file->file_size); } } return ArchiveFilePtr(); } ArchiveTextFilePtr ZipArchive::openTextFile(const std::string& name) { ZipFileSystem::iterator i = _filesystem.find(name); if (i != _filesystem.end() && !i->second.isDirectory()) { const std::shared_ptr& file = i->second.getRecord(); // Guard against concurrent access std::lock_guard lock(_streamLock); _istream.seek(file->position); ZipFileHeader header; stream::readZipFileHeader(_istream, header); if (header.magic != ZIP_MAGIC_FILE_HEADER) { rError() << "Error reading zip file " << _fullPath << std::endl; return ArchiveTextFilePtr(); } switch (file->mode) { case ZipRecord::eStored: return std::make_shared(name, _fullPath, _modName, _istream.tell(), file->stream_size); case ZipRecord::eDeflated: return std::make_shared(name, _fullPath, _modName, _istream.tell(), file->stream_size); } } return ArchiveTextFilePtr(); } bool ZipArchive::containsFile(const std::string& name) { ZipFileSystem::iterator i = _filesystem.find(name); return i != _filesystem.end() && !i->second.isDirectory(); } void ZipArchive::traverse(Visitor& visitor, const std::string& root) { _filesystem.traverse(visitor, root); } void ZipArchive::readZipRecord() { ZipMagic magic; stream::readZipMagic(_istream, magic); if (magic != ZIP_MAGIC_ROOT_DIR_ENTRY) { throw ZipFailureException("Invalid Zip directory entry magic"); } ZipVersion version_encoder; stream::readZipVersion(_istream, version_encoder); ZipVersion version_extract; stream::readZipVersion(_istream, version_extract); //unsigned short flags = stream::readLittleEndian(_istream); uint16_t compression_mode = stream::readLittleEndian(_istream); if (compression_mode != Z_DEFLATED && compression_mode != 0) { throw ZipFailureException("Unsupported compression mode"); } ZipDosTime dostime; stream::readZipDosTime(_istream, dostime); //unsigned int crc32 = stream::readLittleEndian(_istream); uint32_t compressed_size = stream::readLittleEndian(_istream); uint32_t uncompressed_size = stream::readLittleEndian(_istream); uint16_t namelength = stream::readLittleEndian(_istream); uint16_t extras = stream::readLittleEndian(_istream); uint16_t comment = stream::readLittleEndian(_istream); //unsigned short diskstart = stream::readLittleEndian(_istream); //unsigned short filetype = stream::readLittleEndian(_istream); //unsigned int filemode = stream::readLittleEndian(_istream); uint32_t position = stream::readLittleEndian(_istream); // greebo: Read the filename directly into a newly constructed std::string. // I'm not entirely happy about this brute-force casting, but I wanted to // avoid reading the filename into a temporary char[] array // only to let its contents end up being copied by the std::string anyway. // Alternative: use a static std::shared_array here, resized to fit? std::string path(namelength, '\0'); _istream.read( reinterpret_cast(const_cast(path.data())), namelength); _istream.seek(extras + comment, stream::FileInputStream::cur); if (os::isDirectory(path)) { _filesystem[path].getRecord().reset(); } else { ZipFileSystem::entry_type& entry = _filesystem[path]; if (!entry.isDirectory()) { rWarning() << "Zip archive " << _fullPath << " contains duplicated file: " << path << std::endl; } else { entry.getRecord().reset(new ZipRecord(position, compressed_size, uncompressed_size, (compression_mode == Z_DEFLATED) ? ZipRecord::eDeflated : ZipRecord::eStored)); } } } void ZipArchive::loadZipFile() { SeekableStream::position_type pos = findZipDiskTrailerPosition(_istream); if (pos == 0) { throw ZipFailureException("Unable to locate Zip disk trailer"); } _istream.seek(pos); ZipDiskTrailer trailer; stream::readZipDiskTrailer(_istream, trailer); if (trailer.magic != ZIP_MAGIC_DISK_TRAILER) { throw ZipFailureException("Invalid Zip Magic, maybe this is not a zip file?"); } _istream.seek(trailer.rootseek); for (unsigned short i = 0; i < trailer.entries; ++i) { readZipRecord(); } } } DarkRadiant-2.5.0/plugins/archivezip/ZipArchive.h000066400000000000000000000032371321750546400217600ustar00rootroot00000000000000#pragma once #include "iarchive.h" #include "GenericFileSystem.h" #include "stream/FileInputStream.h" #include namespace archive { /** * Archive adapter representing a PK4 (Zip) archive file. * * In idTech4 Virtual Filesystems this is the only other * Archive type allowed, next to the DirectoryArchives representing * physical directories. * * Archives are owned and instantiated by the GlobalFileSystem instance. */ class ZipArchive : public Archive { private: class ZipRecord { public: enum CompressionMode { eStored, eDeflated, }; ZipRecord(uint32_t position_, uint32_t compressed_size_, uint32_t uncompressed_size_, CompressionMode mode_) : position(position_), stream_size(compressed_size_), file_size(uncompressed_size_), mode(mode_) {} uint32_t position; uint32_t stream_size; uint32_t file_size; CompressionMode mode; }; typedef GenericFileSystem ZipFileSystem; ZipFileSystem _filesystem; std::string _fullPath; // the full path to the Zip file std::string _containingFolder; // the folder this Zip is located in std::string _modName; // mod name, calculated based on the containing folder stream::FileInputStream _istream; std::mutex _streamLock; public: ZipArchive(const std::string& fullPath); virtual ~ZipArchive(); // Archive implementation virtual ArchiveFilePtr openFile(const std::string& name) override; virtual ArchiveTextFilePtr openTextFile(const std::string& name) override; bool containsFile(const std::string& name) override; void traverse(Visitor& visitor, const std::string& root) override; private: void readZipRecord(); void loadZipFile(); }; } DarkRadiant-2.5.0/plugins/archivezip/ZipStreamUtils.h000066400000000000000000000233041321750546400226500ustar00rootroot00000000000000#pragma once #include "stream/utils.h" #include "idatastream.h" #include /** * greebo: Various data structures as defined by the PKWARE * ZIP File Format Specification, plus a few stream helper functions. */ namespace archive { class ZipMagic { public: ZipMagic() {} ZipMagic(char c0, char c1, char c2, char c3) { value[0] = c0; value[1] = c1; value[2] = c2; value[3] = c3; } bool operator==(const ZipMagic& other) const { return value[0] == other.value[0] && value[1] == other.value[1] && value[2] == other.value[2] && value[3] == other.value[3]; } bool operator!=(const ZipMagic& other) const { return !(*this == other); } char value[4]; }; const ZipMagic ZIP_MAGIC_FILE_HEADER('P', 'K', 0x03, 0x04); struct ZipVersion { char version; char ostype; }; struct ZipDosTime { uint16_t time; uint16_t date; }; /* A. Local file header */ struct ZipFileHeader { ZipMagic magic; /* local file header signature (0x04034b50) */ ZipVersion extract; /* version needed to extract */ uint16_t flags; /* general purpose bit flag */ uint16_t compressionMethod; /* compression method */ ZipDosTime dosTime; /* last mod file time (dos format) */ uint32_t crc32; /* crc-32 */ uint32_t compressedSize; /* compressed size */ uint32_t uncompressedSize; /* uncompressed size */ uint16_t nameLength; /* filename length (null if stdin) */ uint16_t extras; /* extra field length */ /* followed by filename (of variable size) */ /* followed by extra field (of variable size) */ }; /* B. data descriptor * the data descriptor exists only if bit 3 of z_flags is set. It is byte aligned * and immediately follows the last byte of compressed data. It is only used if * the output media of the compressor was not seekable, eg. standard output. */ const ZipMagic ZIP_MAGIC_FILE_TRAILER('P', 'K', 0x07, 0x08); struct ZipFileTrailer { ZipMagic magic; uint32_t crc32; uint32_t compressedSize; uint32_t uncompressedSize; }; /* C. central directory structure: [file header] . . . end of central dir record */ /* directory file header * - a single entry including filename, extras and comment may not exceed 64k. */ const ZipMagic ZIP_MAGIC_ROOT_DIR_ENTRY('P', 'K', 0x01, 0x02); struct ZipRootDirEntry { ZipMagic magic; ZipVersion encoder; /* version made by */ ZipVersion extract; /* version need to extract */ uint16_t flags; /* general purpose bit flag */ uint16_t compressionMethod; /* compression method */ ZipDosTime dostime; /* last mod file time&date (dos format) */ uint32_t crc32; /* crc-32 */ uint32_t compressedSize; /* compressed size */ uint32_t uncompressedSize; /* uncompressed size */ uint16_t nameLength; /* filename length (null if stdin) */ uint16_t extras; /* extra field length */ uint16_t comment; /* file comment length */ uint16_t diskstart; /* disk number of start (if spanning zip over multiple disks) */ uint16_t filetype; /* internal file attributes, bit0 = ascii */ uint32_t filemode; /* extrnal file attributes, eg. msdos attrib byte */ uint32_t offset; /* relative offset of local file header, seekval if singledisk */ /* followed by filename (of variable size) */ /* followed by extra field (of variable size) */ /* followed by file comment (of variable size) */ }; /* end of central dir record */ const ZipMagic ZIP_MAGIC_DISK_TRAILER('P', 'K', 0x05, 0x06); struct ZipDiskTrailer { ZipMagic magic; uint16_t disk; /* number of this disk */ uint16_t finaldisk; /* number of the disk with the start of the central dir */ uint16_t entries; /* total number of entries in the central dir on this disk */ uint16_t finalentries; /* total number of entries in the central dir */ uint32_t rootsize; /* size of the central directory */ uint32_t rootseek; /* offset of start of central directory with respect to * the starting disk number */ uint16_t comment; /* zipfile comment length */ /* followed by zipfile comment (of variable size) */ }; const std::size_t ZIP_DISK_TRAILER_LENGTH = 22; } // Convenience functions, reading Zip structures from an InputStream namespace stream { inline void readZipMagic(InputStream& stream, archive::ZipMagic& magic) { stream.read(reinterpret_cast(magic.value), 4); } inline void readZipVersion(InputStream& stream, archive::ZipVersion& version) { version.version = stream::readByte(stream); version.ostype = stream::readByte(stream); } inline void readZipDosTime(InputStream& stream, archive::ZipDosTime& dostime) { dostime.time = stream::readLittleEndian(stream); dostime.date = stream::readLittleEndian(stream); } inline void readZipFileHeader(SeekableInputStream& stream, archive::ZipFileHeader& header) { stream::readZipMagic(stream, header.magic); stream::readZipVersion(stream, header.extract); header.flags = stream::readLittleEndian(stream); header.compressionMethod = stream::readLittleEndian(stream); stream::readZipDosTime(stream, header.dosTime); header.crc32 = stream::readLittleEndian(stream); header.compressedSize = stream::readLittleEndian(stream); header.uncompressedSize = stream::readLittleEndian(stream); header.nameLength = stream::readLittleEndian(stream); header.extras = stream::readLittleEndian(stream); stream.seek(header.nameLength + header.extras, SeekableInputStream::cur); }; inline void readZipFileTrailer(InputStream& stream, archive::ZipFileTrailer& trailer) { stream::readZipMagic(stream, trailer.magic); trailer.crc32 = stream::readLittleEndian(stream); trailer.compressedSize = stream::readLittleEndian(stream); trailer.uncompressedSize = stream::readLittleEndian(stream); }; inline void readZipRootDirEntry(SeekableInputStream& stream, archive::ZipRootDirEntry& entry) { stream::readZipMagic(stream, entry.magic); stream::readZipVersion(stream, entry.encoder); stream::readZipVersion(stream, entry.extract); entry.flags = stream::readLittleEndian(stream); entry.compressionMethod = stream::readLittleEndian(stream); stream::readZipDosTime(stream, entry.dostime); entry.crc32 = stream::readLittleEndian(stream); entry.compressedSize = stream::readLittleEndian(stream); entry.uncompressedSize = stream::readLittleEndian(stream); entry.nameLength = stream::readLittleEndian(stream); entry.extras = stream::readLittleEndian(stream); entry.comment = stream::readLittleEndian(stream); entry.diskstart = stream::readLittleEndian(stream); entry.filetype = stream::readLittleEndian(stream); entry.filemode = stream::readLittleEndian(stream); entry.offset = stream::readLittleEndian(stream); stream.seek(entry.nameLength + entry.extras + entry.comment, SeekableInputStream::cur); } inline void readZipDiskTrailer(SeekableInputStream& stream, archive::ZipDiskTrailer& trailer) { stream::readZipMagic(stream, trailer.magic); trailer.disk = stream::readLittleEndian(stream); trailer.finaldisk = stream::readLittleEndian(stream); trailer.entries = stream::readLittleEndian(stream); trailer.finalentries = stream::readLittleEndian(stream); trailer.rootsize = stream::readLittleEndian(stream); trailer.rootseek = stream::readLittleEndian(stream); trailer.comment = stream::readLittleEndian(stream); stream.seek(trailer.comment, SeekableInputStream::cur); } } // namespace namespace archive { // Function trying to locate the trailer position in the given seekable stream. // Will return the position of the trailer structure, or 0 if it was unable to find it. inline SeekableStream::position_type findZipDiskTrailerPosition(SeekableInputStream& stream) { // Seek to the end of the file and check if the last 22 bytes match the Zip Disk Trailer stream.seek(0, SeekableInputStream::end); SeekableStream::position_type startPosition = stream.tell(); if (startPosition < ZIP_DISK_TRAILER_LENGTH) { return 0; // file is too small } startPosition -= ZIP_DISK_TRAILER_LENGTH; stream.seek(startPosition); ZipMagic magic; stream::readZipMagic(stream, magic); if (magic == ZIP_MAGIC_DISK_TRAILER) { // We got lucky and found the trailer right at the end of the file return startPosition; } // Trailer not found right at the end of the file. // Search for it, starting at the end of the file in backwards direction // ZIP comments havea 2-byte size descriptor, so the maximum size of the comment is 65k const SeekableStream::position_type maxCommentSize = 0x10000; // Allocate a buffer to hold the data to be searched const SeekableStream::position_type bufshift = 6; const SeekableStream::position_type bufsize = maxCommentSize >> bufshift; unsigned char buffer[bufsize]; // Mark the end searching point in the file SeekableStream::position_type searchEndPos = (maxCommentSize < startPosition) ? startPosition - maxCommentSize : 0; SeekableStream::position_type position = startPosition; while (position != searchEndPos) { StreamBase::size_type bytesToRead = std::min(bufsize, position - searchEndPos); position -= bytesToRead; // Go the current search position, load the data and search it stream.seek(position); StreamBase::size_type size = stream.read(buffer, bytesToRead); // Search for the magic value and return its position on success unsigned char* p = buffer + size; while (p != buffer) { --p; magic.value[3] = magic.value[2]; magic.value[2] = magic.value[1]; magic.value[1] = magic.value[0]; magic.value[0] = *p; if (magic == ZIP_MAGIC_DISK_TRAILER) { return position + (p - buffer); } } } // Zip magic not found return 0; } } // namespace DarkRadiant-2.5.0/plugins/archivezip/plugin.cpp000066400000000000000000000020441321750546400215400ustar00rootroot00000000000000#include "iarchive.h" #include "itextstream.h" #include "ZipArchive.h" namespace archive { class Pk4ArchiveLoader : public ArchiveLoader { public: // greebo: Returns the opened file or NULL if failed. virtual ArchivePtr openArchive(const std::string& name) override { return std::make_shared(name); } virtual const std::string& getExtension() override { static std::string _ext("pk4"); return _ext; } // RegisterableModule implementation const std::string& getName() const override { static std::string _name(MODULE_ARCHIVE + "PK4"); return _name; } const StringSet& getDependencies() const override { static StringSet _dependencies; return _dependencies; } void initialiseModule(const ApplicationContext& ctx) override { rMessage() << getName() << "::initialiseModule called" << std::endl; } }; } extern "C" void DARKRADIANT_DLLEXPORT RegisterModule(IModuleRegistry& registry) { module::performDefaultInitialisation(registry); registry.registerModule(std::make_shared()); } DarkRadiant-2.5.0/plugins/commandsystem/000077500000000000000000000000001321750546400202555ustar00rootroot00000000000000DarkRadiant-2.5.0/plugins/commandsystem/CaseInsensitiveCompare.h000066400000000000000000000007421321750546400250340ustar00rootroot00000000000000#pragma once #include #include #include "string/string.h" namespace cmd { /** * Compare-functor to allow case-insensitive lookups of commands. */ struct CaseInsensitiveCompare : public std::binary_function { bool operator()(const std::string &s1, const std::string &s2) const { //return boost::algorithm::ilexicographical_compare(s1, s2); return string_compare_nocase(s1.c_str(), s2.c_str()) < 0; } }; } // namespace cmd DarkRadiant-2.5.0/plugins/commandsystem/Command.h000066400000000000000000000034511321750546400220070ustar00rootroot00000000000000#ifndef _COMMAND_H_ #define _COMMAND_H_ #include "itextstream.h" #include "Executable.h" namespace cmd { class Command : public Executable { // The actual function to call Function _function; // The number and types of arguments to use Signature _signature; public: Command(const Function& function, const Signature& signature) : _function(function), _signature(signature) {} Signature getSignature() { return _signature; } virtual void execute(const ArgumentList& args) { // Check arguments if (_signature.size() < args.size()) { // Too many arguments, that's for sure rError() << "Cannot execute command: Too many arguments. " << "(max. " << _signature.size() << " arguments required)" << std::endl; return; } // Check matching arguments ArgumentList::const_iterator arg = args.begin(); for (Signature::const_iterator cur = _signature.begin(); cur != _signature.end(); ++cur) { std::size_t curFlags = *cur; bool curIsOptional = ((curFlags & ARGTYPE_OPTIONAL) != 0); // If arguments have run out, all remaining parts of the signature must be optional if (arg == args.end()) { // Non-optional arguments will cause an error if (!curIsOptional) { rError() << "Cannot execute command: Missing arguments. " << std::endl; return; } } else { // We have incoming arguments to match our signature if ((curFlags & arg->getType()) == 0) { // Type mismatch rError() << "Cannot execute command: Type mismatch at argument: " << arg->getString() << std::endl; return; } } // Increase argument iterator if possible if (arg != args.end()) { ++arg; } } // Checks passed, call the command _function(args); } }; typedef std::shared_ptr CommandPtr; } // namespace cmd #endif /* _COMMAND_H_ */ DarkRadiant-2.5.0/plugins/commandsystem/CommandSystem.cpp000066400000000000000000000242261321750546400235520ustar00rootroot00000000000000#include "CommandSystem.h" #include "itextstream.h" #include "iregistry.h" #include "ieventmanager.h" #include "debugging/debugging.h" #include "CommandTokeniser.h" #include "Command.h" #include "Statement.h" #include #include "string/trim.h" #include "string/predicate.h" namespace cmd { namespace { const std::string RKEY_COMMANDSYSTEM_BINDS = "user/ui/commandsystem/binds"; } // RegisterableModule implementation const std::string& CommandSystem::getName() const { static std::string _name(MODULE_COMMANDSYSTEM); return _name; } const StringSet& CommandSystem::getDependencies() const { static StringSet _dependencies; if (_dependencies.empty()) { _dependencies.insert(MODULE_XMLREGISTRY); } return _dependencies; } void CommandSystem::initialiseModule(const ApplicationContext& ctx) { rMessage() << "CommandSystem::initialiseModule called." << std::endl; // Add the built-in commands addCommand("bind", std::bind(&CommandSystem::bindCmd, this, std::placeholders::_1), Signature(ARGTYPE_STRING, ARGTYPE_STRING)); addCommand("unbind", std::bind(&CommandSystem::unbindCmd, this, std::placeholders::_1), ARGTYPE_STRING); addCommand("listCmds", std::bind(&CommandSystem::listCmds, this, std::placeholders::_1)); addCommand("print", std::bind(&CommandSystem::printCmd, this, std::placeholders::_1), ARGTYPE_STRING); loadBinds(); } void CommandSystem::shutdownModule() { rMessage() << "CommandSystem: shutting down." << std::endl; // Save binds to registry saveBinds(); // Free all commands _commands.clear(); } void CommandSystem::printCmd(const ArgumentList& args) { for (ArgumentList::const_iterator i = args.begin(); i != args.end(); ++i) { rMessage() << i->getString() << (i != args.begin() ? " " : ""); } rMessage() << std::endl; } void CommandSystem::loadBinds() { // Find all accelerators xml::NodeList nodeList = GlobalRegistry().findXPath(RKEY_COMMANDSYSTEM_BINDS + "//bind"); if (nodeList.empty()) { return; } // Load all bind commands from the nodes for (std::size_t i = 0; i < nodeList.size(); ++i) { xml::Node& node = nodeList[i]; std::string name = node.getAttributeValue("name"); std::string statement = node.getAttributeValue("value"); // remove all whitespace from the front and the tail string::trim(statement); // Create a new statement StatementPtr st(new Statement( statement, (node.getAttributeValue("readonly") == "1") )); std::pair result = _commands.insert( CommandMap::value_type(name, st) ); if (!result.second) { rWarning() << "Duplicate statement detected: " << name << std::endl; } } } void CommandSystem::saveBinds() { // Delete all previous binds GlobalRegistry().deleteXPath(RKEY_COMMANDSYSTEM_BINDS + "//bind"); for (CommandMap::const_iterator i = _commands.begin(); i != _commands.end(); ++i) { // Check if this is actually a statement StatementPtr st = std::dynamic_pointer_cast(i->second); if (st == NULL || st->isReadonly()) continue; // not a statement or readonly xml::Node node = GlobalRegistry().createKeyWithName(RKEY_COMMANDSYSTEM_BINDS, "bind", i->first); node.setAttributeValue("value", st->getValue()); } } void CommandSystem::bindCmd(const ArgumentList& args) { // Sanity check if (args.size() != 2) return; // First argument is the command name // Second argument is the command to be called plus its arguments // Use a tokeniser to split the second argument into its pieces // and trim all unnecessary whitespace std::string input = args[1].getString(); // Add the statement - bind complete addStatement(args[0].getString(), input); // To enable this statement to be triggered via UI, register it as new event GlobalEventManager().addCommand(args[0].getString(), args[0].getString()); } void CommandSystem::unbindCmd(const ArgumentList& args) { // Sanity check if (args.size() != 1) return; // First argument is the statement to unbind CommandMap::iterator found = _commands.find(args[0].getString()); if (found == _commands.end()) { rError() << "Cannot unbind: " << args[0].getString() << ": no such command." << std::endl; return; } // Check if this is actually a statement StatementPtr st = std::dynamic_pointer_cast(found->second); if (st != NULL && !st->isReadonly()) { // This is a user-statement _commands.erase(found); // Unregister this as event too GlobalEventManager().removeEvent(args[0].getString()); } else { rError() << "Cannot unbind built-in command: " << args[0].getString() << std::endl; return; } } void CommandSystem::listCmds(const ArgumentList& args) { // Dump all commands for (CommandMap::const_iterator i = _commands.begin(); i != _commands.end(); ++i) { rMessage() << i->first; StatementPtr st = std::dynamic_pointer_cast(i->second); if (st != NULL) { rMessage() << " => " << st->getValue(); } rMessage() << std::endl; } } void CommandSystem::foreachCommand(const std::function& functor) { std::for_each(_commands.begin(), _commands.end(), [&] (const CommandMap::value_type& i) { functor(i.first); }); } void CommandSystem::addCommand(const std::string& name, Function func, const Signature& signature) { // Create a new command CommandPtr cmd(new Command(func, signature)); std::pair result = _commands.insert( CommandMap::value_type(name, cmd) ); if (!result.second) { rError() << "Cannot register command " << name << ", this command is already registered." << std::endl; } } void CommandSystem::removeCommand(const std::string& name) { CommandMap::iterator i = _commands.find(name); if (i != _commands.end()) { _commands.erase(i); } } void CommandSystem::addStatement(const std::string& statementName, const std::string& str, bool saveStatementToRegistry) { // Remove all whitespace at the front and the tail StatementPtr st(new Statement( string::trim_copy(str), !saveStatementToRegistry // read-only if we should not save this statement )); std::pair result = _commands.insert( CommandMap::value_type(statementName, st) ); if (!result.second) { rError() << "Cannot register statement " << statementName << ", this statement is already registered." << std::endl; } } void CommandSystem::foreachStatement(const std::function& functor, bool customStatementsOnly) { std::for_each(_commands.begin(), _commands.end(), [&] (const CommandMap::value_type& i) { StatementPtr statement = std::dynamic_pointer_cast(i.second); if (statement && (!customStatementsOnly || !statement->isReadonly())) { functor(i.first); } }); } Signature CommandSystem::getSignature(const std::string& name) { // Lookup the named command CommandMap::iterator i = _commands.find(name); if (i == _commands.end()) { return Signature(); // not found => empty signature } return i->second->getSignature(); } namespace local { // A statement consists of a command and a set of arguments struct Statement { // The command to invoke std::string command; // The arguments to pass ArgumentList args; }; } void CommandSystem::execute(const std::string& input) { // Instantiate a CommandTokeniser to analyse the given input string CommandTokeniser tokeniser(input); if (!tokeniser.hasMoreTokens()) return; // nothing to do! std::vector statements; local::Statement curStatement; while (tokeniser.hasMoreTokens()) { // Inspect the next token std::string token = tokeniser.nextToken(); if (token.empty()) { continue; // skip empty tokens } else if (token == ";") { // Finish the current statement if (!curStatement.command.empty()) { // Add the non-empty statement to our list statements.push_back(curStatement); } // Clear the statement curStatement = local::Statement(); continue; } // Token is not a semicolon else if (curStatement.command.empty()) { // The statement is still without command name, take this one curStatement.command = token; continue; } else { // Non-empty token, command name is already known, so // this must be an argument curStatement.args.push_back(token); } } // Check if we have an unfinished statement if (!curStatement.command.empty()) { // Add the non-empty statement to our list statements.push_back(curStatement); } // Now execute the statements for (std::vector::iterator i = statements.begin(); i != statements.end(); ++i) { // Attempt ordinary command execution executeCommand(i->command, i->args); } } void CommandSystem::executeCommand(const std::string& name) { executeCommand(name, ArgumentList()); } void CommandSystem::executeCommand(const std::string& name, const Argument& arg1) { ArgumentList args(1); args[0] = arg1; executeCommand(name, args); } void CommandSystem::executeCommand(const std::string& name, const Argument& arg1, const Argument& arg2) { ArgumentList args(2); args[0] = arg1; args[1] = arg2; executeCommand(name, args); } void CommandSystem::executeCommand(const std::string& name, const Argument& arg1, const Argument& arg2, const Argument& arg3) { ArgumentList args(2); args[0] = arg1; args[1] = arg2; args[2] = arg3; executeCommand(name, args); } void CommandSystem::executeCommand(const std::string& name, const ArgumentList& args) { // Find the named command CommandMap::const_iterator i = _commands.find(name); if (i == _commands.end()) { rError() << "Cannot execute command " << name << ": Command not found." << std::endl; return; } i->second->execute(args); } AutoCompletionInfo CommandSystem::getAutoCompletionInfo(const std::string& prefix) { AutoCompletionInfo returnValue; returnValue.prefix = prefix; for (CommandMap::const_iterator i = _commands.begin(); i != _commands.end(); ++i) { // Check if the command matches the given prefix if (string::istarts_with(i->first, prefix)) { returnValue.candidates.push_back(i->first); } } return returnValue; } } // namespace cmd extern "C" void DARKRADIANT_DLLEXPORT RegisterModule(IModuleRegistry& registry) { module::performDefaultInitialisation(registry); registry.registerModule(cmd::CommandSystemPtr(new cmd::CommandSystem)); } DarkRadiant-2.5.0/plugins/commandsystem/CommandSystem.h000066400000000000000000000041331321750546400232120ustar00rootroot00000000000000#pragma once #include "icommandsystem.h" #include #include "CaseInsensitiveCompare.h" #include "Executable.h" namespace cmd { class CommandSystem : public ICommandSystem { // The named executables (case-insensitive lookup) typedef std::map CommandMap; CommandMap _commands; public: void foreachCommand(const std::function& functor); void addCommand(const std::string& name, Function func, const Signature& signature = Signature()); void removeCommand(const std::string& name); void addStatement(const std::string& statementName, const std::string& string, bool saveStatementToRegistry = true); void foreachStatement(const std::function& functor, bool customStatementsOnly); // Retrieve the signature for the given command Signature getSignature(const std::string& name); // Execute the given command sequence void execute(const std::string& input); void executeCommand(const std::string& name); void executeCommand(const std::string& name, const Argument& arg1); void executeCommand(const std::string& name, const Argument& arg1, const Argument& arg2); void executeCommand(const std::string& name, const Argument& arg1, const Argument& arg2, const Argument& arg3); // For more than 3 arguments, use this method to pass a vector of arguments void executeCommand(const std::string& name, const ArgumentList& args); // Get autocompletion suggestions for the given prefix AutoCompletionInfo getAutoCompletionInfo(const std::string& prefix); // The "bind" command void bindCmd(const ArgumentList& args); void unbindCmd(const ArgumentList& args); void listCmds(const ArgumentList& args); void printCmd(const ArgumentList& args); // RegisterableModule implementation const std::string& getName() const; const StringSet& getDependencies() const; void initialiseModule(const ApplicationContext& ctx); void shutdownModule(); private: // Save/load bind strings from Registry void loadBinds(); void saveBinds(); }; typedef std::shared_ptr CommandSystemPtr; } // namespace cmd DarkRadiant-2.5.0/plugins/commandsystem/CommandTokeniser.h000066400000000000000000000142421321750546400236730ustar00rootroot00000000000000#pragma once #include "parser/Tokeniser.h" namespace cmd { class CommandTokeniserFunc { // Enumeration of states enum States { SEARCHING, // haven't found anything yet TOKEN_STARTED, // found the start of a possible multi-char token DOUBLEQUOTE, // inside double-quoted text, no tokenising SINGLEQUOTE, // Inside single-quoted text, no tokenising } _state; // List of delimiters to skip const char* _delims; // Test if a character is a delimiter bool isDelim(char c) { const char* curDelim = _delims; while (*curDelim != 0) { if (*(curDelim++) == c) { return true; } } return false; } public: // Constructor CommandTokeniserFunc(const char* delims) : _state(SEARCHING), _delims(delims) {} /* REQUIRED. Operator() is called by the tokeniser. This function * must search for a token between the two iterators next and end, and if * a token is found, set tok to the token, set next to position to start * parsing on the next call, and return true. */ template bool operator() (InputIterator& next, const InputIterator& end, std::string& tok) { // Initialise state, no persistence between calls _state = SEARCHING; // Clear out the token, no guarantee that it is empty tok = ""; while (next != end) { switch (_state) { case SEARCHING: // If we have a delimiter, just advance to the next character if (isDelim(*next)) { ++next; continue; } // If we have a KEPT delimiter, this is the token to return. if (*next == ';') { tok = *(next++); return true; } // Otherwise fall through into TOKEN_STARTED, saving the state for the // next iteration _state = TOKEN_STARTED; case TOKEN_STARTED: // Here a delimiter indicates a successful token match if (isDelim(*next) || *next == ';') { return true; } // Now next is pointing at a non-delimiter. Switch on this // character. switch (*next) { // Found a quote, enter DOUBLEQUOTE state, or return the // current token if we are in the process of building // one. case '\"': if (tok != "") { return true; } else { _state = DOUBLEQUOTE; ++next; continue; // skip the quote } // Found a quote, enter SINGLEQUOTE state, or return the // current token if we are in the process of building // one. case '\'': if (tok != "") { return true; } else { _state = SINGLEQUOTE; ++next; continue; // skip the quote } // General case. Token lasts until next delimiter. default: tok += *next; ++next; continue; } case DOUBLEQUOTE: // In the quoted state, just advance until the closing // quote. No delimiter splitting is required. if (*next == '\"') { ++next; return true; } else { tok += *next; ++next; continue; } case SINGLEQUOTE: // In the quoted state, just advance until the closing // quote. No delimiter splitting is required. if (*next == '\'') { ++next; return true; } else { tok += *next; ++next; continue; } } // end of state switch } // end of for loop // Return true if we have added anything to the token return (tok != ""); } }; /** * greebo: A Command Tokeniser splits the given input strings into * pieces, delimited by whitespace characters. The tokeniser is respecting * quoted content, which will be treated as one string token (excluding the actual quotes). */ class CommandTokeniser : public parser::StringTokeniser { typedef string::Tokeniser Tokeniser; Tokeniser _tok; Tokeniser::Iterator _tokIter; public: CommandTokeniser(const std::string& str) : _tok(str, CommandTokeniserFunc(" \n\t\v\r")), _tokIter(_tok.getIterator()) {} // Documentation: see base class bool hasMoreTokens() override { return !_tokIter.isExhausted(); } // Documentation: see base class std::string nextToken() override { if (hasMoreTokens()) { return *(_tokIter++); } throw parser::ParseException("CommandTokeniser: no more tokens"); } void assertNextToken(const std::string& val) override { const std::string tok = nextToken(); if (tok != val) throw parser::ParseException("CommandTokeniser: Assertion failed: Required \"" + val + "\", found \"" + tok + "\""); } void skipTokens(unsigned int n) override { for (unsigned int i = 0; i < n; i++) { if (hasMoreTokens()) { _tokIter++; } throw parser::ParseException("CommandTokeniser: no more tokens"); } } }; } // namespace cmd DarkRadiant-2.5.0/plugins/commandsystem/Executable.h000066400000000000000000000010411321750546400225030ustar00rootroot00000000000000#ifndef _EXECUTABLE_H_ #define _EXECUTABLE_H_ #include "icommandsystem.h" #include namespace cmd { class Executable { public: /** * Destructor */ virtual ~Executable() {} /** * greebo: Execute this object with the given set of arguments. */ virtual void execute(const ArgumentList& args) = 0; /** * Returns the function signature (argumen types) of this executable. */ virtual Signature getSignature() = 0; }; typedef std::shared_ptr ExecutablePtr; } // namespace cmd #endif /* _EXECUTABLE_H_ */ DarkRadiant-2.5.0/plugins/commandsystem/Makefile.am000066400000000000000000000006461321750546400223170ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs \ $(XML_CFLAGS) $(LIBSIGC_CFLAGS) modulesdir = $(pkglibdir)/modules modules_LTLIBRARIES = commandsystem.la commandsystem_la_LDFLAGS = -module -avoid-version commandsystem_la_LIBADD = $(top_builddir)/libs/xmlutil/libxmlutil.la \ $(XML_LIBS) \ $(LIBSIGC_LIBS) commandsystem_la_SOURCES = CommandSystem.cpp DarkRadiant-2.5.0/plugins/commandsystem/Statement.h000066400000000000000000000016251321750546400223760ustar00rootroot00000000000000#ifndef _STATEMENT_H_ #define _STATEMENT_H_ #include "itextstream.h" #include "Executable.h" namespace cmd { class Statement : public Executable { // The input string to execute std::string _string; // Whether this statement is a default one (won't be saved or deleted) bool _isReadOnly; public: Statement(const std::string& str, bool isReadOnly = false) : _string(str), _isReadOnly(isReadOnly) {} virtual void execute(const ArgumentList& args) { // Execution means parsing another string GlobalCommandSystem().execute(_string); } Signature getSignature() { return Signature(); // signature is always empty } const std::string& getValue() const { return _string; } // Whether this statement is a default one (won't be saved or deleted) bool isReadonly() { return _isReadOnly; } }; typedef std::shared_ptr StatementPtr; } // namespace cmd #endif /* _STATEMENT_H_ */ DarkRadiant-2.5.0/plugins/dm.conversation/000077500000000000000000000000001321750546400205035ustar00rootroot00000000000000DarkRadiant-2.5.0/plugins/dm.conversation/ActorNodeFinder.h000066400000000000000000000013621321750546400236640ustar00rootroot00000000000000#pragma once #include "iscenegraph.h" #include "ientity.h" namespace scene { // Tries to locate the named actor in the current map class ActorNodeFinder : public scene::NodeVisitor { private: std::string _actorName; scene::INodePtr _foundNode; public: ActorNodeFinder(const std::string& actorName) : _actorName(actorName) {} bool pre(const scene::INodePtr& node) { if (_foundNode) return false; // we've already found what we're looking for Entity* entity = Node_getEntity(node); if (entity == nullptr) return true; // Found an entity, compare names if (entity->getKeyValue("name") == _actorName) { _foundNode = node; } return false; } const scene::INodePtr& getFoundNode() const { return _foundNode; } }; } DarkRadiant-2.5.0/plugins/dm.conversation/CommandArgumentItem.cpp000066400000000000000000000152431321750546400251140ustar00rootroot00000000000000#include "CommandArgumentItem.h" #include "ianimationchooser.h" #include "idialogmanager.h" #include "iresourcechooser.h" #include "iuimanager.h" #include "i18n.h" #include "string/convert.h" #include #include #include #include #include #include #include #include #include "wxutil/ChoiceHelper.h" #include #include "CommandEditor.h" #include "ActorNodeFinder.h" namespace ui { namespace { const char* const FOLDER_ICON = "folder16.png"; } CommandArgumentItem::CommandArgumentItem(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo) : _owner(owner), _argInfo(argInfo) { // Pack the label into an eventbox _labelBox = new wxStaticText(parent, wxID_ANY, _argInfo.title + ":"); _labelBox->SetToolTip(argInfo.description); // Pack the description widget into an eventbox _descBox = new wxStaticText(parent, wxID_ANY, "?"); _descBox->SetFont(_descBox->GetFont().Bold()); _descBox->SetToolTip(argInfo.description); } // Retrieve the label widget wxWindow* CommandArgumentItem::getLabelWidget() { return _labelBox; } wxWindow* CommandArgumentItem::getHelpWidget() { return _descBox; } // StringArgument StringArgument::StringArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo) : CommandArgumentItem(owner, parent, argInfo) { _entry = new wxTextCtrl(parent, wxID_ANY); } wxWindow* StringArgument::getEditWidget() { return _entry; } std::string StringArgument::getValue() { return _entry->GetValue().ToStdString(); } void StringArgument::setValueFromString(const std::string& value) { _entry->SetValue(value); } // Boolean argument BooleanArgument::BooleanArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo) : CommandArgumentItem(owner, parent, argInfo) { _checkButton = new wxCheckBox(parent, wxID_ANY, argInfo.title); } wxWindow* BooleanArgument::getEditWidget() { return _checkButton; } std::string BooleanArgument::getValue() { return _checkButton->GetValue() ? "1" : ""; } void BooleanArgument::setValueFromString(const std::string& value) { _checkButton->SetValue(value == "1"); } // Actor Argument ActorArgument::ActorArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo, const conversation::Conversation::ActorMap& actors) : CommandArgumentItem(owner, parent, argInfo) { _comboBox = new wxChoice(parent, wxID_ANY); // Fill the actor list conversation::Conversation::ActorMap dummy = actors; for (conversation::Conversation::ActorMap::const_iterator i = dummy.begin(); i != dummy.end(); ++i) { std::string actorStr = fmt::format(_("Actor {0:d} ({1})"), i->first, i->second); // Store the actor ID into a client data object and pass it along _comboBox->Append(actorStr, new wxStringClientData(string::to_string(i->first))); } } std::string ActorArgument::getValue() { return string::to_string(wxutil::ChoiceHelper::GetSelectionId(_comboBox)); } void ActorArgument::setValueFromString(const std::string& value) { wxutil::ChoiceHelper::SelectItemByStoredId(_comboBox, string::convert(value, wxNOT_FOUND)); } wxWindow* ActorArgument::getEditWidget() { return _comboBox; } SoundShaderArgument::SoundShaderArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo) : StringArgument(owner, parent, argInfo) { _soundShaderPanel = new wxPanel(parent); wxBoxSizer* shaderHBox = new wxBoxSizer(wxHORIZONTAL); _soundShaderPanel->SetSizer(shaderHBox); _entry->SetMinSize(wxSize(100, -1)); _entry->Reparent(_soundShaderPanel); shaderHBox->Add(_entry, 1, wxEXPAND); // Create the icon button to open the ShaderChooser wxButton* selectShaderButton = new wxBitmapButton(_soundShaderPanel, wxID_ANY, wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + FOLDER_ICON)); selectShaderButton->SetToolTip(_("Browse Sound Shaders")); selectShaderButton->Bind(wxEVT_BUTTON, [&](wxCommandEvent& ev) { pickSoundShader(); }); shaderHBox->Add(selectShaderButton, 0, wxLEFT, 6); } wxWindow* SoundShaderArgument::getEditWidget() { return _soundShaderPanel; } std::string SoundShaderArgument::getValue() { return _entry->GetValue().ToStdString(); } void SoundShaderArgument::setValueFromString(const std::string& value) { _entry->SetValue(value); } void SoundShaderArgument::pickSoundShader() { IResourceChooser* chooser = GlobalDialogManager().createSoundShaderChooser(wxGetTopLevelParent(_entry)); std::string picked = chooser->chooseResource(getValue()); if (!picked.empty()) { setValueFromString(picked); } chooser->destroyDialog(); } AnimationArgument::AnimationArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo) : StringArgument(owner, parent, argInfo) { _animPanel = new wxPanel(parent); wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL); _animPanel->SetSizer(hbox); _entry->SetMinSize(wxSize(100, -1)); _entry->Reparent(_animPanel); hbox->Add(_entry, 1, wxEXPAND); // Create the icon button to open the wxButton* selectButton = new wxBitmapButton(_animPanel, wxID_ANY, wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + FOLDER_ICON)); selectButton->SetToolTip(_("Browse Animations")); selectButton->Bind(wxEVT_BUTTON, [&](wxCommandEvent& ev) { pickAnimation(); }); hbox->Add(selectButton, 0, wxLEFT, 6); } wxWindow* AnimationArgument::getEditWidget() { return _animPanel; } std::string AnimationArgument::getValue() { return _entry->GetValue().ToStdString(); } void AnimationArgument::setValueFromString(const std::string& value) { _entry->SetValue(value); } void AnimationArgument::pickAnimation() { // Find out which actor we're talking about int actorId = _owner.getCommand().actor; std::string preselectModel = std::string(); if (actorId != -1 && _owner.getConversation().actors.find(actorId) != _owner.getConversation().actors.end()) { std::string actorName = _owner.getConversation().actors.find(actorId)->second; // Try to find the entity in the current map scene::ActorNodeFinder finder(actorName); GlobalSceneGraph().root()->traverse(finder); if (finder.getFoundNode()) { // Found the corresponding entity, get the model name Entity* entity = Node_getEntity(finder.getFoundNode()); assert(entity != nullptr); preselectModel = entity->getKeyValue("model"); } } IAnimationChooser* chooser = GlobalDialogManager().createAnimationChooser(wxGetTopLevelParent(_entry)); IAnimationChooser::Result result = chooser->runDialog(preselectModel, getValue()); if (!result.cancelled()) { setValueFromString(result.anim); } chooser->destroyDialog(); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.conversation/CommandArgumentItem.h000066400000000000000000000077511321750546400245660ustar00rootroot00000000000000#pragma once #include "ConversationCommandInfo.h" #include "Conversation.h" #include class wxWindow; class wxStaticText; class wxTextCtrl; class wxCheckBox; class wxChoice; class wxPanel; namespace ui { class CommandEditor; class CommandArgumentItem { protected: // The reference to the editor instance, some arguments need this // for cross-referencing actors and commands CommandEditor& _owner; // The argument this row is referring to const conversation::ArgumentInfo& _argInfo; wxStaticText* _labelBox; wxStaticText* _descBox; public: CommandArgumentItem(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo); // destructor virtual ~CommandArgumentItem() {} /** * greebo: This retrieves the string representation of the current * value of this row. This has to be implemented by the subclasses. */ virtual std::string getValue() = 0; /** * greebo: Loads the given value into the widgets, passed in string form. */ virtual void setValueFromString(const std::string& value) = 0; // Retrieve the label widget virtual wxWindow* getLabelWidget(); // Retrieve the edit widgets (abstract) virtual wxWindow* getEditWidget() = 0; // Retrieves the help widget (a question mark with a tooltip) virtual wxWindow* getHelpWidget(); }; typedef std::shared_ptr CommandArgumentItemPtr; /** * greebo: This is an item querying a simple string */ class StringArgument : public CommandArgumentItem { protected: wxTextCtrl* _entry; public: StringArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo); virtual wxWindow* getEditWidget(); virtual std::string getValue(); virtual void setValueFromString(const std::string& value); }; /** * greebo: This is an item querying a float (derives from string) */ class FloatArgument : public StringArgument { public: FloatArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo) : StringArgument(owner, parent, argInfo) {} }; /** * greebo: This is an item querying a vector (derives from string) */ class VectorArgument : public StringArgument { public: VectorArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo) : StringArgument(owner, parent, argInfo) {} }; class BooleanArgument : public CommandArgumentItem { protected: wxCheckBox* _checkButton; public: BooleanArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo); virtual wxWindow* getEditWidget(); virtual std::string getValue(); virtual void setValueFromString(const std::string& value); }; /** * greebo: This is an item querying an actor (dropdown combo) */ class ActorArgument : public CommandArgumentItem { protected: wxChoice* _comboBox; public: // Pass the reference to the helper class ActorArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo, const conversation::Conversation::ActorMap& actors); virtual wxWindow* getEditWidget(); virtual std::string getValue(); virtual void setValueFromString(const std::string& value); }; /** * greebo: This is an item querying a sound shader */ class SoundShaderArgument : public StringArgument { private: wxPanel* _soundShaderPanel; public: SoundShaderArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo); virtual wxWindow* getEditWidget() override; virtual std::string getValue() override; virtual void setValueFromString(const std::string& value) override; private: void pickSoundShader(); }; /** * greebo: This is an item querying an animation */ class AnimationArgument : public StringArgument { private: wxPanel* _animPanel; public: AnimationArgument(CommandEditor& owner, wxWindow* parent, const conversation::ArgumentInfo& argInfo); virtual wxWindow* getEditWidget() override; virtual std::string getValue() override; virtual void setValueFromString(const std::string& value) override; private: void pickAnimation(); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.conversation/CommandEditor.cpp000066400000000000000000000230021321750546400237310ustar00rootroot00000000000000#include "CommandEditor.h" #include "i18n.h" #include "string/string.h" #include "string/convert.h" #include #include "itextstream.h" #include #include #include #include #include #include "wxutil/ChoiceHelper.h" #include "ConversationCommandLibrary.h" namespace ui { namespace { const char* const WINDOW_TITLE = N_("Edit Command"); } CommandEditor::CommandEditor(wxWindow* parent, conversation::ConversationCommand& command, const conversation::Conversation& conv) : DialogBase(_(WINDOW_TITLE), parent), _conversation(conv), _command(command), // copy the conversation command to a local object _targetCommand(command) { // Create all widgets populateWindow(); // Fill the actor dropdown for (conversation::Conversation::ActorMap::const_iterator i = _conversation.actors.begin(); i != _conversation.actors.end(); ++i) { std::string actorStr = fmt::format(_("Actor {0:d} ({1})"), i->first, i->second); // Store the actor ID into a client data object and pass it along findNamedObject(this, "ConvCmdEditorActorChoice")->Append(actorStr, new wxStringClientData(string::to_string(i->first))); } // Let the command library fill the command dropdown conversation::ConversationCommandLibrary::Instance().populateChoice( findNamedObject(this, "ConvCmdEditorCommandChoice")); // Fill the values updateWidgets(); } conversation::ConversationCommand& CommandEditor::getCommand() { return _command; } const conversation::Conversation& CommandEditor::getConversation() { return _conversation; } void CommandEditor::updateWidgets() { // Select the actor passed from the command wxutil::ChoiceHelper::SelectItemByStoredId( findNamedObject(this, "ConvCmdEditorActorChoice"), _command.actor); // Select the command type wxutil::ChoiceHelper::SelectItemByStoredId( findNamedObject(this, "ConvCmdEditorCommandChoice"), _command.type); // Populate the correct command argument widgets createArgumentWidgets(_command.type); // Pre-fill the values for (conversation::ConversationCommand::ArgumentMap::const_iterator i = _command.arguments.begin(); i != _command.arguments.end(); ++i) { int argIndex = i->first; if (argIndex > static_cast(_argumentItems.size()) || argIndex < 0) { // Invalid command argument index rError() << "Invalid command argument index " << argIndex << std::endl; continue; } // Load the value into the argument item _argumentItems[argIndex - 1]->setValueFromString(i->second); } // Update the "wait until finished" flag findNamedObject(this, "ConvCmdEditorWaitUntilFinished")->SetValue(_command.waitUntilFinished); // Update the sensitivity of the correct flag updateWaitUntilFinished(_command.type); } void CommandEditor::save() { _command.actor = wxutil::ChoiceHelper::GetSelectionId( findNamedObject(this, "ConvCmdEditorActorChoice")); _command.type = wxutil::ChoiceHelper::GetSelectionId( findNamedObject(this, "ConvCmdEditorCommandChoice")); // Clear the existing arguments _command.arguments.clear(); int index = 1; for (ArgumentItemList::iterator i = _argumentItems.begin(); i != _argumentItems.end(); ++i, ++index) { _command.arguments[index] = (*i)->getValue(); } // Get the value of the "wait until finished" flag try { const conversation::ConversationCommandInfo& cmdInfo = conversation::ConversationCommandLibrary::Instance().findCommandInfo(_command.type); if (cmdInfo.waitUntilFinishedAllowed) { // Load the value _command.waitUntilFinished = findNamedObject(this, "ConvCmdEditorWaitUntilFinished")->GetValue(); } else { // Command doesn't support "wait until finished", set to default == true _command.waitUntilFinished = true; } } catch (std::runtime_error&) { rError() << "Cannot find conversation command info for index " << _command.type << std::endl; } // Copy the command over the target object _targetCommand = _command; } void CommandEditor::populateWindow() { loadNamedPanel(this, "ConvCmdEditorMainPanel"); makeLabelBold(this, "ConvCmdEditorActorLabel"); makeLabelBold(this, "ConvCmdEditorCommandLabel"); makeLabelBold(this, "ConvCmdEditorCmdArgLabel"); makeLabelBold(this, "ConvCmdEditorPropertiesLabel"); wxChoice* cmdDropDown = findNamedObject(this, "ConvCmdEditorCommandChoice"); cmdDropDown->Connect(wxEVT_CHOICE, wxCommandEventHandler(CommandEditor::onCommandTypeChange), NULL, this); // Wire up button events findNamedObject(this, "ConvCmdEditorCancelButton")->Connect( wxEVT_BUTTON, wxCommandEventHandler(CommandEditor::onCancel), NULL, this); findNamedObject(this, "ConvCmdEditorOkButton")->Connect( wxEVT_BUTTON, wxCommandEventHandler(CommandEditor::onSave), NULL, this); } void CommandEditor::commandTypeChanged() { int newCommandTypeID = -1; wxChoice* cmdDropDown = findNamedObject(this, "ConvCmdEditorCommandChoice"); int selectedItem = cmdDropDown->GetSelection(); wxStringClientData* cmdIdStr = static_cast(cmdDropDown->GetClientObject(selectedItem)); newCommandTypeID = string::convert(cmdIdStr->GetData().ToStdString(), -1); // Create the argument widgets for this new command type createArgumentWidgets(newCommandTypeID); // Update the sensitivity of the correct flag updateWaitUntilFinished(newCommandTypeID); } void CommandEditor::updateWaitUntilFinished(int commandTypeID) { // Update the sensitivity of the correct flag try { const conversation::ConversationCommandInfo& cmdInfo = conversation::ConversationCommandLibrary::Instance().findCommandInfo(commandTypeID); findNamedObject(this, "ConvCmdEditorWaitUntilFinished")->Enable(cmdInfo.waitUntilFinishedAllowed); } catch (std::runtime_error&) { rError() << "Cannot find conversation command info for index " << commandTypeID << std::endl; } } void CommandEditor::createArgumentWidgets(int commandTypeID) { try { const conversation::ConversationCommandInfo& cmdInfo = conversation::ConversationCommandLibrary::Instance().findCommandInfo(commandTypeID); // Remove all possible previous items from the list _argumentItems.clear(); // Clear the panel and add a new table wxPanel* argPanel = findNamedObject(this, "ConvCmdEditorArgPanel"); argPanel->DestroyChildren(); // Create the table wxFlexGridSizer* table = new wxFlexGridSizer(static_cast(cmdInfo.arguments.size()), 3, 6, 12); table->AddGrowableCol(1); argPanel->SetSizer(table); if (cmdInfo.arguments.empty()) { wxStaticText* noneText = new wxStaticText(argPanel, wxID_ANY, _("None")); noneText->SetFont(noneText->GetFont().Italic()); argPanel->GetSizer()->Add(noneText, 0, wxLEFT, 6); return; } // Setup the table with default spacings for (const conversation::ArgumentInfo& argInfo : cmdInfo.arguments) { CommandArgumentItemPtr item = createCommandArgumentItem(argInfo, argPanel); if (item) { _argumentItems.push_back(item); // The label table->Add(item->getLabelWidget(), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 6); // The edit widgets table->Add(item->getEditWidget(), 1, wxEXPAND, wxALIGN_CENTER_VERTICAL); // The help widgets table->Add(item->getHelpWidget(), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxRIGHT, 6); } } } catch (std::runtime_error&) { rError() << "Cannot find conversation command info for index " << commandTypeID << std::endl; } wxPanel* mainPanel = findNamedObject(this, "ConvCmdEditorMainPanel"); mainPanel->Fit(); mainPanel->Layout(); Fit(); } CommandArgumentItemPtr CommandEditor::createCommandArgumentItem(const conversation::ArgumentInfo& argInfo, wxWindow* parent) { // Unfortunately we don't have a type declaration for animation in the .def file (didn't think about that back then) // so let's detect the "Anim" title of the argument and construct an animation picker in this case if (argInfo.title == "Anim") { return std::make_shared(*this, parent, argInfo); } switch (argInfo.type) { case conversation::ArgumentInfo::ARGTYPE_BOOL: // Create a new bool argument item return std::make_shared(*this, parent, argInfo); case conversation::ArgumentInfo::ARGTYPE_INT: case conversation::ArgumentInfo::ARGTYPE_FLOAT: case conversation::ArgumentInfo::ARGTYPE_STRING: // Create a new string argument item return std::make_shared(*this, parent, argInfo); case conversation::ArgumentInfo::ARGTYPE_VECTOR: // Create a new string argument item return std::make_shared(*this, parent, argInfo); case conversation::ArgumentInfo::ARGTYPE_SOUNDSHADER: // Create a new sound shader argument item return std::make_shared(*this, parent, argInfo); case conversation::ArgumentInfo::ARGTYPE_ACTOR: // Create a new actor argument item return std::make_shared(*this, parent, argInfo, _conversation.actors); case conversation::ArgumentInfo::ARGTYPE_ENTITY: // Create a new string argument item return std::make_shared(*this, parent, argInfo); default: rError() << "Unknown command argument type: " << argInfo.type << std::endl; break; }; return CommandArgumentItemPtr(); } void CommandEditor::onSave(wxCommandEvent& ev) { save(); EndModal(wxID_OK); } void CommandEditor::onCancel(wxCommandEvent& ev) { // Just close the window without writing the values EndModal(wxID_CANCEL); } void CommandEditor::onCommandTypeChange(wxCommandEvent& ev) { commandTypeChanged(); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.conversation/CommandEditor.h000066400000000000000000000030761321750546400234070ustar00rootroot00000000000000#pragma once #include "wxutil/TreeModel.h" #include "wxutil/dialog/DialogBase.h" #include "wxutil/XmlResourceBasedWidget.h" #include "Conversation.h" #include "ConversationCommand.h" #include "ConversationCommandLibrary.h" #include "CommandArgumentItem.h" namespace ui { class CommandEditor : public wxutil::DialogBase, private wxutil::XmlResourceBasedWidget { private: // The conversation (read-only) const conversation::Conversation& _conversation; // The command we're editing (working copy) conversation::ConversationCommand _command; // The actual command we're saving to on "OK" conversation::ConversationCommand& _targetCommand; typedef std::vector ArgumentItemList; ArgumentItemList _argumentItems; public: // Pass the parent window, the command and the conversation to edit CommandEditor(wxWindow* parent, conversation::ConversationCommand& command, const conversation::Conversation& conv); // The Command we're working on (working copy) conversation::ConversationCommand& getCommand(); // Return the conversation we're defined on const conversation::Conversation& getConversation(); private: void populateWindow(); void updateWidgets(); void save(); void commandTypeChanged(); void createArgumentWidgets(int commandTypeID); void updateWaitUntilFinished(int commandTypeID); void onSave(wxCommandEvent& ev); void onCancel(wxCommandEvent& ev); void onCommandTypeChange(wxCommandEvent& ev); CommandArgumentItemPtr createCommandArgumentItem(const conversation::ArgumentInfo& argInfo, wxWindow* parent); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.conversation/Conversation.h000066400000000000000000000030261321750546400233270ustar00rootroot00000000000000#ifndef CONVERSATION_H_ #define CONVERSATION_H_ #include #include #include #include "ConversationCommand.h" namespace conversation { /** * Data object representing a single Conversation. */ class Conversation { public: // The (unique) name of this conversation std::string name; float talkDistance; bool actorsMustBeWithinTalkdistance; bool actorsAlwaysFaceEachOther; int maxPlayCount; // Indexed list of commands typedef std::map CommandMap; CommandMap commands; // Indexed list of actors (by name) typedef std::map ActorMap; ActorMap actors; // Constructor Conversation() : talkDistance(60), actorsMustBeWithinTalkdistance(true), actorsAlwaysFaceEachOther(true), maxPlayCount(-1) {} // Copy Constructor Conversation(const Conversation& other) : name(other.name), talkDistance(other.talkDistance), actorsMustBeWithinTalkdistance(other.actorsMustBeWithinTalkdistance), actorsAlwaysFaceEachOther(other.actorsAlwaysFaceEachOther), maxPlayCount(other.maxPlayCount), actors(other.actors) { // Copy all commands, one by one for (CommandMap::const_iterator i = other.commands.begin(); i != other.commands.end(); ++i) { // Copy-construct a new conversation command ConversationCommandPtr copy(new ConversationCommand(*i->second)); // Insert this into our own map commands[i->first] = copy; } } }; /** * Conversation map type. */ typedef std::map ConversationMap; } #endif /* CONVERSATION_H_ */ DarkRadiant-2.5.0/plugins/dm.conversation/ConversationCommand.cpp000066400000000000000000000025151321750546400251630ustar00rootroot00000000000000#include "ConversationCommand.h" #include "string/convert.h" #include "string/replace.h" #include "ConversationCommandLibrary.h" namespace conversation { ConversationCommand::ConversationCommand() : type(-1), // invalid id actor(-1), waitUntilFinished(true) {} std::string ConversationCommand::getArgument(int index) const { ArgumentMap::const_iterator i = arguments.find(index); return (i != arguments.end()) ? i->second : ""; } std::string ConversationCommand::getSentence() const { // Get the command description for this type try { const ConversationCommandInfo& cmdInfo = ConversationCommandLibrary::Instance().findCommandInfo(type); // Get the sentence and fill in the placeholders, if any std::string sentence = cmdInfo.sentence; int counter = 1; for (ConversationCommandInfo::ArgumentInfoList::const_iterator i = cmdInfo.arguments.begin(); i != cmdInfo.arguments.end(); ++i, ++counter) { std::string needle = "[arg" + string::to_string(counter) + "]"; std::string replacement = getArgument(counter); // Check for a bool /*if (i->second.type == "b") { replacement = (i->second.value.empty()) ? "no" : "yes"; }*/ string::replace_all(sentence, needle, replacement); } return sentence; } catch (std::runtime_error&) { return "Unrecognised command."; } } } // namespace conversation DarkRadiant-2.5.0/plugins/dm.conversation/ConversationCommand.h000066400000000000000000000017501321750546400246300ustar00rootroot00000000000000#ifndef CONVERSATION_COMMAND_H_ #define CONVERSATION_COMMAND_H_ #include #include #include namespace conversation { /** * Data object representing a single ConversationCommand. */ class ConversationCommand { public: // What kind of command this is (index of a certain ConversationCommandInfo structure) int type; // Which actor should perform this command. int actor; // whether the command must be finished before the next command can start bool waitUntilFinished; // The numbered arguments typedef std::map ArgumentMap; ArgumentMap arguments; // Constructor ConversationCommand(); /** * greebo: Returns a human-readable version of this command. */ std::string getSentence() const; // Returns the argument with the given index or "" if not found std::string getArgument(int index) const; }; typedef std::shared_ptr ConversationCommandPtr; } // namespace conversation #endif /* CONVERSATION_COMMAND_H_ */ DarkRadiant-2.5.0/plugins/dm.conversation/ConversationCommandInfo.cpp000066400000000000000000000044411321750546400257770ustar00rootroot00000000000000#include "ConversationCommandInfo.h" #include "string/string.h" #include "itextstream.h" #include "eclass.h" namespace conversation { // initialise the static member variable int ConversationCommandInfo::_highestId = 0; void ConversationCommandInfo::parseFromEntityClass(const IEntityClassPtr& eclass) { assert(eclass != NULL); // don't accept NULL pointers name = eclass->getAttribute("editor_cmdName").getValue(); waitUntilFinishedAllowed = (eclass->getAttribute("editor_waitUntilFinishedAllowed").getValue() == "1"); sentence = eclass->getAttribute("editor_sentence").getValue(); // Read the arguments // Find all attributes matching "argType", this spawnarg is mandatory eclass::AttributeList argTypes = eclass::getSpawnargsWithPrefix( *eclass, "editor_argType" ); for (eclass::AttributeList::const_iterator i = argTypes.begin(); i != argTypes.end(); ++i) { // Cut off the "editor_argType" part and retrieve the number std::string argIndex = i->getName().substr(14); ArgumentInfo info; info.required = (eclass->getAttribute("editor_argRequired" + argIndex).getValue() != "0"); info.description = eclass->getAttribute("editor_argDesc" + argIndex).getValue(); info.title = eclass->getAttribute("editor_argTitle" + argIndex).getValue(); std::string argTypeStr = eclass->getAttribute("editor_argType" + argIndex).getValue(); if (argTypeStr == "float") { info.type = ArgumentInfo::ARGTYPE_FLOAT; } else if (argTypeStr == "int") { info.type = ArgumentInfo::ARGTYPE_INT; } else if (argTypeStr == "string") { info.type = ArgumentInfo::ARGTYPE_STRING; } else if (argTypeStr == "vector") { info.type = ArgumentInfo::ARGTYPE_VECTOR; } else if (argTypeStr == "soundshader") { info.type = ArgumentInfo::ARGTYPE_SOUNDSHADER; } else if (argTypeStr == "actor") { info.type = ArgumentInfo::ARGTYPE_ACTOR; } else if (argTypeStr == "entity") { info.type = ArgumentInfo::ARGTYPE_ENTITY; } else if (argTypeStr == "bool") { info.type = ArgumentInfo::ARGTYPE_BOOL; } else { rError() << "Could not determine Conversation Command Argument type: " << argTypeStr << " on entityDef " << eclass->getName() << std::endl; } // add the argument to the local list arguments.push_back(info); } } } // namespace conversation DarkRadiant-2.5.0/plugins/dm.conversation/ConversationCommandInfo.h000066400000000000000000000033771321750546400254530ustar00rootroot00000000000000#ifndef CONVERSATION_COMMAND_INFO_H_ #define CONVERSATION_COMMAND_INFO_H_ #include "ieclass.h" #include namespace conversation { // Information about a single conversation command argument struct ArgumentInfo { enum ArgumentType { ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_STRING, ARGTYPE_VECTOR, ARGTYPE_SOUNDSHADER, ARGTYPE_ACTOR, ARGTYPE_ENTITY, ARGTYPE_BOOL, NUM_ARGTYPES, }; ArgumentType type; std::string description; std::string title; bool required; }; /** * greebo: This structure holds information about a certain * Conversation Command Type (e.g. "WaitSeconds") and its arguments. * * As the number and type of arguments varies, the CommandEditor GUI * needs this structure to build the variable interface. */ class ConversationCommandInfo { public: // The unique id of this conversation command. int id; // The internal name, like "WaitSeconds" or "InteractWithEntity" std::string name; // Whether the "wait until finished" checkbox is displayed bool waitUntilFinishedAllowed; // The "human readable" sentence format (with placeholders) std::string sentence; typedef std::vector ArgumentInfoList; ArgumentInfoList arguments; ConversationCommandInfo() : id(++_highestId), waitUntilFinishedAllowed(true) {} // Fills the member variables from the given entityDef void parseFromEntityClass(const IEntityClassPtr& eclass); private: // Highest ID so far static int _highestId; }; typedef std::shared_ptr ConversationCommandInfoPtr; // A mapping between command typenames ("WalkToActor") to actual information structures typedef std::map ConversationCommandInfoMap; } // namespace conversation #endif /* CONVERSATION_COMMAND_INFO_H_ */ DarkRadiant-2.5.0/plugins/dm.conversation/ConversationCommandLibrary.cpp000066400000000000000000000054551321750546400265160ustar00rootroot00000000000000#include "ConversationCommandLibrary.h" #include "ieclass.h" #include "iregistry.h" #include "gamelib.h" #include "string/convert.h" #include "string/predicate.h" #include namespace conversation { /** greebo: The visitor class that stores all the relevant eclassptrs * into the given target map if the prefix matches. */ class ConversationCommandInfoLoader : public EntityClassVisitor { // The target map to populate ConversationCommandInfoMap& _map; // The entityDef prefix (e.g. "atdm:conversation_command_") std::string _prefix; public: /** * greebo: Pass the target map where all the eclassptrs should be stored into. */ ConversationCommandInfoLoader(ConversationCommandInfoMap& map) : _map(map), _prefix(game::current::getValue(GKEY_CONVERSATION_COMMAND_INFO_PREFIX)) {} void visit(const IEntityClassPtr& eclass) { if (string::starts_with(eclass->getName(), _prefix)) { // We have a match, create a new structure ConversationCommandInfoPtr commandInfo(new ConversationCommandInfo); // Fill the values from the found entityDef commandInfo->parseFromEntityClass(eclass); // Store the structure to the target map _map[commandInfo->name] = commandInfo; } } }; ConversationCommandLibrary::ConversationCommandLibrary() { loadConversationCommands(); } const ConversationCommandInfo& ConversationCommandLibrary::findCommandInfo(const std::string& name) { ConversationCommandInfoMap::const_iterator i = _commandInfo.find(name); if (i == _commandInfo.end()) { throw std::runtime_error(std::string("Could not find command info with the given name: ") + name); } return *(i->second); } const ConversationCommandInfo& ConversationCommandLibrary::findCommandInfo(int id) { for (ConversationCommandInfoMap::const_iterator i = _commandInfo.begin(); i != _commandInfo.end(); ++i) { if (i->second->id == id) { return *(i->second); } } throw std::runtime_error(std::string("Could not find command info with the given ID: ") + string::to_string(id)); } void ConversationCommandLibrary::loadConversationCommands() { // Load the possible command types ConversationCommandInfoLoader loader(_commandInfo); GlobalEntityClassManager().forEachEntityClass(loader); } void ConversationCommandLibrary::populateChoice(wxChoice* choice) { // Iterate over everything and push the data into the liststore for (ConversationCommandInfoMap::const_iterator i = _commandInfo.begin(); i != _commandInfo.end(); ++i) { // Store the actor ID into a client data object and pass it along choice->Append(i->second->name, new wxStringClientData(string::to_string(i->second->id))); } } // Static accessor ConversationCommandLibrary& ConversationCommandLibrary::Instance() { static ConversationCommandLibrary _instance; return _instance; } } // namespace conversation DarkRadiant-2.5.0/plugins/dm.conversation/ConversationCommandLibrary.h000066400000000000000000000027441321750546400261610ustar00rootroot00000000000000#pragma once #include "ConversationCommandInfo.h" class wxChoice; namespace conversation { namespace { const std::string GKEY_CONVERSATION_COMMAND_INFO_PREFIX = "/conversationSystem/conversationCommandPrefix"; } /** * greebo: This class holds all the possible conversation command types, * indexed by name. Each conversation commmand is parsed from an entityDef * matching a given prefix and holding a bunch of information about that command. * * The ConversationCommand editor is using this information to construct * the UI elements. */ class ConversationCommandLibrary { // The map containing the named information ConversationCommandInfoMap _commandInfo; // Private constructor, loads all matching entityDefs ConversationCommandLibrary(); public: // Accessor to the singleton instance static ConversationCommandLibrary& Instance(); /** * Returns the command info with the given typename or ID. Throws an exception if not found. * * @throws: std::runtime error if the named info structure could not be found. */ const ConversationCommandInfo& findCommandInfo(const std::string& name); const ConversationCommandInfo& findCommandInfo(int id); /** * greebo: This populates the given choice field with all available commands. The command ID * will be attached to the items as wxStringClientData. */ void populateChoice(wxChoice* choice); private: // Loads all entityDefs matching the given prefix void loadConversationCommands(); }; } // namespace conversation DarkRadiant-2.5.0/plugins/dm.conversation/ConversationDialog.cpp000066400000000000000000000254431321750546400250110ustar00rootroot00000000000000#include "ConversationDialog.h" #include "i18n.h" #include "iregistry.h" #include "iundo.h" #include "ieclass.h" #include "imainframe.h" #include "iscenegraph.h" #include "string/string.h" #include "wxutil/TreeModel.h" #include "wxutil/dialog/MessageBox.h" #include "RandomOrigin.h" #include "ConversationEntityFinder.h" #include "ConversationEditor.h" #include #include #include #include #include namespace ui { namespace { const char* const WINDOW_TITLE = N_("Conversation Editor"); const std::string CONVERSATION_ENTITY_CLASS = "atdm:conversation_info"; } ConversationDialog::ConversationDialog() : DialogBase(_(WINDOW_TITLE)), _entityList(new wxutil::TreeModel(_convEntityColumns, true)), _entityView(NULL), _convList(new wxutil::TreeModel(_convColumns, true)), _convView(NULL) { // Create the widgets populateWindow(); FitToScreen(0.3f, 0.5f); } void ConversationDialog::populateWindow() { loadNamedPanel(this, "ConvDialogMainPanel"); wxPanel* entityPanel = findNamedObject(this, "ConvDialogEntityPanel"); // Entity Tree View _entityView = wxutil::TreeView::CreateWithModel(entityPanel, _entityList, wxDV_NO_HEADER); entityPanel->GetSizer()->Add(_entityView, 1, wxEXPAND); _entityView->AppendTextColumn("", _convEntityColumns.displayName.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); _entityView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(ConversationDialog::onEntitySelectionChanged), NULL, this); // Wire up button signals _addEntityButton = findNamedObject(this, "ConvDialogAddEntityButton"); _addEntityButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onAddEntity), NULL, this); _deleteEntityButton = findNamedObject(this, "ConvDialogDeleteEntityButton"); _deleteEntityButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onDeleteEntity), NULL, this); _deleteEntityButton->Enable(false); wxPanel* convPanel = findNamedObject(this, "ConvDialogConversationPanel"); _convView = wxutil::TreeView::CreateWithModel(convPanel, _convList); // Key and value text columns _convView->AppendTextColumn("#", _convColumns.index.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); _convView->AppendTextColumn(_("Name"), _convColumns.name.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); _convView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(ConversationDialog::onConversationSelectionChanged), NULL, this); convPanel->GetSizer()->Add(_convView, 1, wxEXPAND); convPanel->Enable(false); // Wire up button signals _addConvButton = findNamedObject(this, "ConvDialogAddConvButton"); _addConvButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onAddConversation), NULL, this); _addConvButton->Enable(false); // not enabled without selection _editConvButton = findNamedObject(this, "ConvDialogEditConvButton"); _editConvButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onEditConversation), NULL, this); _editConvButton->Enable(false); // not enabled without selection _deleteConvButton = findNamedObject(this, "ConvDialogDeleteConvButton"); _deleteConvButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onDeleteConversation), NULL, this); _deleteConvButton->Enable(false); // not enabled without selection _clearConvButton = findNamedObject(this, "ConvDialogClearConvButton"); _clearConvButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onClearConversations), NULL, this); _clearConvButton->Enable(false); // requires >0 conversations // Boldify a few labels makeLabelBold(this, "ConvDialogEntityLabel"); makeLabelBold(this, "ConvDialogConvLabel"); // Connect dialog buttons findNamedObject(this, "ConvDialogCancelButton")->Connect( wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onCancel), NULL, this); findNamedObject(this, "ConvDialogOkButton")->Connect( wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onOK), NULL, this); } void ConversationDialog::save() { // Consistency check can go here // Scoped undo object UndoableCommand command("editConversations"); // Save the working set to the entity for (conversation::ConversationEntityMap::iterator i = _entities.begin(); i != _entities.end(); ++i) { i->second->writeToEntity(); } } void ConversationDialog::clear() { // Clear internal data _entities.clear(); _curEntity = _entities.end(); // Clear the list boxes _entityList->Clear(); _convList->Clear(); } void ConversationDialog::refreshConversationList() { // Clear and refresh the conversation list _convList->Clear(); _curEntity->second->populateListStore(*_convList, _convColumns); // If there is at least one conversation, make the Clear button available _clearConvButton->Enable(!_curEntity->second->isEmpty()); handleConversationSelectionChange(); } void ConversationDialog::populateWidgets() { // First clear the data clear(); // Use an ConversationEntityFinder to walk the map and add any conversation // entities to the liststore and entity map conversation::ConversationEntityFinder finder( _entityList, _convEntityColumns, _entities, CONVERSATION_ENTITY_CLASS ); GlobalSceneGraph().root()->traverseChildren(finder); updateConversationPanelSensitivity(); } int ConversationDialog::ShowModal() { populateWidgets(); int returnCode = DialogBase::ShowModal(); if (returnCode == wxID_OK) { save(); } return returnCode; } // Static command target void ConversationDialog::ShowDialog(const cmd::ArgumentList& args) { // Construct a new instance, this enters the main loop ConversationDialog* editor = new ConversationDialog; editor->ShowModal(); editor->Destroy(); } void ConversationDialog::updateConversationPanelSensitivity() { // Clear the conversations list _convList->Clear(); wxDataViewItem item = _entityView->GetSelection(); if (item.IsOk()) { // Get name of the entity and find the corresponding ConversationEntity in the map wxutil::TreeModel::Row row(item, *_entityList); std::string name = row[_convEntityColumns.entityName]; // Save the current selection and refresh the conversation list _curEntity = _entities.find(name); refreshConversationList(); // Enable the delete button and conversation panel _deleteEntityButton->Enable(true); findNamedObject(this, "ConvDialogConversationPanel")->Enable(true); _addConvButton->Enable(true); } else { // No selection, disable the delete button and clear the conversation panel _deleteEntityButton->Enable(false); // Disable all the Conversation buttons findNamedObject(this, "ConvDialogConversationPanel")->Enable(false); _addConvButton->Enable(false); _editConvButton->Enable(false); _deleteConvButton->Enable(false); _clearConvButton->Enable(false); } } // Callback for conversation entity selection changed in list box void ConversationDialog::onEntitySelectionChanged(wxDataViewEvent& ev) { updateConversationPanelSensitivity(); } void ConversationDialog::onOK(wxCommandEvent& ev) { EndModal(wxID_OK); } void ConversationDialog::onCancel(wxCommandEvent& ev) { EndModal(wxID_CANCEL); } // Add a new conversations entity button void ConversationDialog::onAddEntity(wxCommandEvent& ev) { // Obtain the entity class object IEntityClassPtr eclass = GlobalEntityClassManager().findClass(CONVERSATION_ENTITY_CLASS); if (eclass) { // Construct a Node of this entity type IEntityNodePtr node(GlobalEntityCreator().createEntity(eclass)); // Create a random offset node->getEntity().setKeyValue( "origin", RandomOrigin::generate(128) ); // Insert the node into the scene graph assert(GlobalSceneGraph().root()); GlobalSceneGraph().root()->addChildNode(node); // Refresh the widgets populateWidgets(); } else { // conversation entityclass was not found wxutil::Messagebox::ShowError( fmt::format(_("Unable to create conversation Entity: class '{0}' not found."), CONVERSATION_ENTITY_CLASS) ); } } // Delete entity button void ConversationDialog::onDeleteEntity(wxCommandEvent& ev) { // Get the Node* from the tree model and remove it from the scenegraph wxDataViewItem item = _entityView->GetSelection(); if (item) { // Get the name of the selected entity wxutil::TreeModel::Row row(item, *_entityList); std::string name = row[_convEntityColumns.entityName]; // Instruct the ConversationEntity to delete its world node, and then // remove it from the map _entities[name]->deleteWorldNode(); _entities.erase(name); // Update the widgets to remove the selection from the list populateWidgets(); } } // Callback for current conversation selection changed void ConversationDialog::onConversationSelectionChanged(wxDataViewEvent& ev) { handleConversationSelectionChange(); } void ConversationDialog::handleConversationSelectionChange() { // Get the selection _currentConversation = _convView->GetSelection(); if (_currentConversation.IsOk()) { // Enable the edit and delete buttons _editConvButton->Enable(true); _deleteConvButton->Enable(true); } else { // Disable the edit and delete buttons _editConvButton->Enable(false); _deleteConvButton->Enable(false); } } void ConversationDialog::onAddConversation(wxCommandEvent& ev) { // Add a new conversation to the ConversationEntity and refresh the list store _curEntity->second->addConversation(); refreshConversationList(); } void ConversationDialog::onEditConversation(wxCommandEvent& ev) { // Retrieve the index of the current conversation wxutil::TreeModel::Row row(_currentConversation, *_convList); int index = row[_convColumns.index].getInteger(); conversation::Conversation& conv = _curEntity->second->getConversation(index); // Display the edit dialog, blocks on construction ConversationEditor* editor = new ConversationEditor(this, conv); editor->ShowModal(); editor->Destroy(); // Repopulate the conversation list refreshConversationList(); } void ConversationDialog::onDeleteConversation(wxCommandEvent& ev) { // Get the index of the current conversation wxutil::TreeModel::Row row(_currentConversation, *_convList); int index = row[_convColumns.index].getInteger(); // Tell the ConversationEntity to delete this conversation _curEntity->second->deleteConversation(index); // Repopulate the conversation list refreshConversationList(); } void ConversationDialog::onClearConversations(wxCommandEvent& ev) { // Clear the entity and refresh the list _curEntity->second->clearConversations(); refreshConversationList(); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.conversation/ConversationDialog.h000066400000000000000000000046541321750546400244570ustar00rootroot00000000000000#pragma once #include #include "ieventmanager.h" #include "ientity.h" #include "icommandsystem.h" #include "iradiant.h" #include "wxutil/dialog/DialogBase.h" #include "wxutil/XmlResourceBasedWidget.h" #include "wxutil/TreeView.h" #include "ConversationEntity.h" namespace ui { class ConversationDialog; typedef std::shared_ptr ConversationDialogPtr; /** * greebo: The conversation dialog is a modal top-level window providing * views and controls to facilitate the setup of inter-AI conversations. */ class ConversationDialog : public wxutil::DialogBase, private wxutil::XmlResourceBasedWidget { private: // List of conversation_info entities conversation::ConvEntityColumns _convEntityColumns; wxutil::TreeModel::Ptr _entityList; wxutil::TreeView* _entityView; // List of conversations on the selected entity conversation::ConversationColumns _convColumns; wxutil::TreeModel::Ptr _convList; wxutil::TreeView* _convView; // Map of ConversationEntity objects, indexed by the name of the world entity conversation::ConversationEntityMap _entities; // Iterators for current entity and current objective conversation::ConversationEntityMap::iterator _curEntity; wxDataViewItem _currentConversation; wxButton* _addConvButton; wxButton* _editConvButton; wxButton* _deleteConvButton; wxButton* _clearConvButton; wxButton* _addEntityButton; wxButton* _deleteEntityButton; public: ConversationDialog(); // Command target to toggle the dialog static void ShowDialog(const cmd::ArgumentList& args); // Override DialogBase virtual int ShowModal(); private: // greebo: Saves the current working set to the entity void save(); // Clears out all stored data void clear(); void populateWidgets(); // Re-loads the conversation from the selected entity void refreshConversationList(); void updateConversationPanelSensitivity(); void handleConversationSelectionChange(); // WIDGET POPULATION void populateWindow(); // Button callbacks void onOK(wxCommandEvent& ev); void onCancel(wxCommandEvent& ev); void onEntitySelectionChanged(wxDataViewEvent& ev); void onAddEntity(wxCommandEvent& ev); void onDeleteEntity(wxCommandEvent& ev); void onConversationSelectionChanged(wxDataViewEvent& ev); void onAddConversation(wxCommandEvent& ev); void onEditConversation(wxCommandEvent& ev); void onDeleteConversation(wxCommandEvent& ev); void onClearConversations(wxCommandEvent& ev); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.conversation/ConversationEditor.cpp000066400000000000000000000444531321750546400250420ustar00rootroot00000000000000#include "ConversationEditor.h" #include "i18n.h" #include "string/string.h" #include #include "string/join.h" #include #include "CommandEditor.h" #include "ActorNodeFinder.h" #include "wxutil/dialog/MessageBox.h" #include #include #include #include #include #include #include namespace ui { namespace { const char* const WINDOW_TITLE = N_("Edit Conversation"); } ConversationEditor::ConversationEditor(wxWindow* parent, conversation::Conversation& conversation) : DialogBase(_(WINDOW_TITLE), parent), _actorStore(new wxutil::TreeModel(_actorColumns, true)), _commandStore(new wxutil::TreeModel(_commandColumns, true)), _conversation(conversation), // copy the conversation to a local object _targetConversation(conversation), _updateInProgress(false) { // Create the widgets populateWindow(); // Load the conversation values into the widgets updateWidgets(); // Clear the button sensitivity in the command actions panel updateCmdActionSensitivity(false); SetSize(500, 680); } void ConversationEditor::populateWindow() { loadNamedPanel(this, "ConvEditorMainPanel"); makeLabelBold(this, "ConvEditorPropertyLabel"); makeLabelBold(this, "ConvEditorActorLabel"); makeLabelBold(this, "ConvEditorCommandLabel"); findNamedObject(this, "ConvEditorNameEntry")->Bind(wxEVT_TEXT, [&](wxCommandEvent& ev) { if (!_updateInProgress) { _conversation.name = ev.GetString(); } }); findNamedObject(this, "ConvEditorRepeatCheckbox")->Connect( wxEVT_CHECKBOX, wxCommandEventHandler(ConversationEditor::onMaxPlayCountEnabled), NULL, this); findNamedObject(this, "ConvEditorActorsWithinTalkDistance")->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& ev) { _conversation.actorsMustBeWithinTalkdistance = ev.IsChecked(); }); findNamedObject(this, "ConvEditorActorsMustFace")->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& ev) { _conversation.actorsAlwaysFaceEachOther = ev.IsChecked(); }); findNamedObject(this, "ConvEditorRepeatTimes")->Bind(wxEVT_SPINCTRL, [&](wxSpinEvent& ev) { if (!_updateInProgress) { _conversation.maxPlayCount = ev.GetValue(); updateWidgets(); } }); // Actor Panel wxPanel* actorPanel = findNamedObject(this, "ConvEditorActorPanel"); _actorView = wxutil::TreeView::CreateWithModel(actorPanel, _actorStore); _actorView->SetSize(wxSize(350, 160)); actorPanel->GetSizer()->Add(_actorView, 1, wxEXPAND); _actorView->AppendTextColumn("#", _actorColumns.actorNumber.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); _actorView->AppendTextColumn(_("Actor (click to edit)"), _actorColumns.displayName.getColumnIndex(), wxDATAVIEW_CELL_EDITABLE, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); _actorView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(ConversationEditor::onActorSelectionChanged), NULL, this); _actorView->Connect(wxEVT_DATAVIEW_ITEM_EDITING_DONE, wxDataViewEventHandler(ConversationEditor::onActorEdited), NULL, this); // Wire up button signals _addActorButton = findNamedObject(this, "ConvEditorAddActorButton"); _addActorButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onAddActor), NULL, this); _delActorButton = findNamedObject(this, "ConvEditorDeleteActorButton"); _delActorButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onDeleteActor), NULL, this); _delActorButton->Enable(false); _validateActorsButton = findNamedObject(this, "ConvEditorValidateActorsButton"); _validateActorsButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onValidateActors), NULL, this); _validateActorsButton->Enable(false); // Command Panel wxPanel* commandPanel = findNamedObject(this, "ConvEditorCommandPanel"); _commandView = wxutil::TreeView::CreateWithModel(commandPanel, _commandStore); _commandView->SetSize(wxSize(350, 200)); commandPanel->GetSizer()->Add(_commandView, 1, wxEXPAND); _commandView->AppendTextColumn("#", _commandColumns.cmdNumber.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT); _commandView->AppendTextColumn(_("Actor"), _commandColumns.actorName.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT); _commandView->AppendTextColumn(_("Command"), _commandColumns.sentence.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT); _commandView->AppendTextColumn(_("Wait"), _commandColumns.wait.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT); _commandView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(ConversationEditor::onCommandSelectionChanged), NULL, this); // Wire up buttons _addCmdButton = findNamedObject(this, "ConvEditorAddCommandButton"); _addCmdButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onAddCommand), NULL, this); _delCmdButton = findNamedObject(this, "ConvEditorDeleteCommandButton"); _delCmdButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onDeleteCommand), NULL, this); _delCmdButton->Enable(false); _editCmdButton = findNamedObject(this, "ConvEditorEditCommandButton"); _editCmdButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onEditCommand), NULL, this); _editCmdButton->Enable(false); _moveUpCmdButton = findNamedObject(this, "ConvEditorMoveUpCommandButton"); _moveUpCmdButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onMoveUpCommand), NULL, this); _moveUpCmdButton->Enable(false); _moveDownCmdButton = findNamedObject(this, "ConvEditorMoveDownCommandButton"); _moveDownCmdButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onMoveDownCommand), NULL, this); _moveDownCmdButton->Enable(false); // Dialog buttons findNamedObject(this, "ConvEditorCancelButton")->Connect( wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onCancel), NULL, this); findNamedObject(this, "ConvEditorOkButton")->Connect( wxEVT_BUTTON, wxCommandEventHandler(ConversationEditor::onSave), NULL, this); } void ConversationEditor::updateWidgets() { _updateInProgress = true; // Clear the liststore first _actorStore->Clear(); _currentActor = wxDataViewItem(); _currentCommand = wxDataViewItem(); updateCmdActionSensitivity(false); _delActorButton->Enable(false); _validateActorsButton->Enable(!_conversation.actors.empty()); // Name findNamedObject(this, "ConvEditorNameEntry")->SetValue(_conversation.name); findNamedObject(this, "ConvEditorActorsWithinTalkDistance")->SetValue( _conversation.actorsMustBeWithinTalkdistance); findNamedObject(this, "ConvEditorActorsMustFace")->SetValue( _conversation.actorsAlwaysFaceEachOther); // Update the max play count if (_conversation.maxPlayCount != -1) { // Max play count is enabled findNamedObject(this, "ConvEditorRepeatTimes")->Enable(true); findNamedObject(this, "ConvEditorRepeatTimes")->SetValue(_conversation.maxPlayCount); findNamedObject(this, "ConvEditorRepeatAdditionalText")->Enable(true); findNamedObject(this, "ConvEditorRepeatCheckbox")->SetValue(true); } else { // Max play count disabled findNamedObject(this, "ConvEditorRepeatTimes")->Enable(false); findNamedObject(this, "ConvEditorRepeatTimes")->SetValue(-1); findNamedObject(this, "ConvEditorRepeatAdditionalText")->Enable(false); findNamedObject(this, "ConvEditorRepeatCheckbox")->SetValue(false); } // Actors for (conversation::Conversation::ActorMap::const_iterator i = _conversation.actors.begin(); i != _conversation.actors.end(); ++i) { wxutil::TreeModel::Row row = _actorStore->AddItem(); row[_actorColumns.actorNumber] = i->first; row[_actorColumns.displayName] = i->second; row.SendItemAdded(); } // Commands updateCommandList(); _updateInProgress = false; } void ConversationEditor::updateCommandList() { _commandStore->Clear(); // Commands for (conversation::Conversation::CommandMap::const_iterator i = _conversation.commands.begin(); i != _conversation.commands.end(); ++i) { const conversation::ConversationCommand& cmd = *(i->second); wxutil::TreeModel::Row row = _commandStore->AddItem(); row[_commandColumns.cmdNumber] = i->first; row[_commandColumns.actorName] = fmt::format(_("Actor {0:d}"), cmd.actor); row[_commandColumns.sentence] = removeMarkup(cmd.getSentence()); row[_commandColumns.wait] = cmd.waitUntilFinished ? _("yes") : _("no"); row.SendItemAdded(); } } void ConversationEditor::selectCommand(int index) { // Select the actor passed from the command wxDataViewItem found = _commandStore->FindInteger(index, _commandColumns.cmdNumber); _commandView->Select(found); // Update sensitivity based on the new selection _currentCommand = _commandView->GetSelection(); updateCmdActionSensitivity(_currentCommand.IsOk()); } void ConversationEditor::moveSelectedCommand(int delta) { // Get the index of the currently selected command wxutil::TreeModel::Row row(_currentCommand, *_commandStore); int index = row[_commandColumns.cmdNumber].getInteger(); int targetIndex = index + delta; if (targetIndex <= 0) { return; // can't move any more upwards } // Try to look up the command indices in the conversation conversation::Conversation::CommandMap::iterator oldCmd = _conversation.commands.find(index); conversation::Conversation::CommandMap::iterator newCmd = _conversation.commands.find(targetIndex); if (oldCmd != _conversation.commands.end() && newCmd != _conversation.commands.end()) { // There is a command at this position, swap it conversation::ConversationCommandPtr temp = newCmd->second; newCmd->second = oldCmd->second; oldCmd->second = temp; updateWidgets(); // Select the moved command, for the user's convenience selectCommand(newCmd->first); } } void ConversationEditor::save() { // Name _conversation.name = findNamedObject(this, "ConvEditorNameEntry")->GetValue(); _conversation.actorsMustBeWithinTalkdistance = findNamedObject(this, "ConvEditorActorsWithinTalkDistance")->GetValue(); _conversation.actorsAlwaysFaceEachOther = findNamedObject(this, "ConvEditorActorsMustFace")->GetValue(); if (findNamedObject(this, "ConvEditorRepeatCheckbox")->GetValue()) { _conversation.maxPlayCount = findNamedObject(this, "ConvEditorRepeatTimes")->GetValue(); } else { _conversation.maxPlayCount = -1; } // Copy the working copy over the actual object _targetConversation = _conversation; } void ConversationEditor::onSave(wxCommandEvent& ev) { // First, save to the conversation object save(); // Then close the window EndModal(wxID_OK); } void ConversationEditor::onCancel(wxCommandEvent& ev) { EndModal(wxID_CANCEL); } void ConversationEditor::onActorSelectionChanged(wxDataViewEvent& ev) { if (_updateInProgress) return; // Get the selection _currentActor = _actorView->GetSelection(); // Enable the delete buttons if we have a selection _delActorButton->Enable(_currentActor.IsOk()); } void ConversationEditor::updateCmdActionSensitivity(bool hasSelection) { // Enable the edit and delete buttons if we have a selection _editCmdButton->Enable(hasSelection); _delCmdButton->Enable(hasSelection); if (hasSelection) { // Check if this is the first command in the list, get the ID of the selected item wxutil::TreeModel::Row row(_currentCommand, *_commandStore); int index = row[_commandColumns.cmdNumber].getInteger(); bool hasNext = _conversation.commands.find(index+1) != _conversation.commands.end(); bool hasPrev = index > 1; _moveUpCmdButton->Enable(hasPrev); _moveDownCmdButton->Enable(hasNext); } else { _moveUpCmdButton->Enable(false); _moveDownCmdButton->Enable(false); } } void ConversationEditor::onCommandSelectionChanged(wxDataViewEvent& ev) { if (_updateInProgress) return; // Get the selection _currentCommand = _commandView->GetSelection(); updateCmdActionSensitivity(_currentCommand.IsOk()); } void ConversationEditor::onMaxPlayCountEnabled(wxCommandEvent& ev) { if (_updateInProgress) return; if (findNamedObject(this, "ConvEditorRepeatCheckbox")->GetValue()) { // Enabled, write a new value in the spin button findNamedObject(this, "ConvEditorRepeatTimes")->SetValue(1); findNamedObject(this, "ConvEditorRepeatTimes")->Enable(true); findNamedObject(this, "ConvEditorRepeatAdditionalText")->Enable(true); } else { // Disabled, write a -1 in the spin button findNamedObject(this, "ConvEditorRepeatTimes")->SetValue(-1); findNamedObject(this, "ConvEditorRepeatTimes")->Enable(false); findNamedObject(this, "ConvEditorRepeatAdditionalText")->Enable(false); } // Update the value in the conversation as well _conversation.maxPlayCount = findNamedObject(this, "ConvEditorRepeatTimes")->GetValue(); } void ConversationEditor::onAddActor(wxCommandEvent& ev) { // Get the lowest available actor ID int idx = 1; for (idx = 1; idx < INT_MAX; idx++) { if (_conversation.actors.find(idx) == _conversation.actors.end()) { break; } } // Add the new actor to the map _conversation.actors[idx] = _("New Actor"); // Update the widgets updateWidgets(); } void ConversationEditor::onDeleteActor(wxCommandEvent& ev) { // Get the index of the currently selected actor wxutil::TreeModel::Row row(_currentActor, *_actorStore); int index = row[_actorColumns.actorNumber].getInteger(); // Add the new actor to the map conversation::Conversation::ActorMap::iterator i = _conversation.actors.find(index); if (i != _conversation.actors.end()) { // Remove the specified actor _conversation.actors.erase(index); } else { // Index not found, quit here return; } // Adjust the numbers of all other actors with higher numbers while (_conversation.actors.find(index + 1) != _conversation.actors.end()) { // Move the actor with the higher index "down" by one number... _conversation.actors[index] = _conversation.actors[index + 1]; // ...and remove it from the old location _conversation.actors.erase(index + 1); index++; } // Update the widgets updateWidgets(); } void ConversationEditor::onValidateActors(wxCommandEvent& ev) { std::vector errors; for (const conversation::Conversation::ActorMap::value_type& i : _conversation.actors) { scene::ActorNodeFinder finder(i.second); GlobalSceneGraph().root()->traverse(finder); if (!finder.getFoundNode()) { errors.push_back(fmt::format(_("The actor {0} cannot be found in the current map."), i.second)); } } if (!errors.empty()) { std::string message = string::join(errors, "\n"); wxutil::Messagebox::Show(_("Actors missing"), message, IDialog::MESSAGE_ERROR, this); } else { std::string message = _("All actors are correctly referring to entities in the map."); wxutil::Messagebox::Show(_("Actors OK"), message, IDialog::MESSAGE_CONFIRM, this); } } void ConversationEditor::onActorEdited(wxDataViewEvent& ev) { wxutil::TreeModel::Row row(ev.GetItem(), *_actorStore); // The iter points to the edited cell now, get the actor number int actorNum = row[_actorColumns.actorNumber].getInteger(); // Update the conversation _conversation.actors[actorNum] = static_cast(ev.GetValue()); // Update all command widgets updateCommandList(); } void ConversationEditor::onAddCommand(wxCommandEvent& ev) { conversation::Conversation& conv = _conversation; // shortcut // Create a new command conversation::ConversationCommandPtr command(new conversation::ConversationCommand); // Construct a command editor (blocks on construction) CommandEditor* editor = new CommandEditor(this, *command, conv); if (editor->ShowModal() == wxID_OK) { // The user hit ok, insert the command, find the first free index int index = 1; while (conv.commands.find(index) != conv.commands.end()) { index++; } // Insert the command at the new location conv.commands[index] = command; updateWidgets(); } editor->Destroy(); } void ConversationEditor::onEditCommand(wxCommandEvent& ev) { // Get the index of the currently selected command wxutil::TreeModel::Row row(_currentCommand, *_commandStore); int index = row[_commandColumns.cmdNumber].getInteger(); // Try to look up the command in the conversation conversation::Conversation::CommandMap::iterator i = _conversation.commands.find(index); if (i != _conversation.commands.end()) { // Get the command reference conversation::ConversationCommandPtr command = i->second; // Construct a command editor (blocks on construction) CommandEditor* editor = new CommandEditor(this, *command, _conversation); if (editor->ShowModal() == wxID_OK) { updateWidgets(); } editor->Destroy(); } } void ConversationEditor::onMoveUpCommand(wxCommandEvent& ev) { // Pass the call moveSelectedCommand(-1); } void ConversationEditor::onMoveDownCommand(wxCommandEvent& ev) { // Pass the call moveSelectedCommand(+1); } void ConversationEditor::onDeleteCommand(wxCommandEvent& ev) { // Get the index of the currently selected command wxutil::TreeModel::Row row(_currentCommand, *_commandStore); int index = row[_commandColumns.cmdNumber].getInteger(); // Add the new command to the map conversation::Conversation::CommandMap::iterator i = _conversation.commands.find(index); if (i != _conversation.commands.end()) { // Remove the specified command _conversation.commands.erase(index); } else { // Index not found, quit here return; } // Adjust the numbers of all other commands with higher numbers while (_conversation.commands.find(index + 1) != _conversation.commands.end()) { // Move the commands with the higher index "down" by one number... _conversation.commands[index] = _conversation.commands[index + 1]; // ...and remove it from the old location _conversation.commands.erase(index + 1); index++; } // Update the widgets updateWidgets(); } std::string ConversationEditor::removeMarkup(const std::string& input) { std::regex expr("(<[A-Za-z]+>)|()"); return std::regex_replace(input, expr, ""); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.conversation/ConversationEditor.h000066400000000000000000000061361321750546400245030ustar00rootroot00000000000000#pragma once #include "wxutil/dialog/DialogBase.h" #include "wxutil/XmlResourceBasedWidget.h" #include "wxutil/TreeView.h" #include #include "Conversation.h" class wxButton; namespace ui { class ConversationEditor : public wxutil::DialogBase, private wxutil::XmlResourceBasedWidget { private: struct ActorListColumns : public wxutil::TreeModel::ColumnRecord { ActorListColumns() : actorNumber(add(wxutil::TreeModel::Column::Integer)), displayName(add(wxutil::TreeModel::Column::String)) {} wxutil::TreeModel::Column actorNumber; // actor number wxutil::TreeModel::Column displayName; // display name }; ActorListColumns _actorColumns; wxutil::TreeModel::Ptr _actorStore; wxutil::TreeView* _actorView; struct CommandListColumns : public wxutil::TreeModel::ColumnRecord { CommandListColumns() : cmdNumber(add(wxutil::TreeModel::Column::Integer)), actorName(add(wxutil::TreeModel::Column::String)), sentence(add(wxutil::TreeModel::Column::String)), wait(add(wxutil::TreeModel::Column::String)) {} wxutil::TreeModel::Column cmdNumber; // cmd number wxutil::TreeModel::Column actorName; // actor name wxutil::TreeModel::Column sentence; // sentence wxutil::TreeModel::Column wait; // wait yes/no }; CommandListColumns _commandColumns; wxutil::TreeModel::Ptr _commandStore; wxutil::TreeView* _commandView; wxDataViewItem _currentActor; wxDataViewItem _currentCommand; wxButton* _addActorButton; wxButton* _delActorButton; wxButton* _validateActorsButton; wxButton* _addCmdButton; wxButton* _delCmdButton; wxButton* _editCmdButton; wxButton* _moveUpCmdButton; wxButton* _moveDownCmdButton; // The conversation we're editing (the working copy) conversation::Conversation _conversation; // The actual conversation, where the changes will be saved to on "OK" conversation::Conversation& _targetConversation; // Mutex to avoid callback loops bool _updateInProgress; public: ConversationEditor(wxWindow* parent, conversation::Conversation& conversation); private: void save(); void populateWindow(); // Fills the conversation values into the widgets void updateWidgets(); void updateCommandList(); void updateCmdActionSensitivity(bool hasSelection); // Move the currently selected command about the given delta // (-1 is one upwards, +1 is one position downards) void moveSelectedCommand(int delta); // Highlight the command with the given index void selectCommand(int index); void onSave(wxCommandEvent& ev); void onCancel(wxCommandEvent& ev); void onMaxPlayCountEnabled(wxCommandEvent& ev); void onActorSelectionChanged(wxDataViewEvent& ev); void onCommandSelectionChanged(wxDataViewEvent& ev); void onAddActor(wxCommandEvent& ev); void onDeleteActor(wxCommandEvent& ev); void onValidateActors(wxCommandEvent& ev); void onActorEdited(wxDataViewEvent& ev); void onAddCommand(wxCommandEvent& ev); void onEditCommand(wxCommandEvent& ev); void onMoveUpCommand(wxCommandEvent& ev); void onMoveDownCommand(wxCommandEvent& ev); void onDeleteCommand(wxCommandEvent& ev); std::string removeMarkup(const std::string& input); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.conversation/ConversationEntity.cpp000066400000000000000000000120121321750546400250520ustar00rootroot00000000000000#include "ConversationEntity.h" #include "i18n.h" #include "itextstream.h" #include "ientity.h" #include "string/convert.h" #include "ConversationKeyExtractor.h" #include "ConversationCommandLibrary.h" namespace conversation { // Constructor ConversationEntity::ConversationEntity(const scene::INodePtr& node) : _entityNode(node) { Entity* entity = Node_getEntity(node); assert(entity != NULL); // Use an conversationKeyExtractor to populate the ConversationMap from the keys // on the entity ConversationKeyExtractor extractor(_conversations); entity->forEachKeyValue(extractor); } // Delete the entity's world node void ConversationEntity::deleteWorldNode() { // Try to convert the weak_ptr reference to a shared_ptr scene::INodePtr node = _entityNode.lock(); if (node != NULL && node->getParent() != NULL) { node->getParent()->removeChildNode(node); } } // Add a new conversation void ConversationEntity::addConversation() { // Locate the first unused id int index = 1; while (_conversations.find(index) != _conversations.end()) { ++index; } // Insert a new conversation at this ID. Conversation o; o.name = _("New Conversation"); _conversations.insert(ConversationMap::value_type(index, o)); } void ConversationEntity::deleteConversation(int index) { // Look up the conversation with the given index ConversationMap::iterator i = _conversations.find(index); if (i == _conversations.end()) { // not found, nothing to do return; } // Delete the found element _conversations.erase(i++); // Then iterate all the way to the highest index while (i != _conversations.end()) { // Decrease the index of this conversation int newIndex = i->first - 1; // Copy the conversation into a temporary object Conversation temp = i->second; // Remove the old one _conversations.erase(i++); // Re-insert with new index _conversations.insert( ConversationMap::value_type(newIndex, temp) ); } } // Populate a list store with conversations void ConversationEntity::populateListStore(wxutil::TreeModel& store, const ConversationColumns& columns) const { for (ConversationMap::const_iterator i = _conversations.begin(); i != _conversations.end(); ++i) { wxutil::TreeModel::Row row = store.AddItem(); row[columns.index] = i->first; row[columns.name] = i->second.name; row.SendItemAdded(); } } void ConversationEntity::clearEntity(Entity* entity) { // Get all keyvalues matching the "obj" prefix. Entity::KeyValuePairs keyValues = entity->getKeyValuePairs("conv_"); for (Entity::KeyValuePairs::const_iterator i = keyValues.begin(); i != keyValues.end(); ++i) { // Set the spawnarg to empty, which is equivalent to a removal entity->setKeyValue(i->first, ""); } } // Write out conversations to entity keyvals void ConversationEntity::writeToEntity() { // Try to convert the weak_ptr reference to a shared_ptr Entity* entity = Node_getEntity(_entityNode.lock()); assert(entity != NULL); // greebo: Remove all conversation-related spawnargs first clearEntity(entity); for (ConversationMap::const_iterator i = _conversations.begin(); i != _conversations.end(); ++i) { // Obtain the conversation and construct the key prefix from the index const Conversation& conv = i->second; std::string prefix = "conv_" + string::to_string(i->first) + "_"; // Set the entity keyvalues entity->setKeyValue(prefix + "name", conv.name); entity->setKeyValue(prefix + "actors_must_be_within_talkdistance", conv.actorsMustBeWithinTalkdistance ? "1" : "0"); entity->setKeyValue(prefix + "talk_distance", string::to_string(conv.talkDistance)); entity->setKeyValue(prefix + "actors_always_face_each_other_while_talking", conv.actorsAlwaysFaceEachOther ? "1" : "0"); entity->setKeyValue(prefix + "max_play_count", string::to_string(conv.maxPlayCount)); // Write the actor list for (Conversation::ActorMap::const_iterator a = conv.actors.begin(); a != conv.actors.end(); ++a) { std::string actorKey = prefix + "actor_" + string::to_string(a->first); entity->setKeyValue(actorKey, a->second); } // Write all the commands for (Conversation::CommandMap::const_iterator c = conv.commands.begin(); c != conv.commands.end(); ++c) { std::string cmdPrefix = prefix + "cmd_" + string::to_string(c->first) + "_"; try { const ConversationCommandInfo& cmdInfo = ConversationCommandLibrary::Instance().findCommandInfo(c->second->type); entity->setKeyValue(cmdPrefix + "type", cmdInfo.name); entity->setKeyValue(cmdPrefix + "actor", string::to_string(c->second->actor)); entity->setKeyValue(cmdPrefix + "wait_until_finished", c->second->waitUntilFinished ? "1" : "0"); for (ConversationCommand::ArgumentMap::const_iterator a = c->second->arguments.begin(); a != c->second->arguments.end(); ++a) { entity->setKeyValue(cmdPrefix + "arg_" + string::to_string(a->first), a->second); } } catch (std::runtime_error e) { rError() << "Unrecognised conversation command ID: " << c->second->type << std::endl; } } } } } // namespace conversations DarkRadiant-2.5.0/plugins/dm.conversation/ConversationEntity.h000066400000000000000000000073451321750546400245340ustar00rootroot00000000000000#pragma once #include "inode.h" #include "ientity.h" #include #include "wxutil/TreeModel.h" #include "Conversation.h" namespace conversation { struct ConvEntityColumns : public wxutil::TreeModel::ColumnRecord { ConvEntityColumns() : displayName(add(wxutil::TreeModel::Column::String)), entityName(add(wxutil::TreeModel::Column::String)) {} wxutil::TreeModel::Column displayName; wxutil::TreeModel::Column entityName; }; struct ConversationColumns : public wxutil::TreeModel::ColumnRecord { ConversationColumns() : index(add(wxutil::TreeModel::Column::Integer)), name(add(wxutil::TreeModel::Column::String)) {} wxutil::TreeModel::Column index; // conversation index wxutil::TreeModel::Column name; // conversation name }; /** * Representation of a single conversation entity (atdm:conversation_info). * * In The Dark Mod, conversations are stored as numbered spawnargs on a conversation * entity, e.g. conv_1_name. Each conversation entity can contain any number * of conversations described in this way. * * The ConversationEntity class provides an object-oriented view of the conversation * information, by wrapping a pointer to an Entity and providing methods to * retrieve and manipulate conversation information without seeing the spawnargs * directly. When changes are completed, the ConversationEntity::writeToEntity() * method is invoked to save all changes in the form of spawnargs. * * @see Entity */ class ConversationEntity { // The actual entity's world node and entity pointer scene::INodeWeakPtr _entityNode; // Indexed conversations ConversationMap _conversations; public: /** * Construct an ConversationEntity wrapper around the given Node. */ ConversationEntity(const scene::INodePtr& node); /** * Return an Conversation reference by numeric index. * * @param iIndex * The numberic index of the conversation to retrieve. * * @return * A non-const reference to an Conversation object, corresponding to the * given index. If the provided index did not previously exist, a new * Conversation object will be created and returned. */ Conversation& getConversation(int iIndex) { return _conversations[iIndex]; } /** * Add a new conversation, starting from the first unused conversation ID. */ void addConversation(); /** * Delete a numbered conversation. This re-orders all conversations so that the * numbering is consistent again (deleting obj 2 will re-number 3 => 2, 4 => 3, etc.) */ void deleteConversation(int index); /** * Clear all conversations. */ void clearConversations() { _conversations.clear(); } /** * Test whether this entity contains conversations or not. */ bool isEmpty() const { return _conversations.empty(); } /** * Delete the actual entity node from the map. This will render any further * operations on this ConversationEntity undefined, and it should immediately * be deleted. */ void deleteWorldNode(); /** * Populate the given list store with the conversations from this entity. * * @param store * The list store to populate. This must have 2 columns -- an integer * column for the conversation number, and a text column for the name. */ void populateListStore(wxutil::TreeModel& store, const ConversationColumns& columns) const; /** * Write all conversation data to keyvals on the underlying entity. */ void writeToEntity(); private: // Removes all conversation-related spawnargs from the given entity void clearEntity(Entity* entity); }; /** * Conversation entity pointer type. */ typedef std::shared_ptr ConversationEntityPtr; /** * Conversation entity named map type. */ typedef std::map ConversationEntityMap; } // namespace conversation DarkRadiant-2.5.0/plugins/dm.conversation/ConversationEntityFinder.h000066400000000000000000000053021321750546400256530ustar00rootroot00000000000000#pragma once #include "i18n.h" #include #include "ConversationEntity.h" namespace conversation { /** * Visitor class to locate and list any atdm:conversation_info entities in * the current map. * * The ConversationEntityFinder will visit each scenegraph node in turn, as per the * behaviour of a scenegraph walker. The classname of each entity visited is * tested against a given value (passed in during construction) which identifies * it as a Conversation entity, and if the test is successful, the entity's * details are added to the target ConversationEntityMap and TreeModel objects * to be populated. */ class ConversationEntityFinder : public scene::NodeVisitor { // Name of entity class we are looking for std::string _className; // ListStore to populate with results wxutil::TreeModel::Ptr _store; const ConvEntityColumns& _columns; // ConversationEntityMap which we also populate ConversationEntityMap& _map; public: /** * Construct a visitor to populate the given store and ConversationEntityMap. * * The TreeModel provided must contain two columns. The first column * is a string containing the display name of the conversation entity, * which is constructed from the real entity name plus the origin in * brackets for convenience purposes. The second column is a string * containing the raw entity name in the map. * * @param st * The TreeModel to populate. * * @param map * The ConversationEntityMap to populate. * * @param classname * The text classname used to identify a Conversation entity. */ ConversationEntityFinder(wxutil::TreeModel::Ptr st, const ConvEntityColumns& columns, ConversationEntityMap& map, const std::string& classname) : _className(classname), _store(st), _columns(columns), _map(map) {} /** * NodeVisitor implementation */ bool pre(const scene::INodePtr& node) { // Get the entity and check the classname Entity* entity = Node_getEntity(node); // Check for conversation entity or worldspawn if (entity != NULL && entity->getKeyValue("classname") == _className) { // Construct the display string std::string name = entity->getKeyValue("name"); std::string sDisplay = fmt::format(_("{0} at [ {1} ]"), name, entity->getKeyValue("origin")); // Add the entity to the list wxutil::TreeModel::Row row = _store->AddItem(); row[_columns.displayName] = sDisplay; row[_columns.entityName] = name; row.SendItemAdded(); // Construct an ObjectiveEntity with the node, and add to the map ConversationEntityPtr ce(new ConversationEntity(node)); _map.insert(ConversationEntityMap::value_type(name, ce)); } return true; } }; } // namespace conversation DarkRadiant-2.5.0/plugins/dm.conversation/ConversationKeyExtractor.cpp000066400000000000000000000062661321750546400262400ustar00rootroot00000000000000#include "ConversationKeyExtractor.h" #include "itextstream.h" #include #include "string/convert.h" #include "ConversationCommand.h" #include "ConversationCommandLibrary.h" namespace conversation { // Required entity visit function void ConversationKeyExtractor::operator()(const std::string& key, const std::string& value) { // Quick discard of any non-conversation keys if (key.substr(0, 4) != "conv") return; // Extract the objective number static const std::regex reConvNum("conv_(\\d+)_(.*)"); std::smatch results; int iNum; if (!std::regex_match(key, results, reConvNum)) { // No match, abort return; } // Get the conversation number iNum = string::convert(results[1].str()); // We now have the conversation number and the substring (everything after // "conv__" which applies to this conversation. std::string convSubString = results[2]; // Switch on the substring if (convSubString == "name") { _convMap[iNum].name = value; } else if (convSubString == "talk_distance") { _convMap[iNum].talkDistance = string::convert(value, 60); } else if (convSubString == "actors_must_be_within_talkdistance") { _convMap[iNum].actorsMustBeWithinTalkdistance = (value == "1"); } else if (convSubString == "actors_always_face_each_other_while_talking") { _convMap[iNum].actorsAlwaysFaceEachOther = (value == "1"); } else if (convSubString == "max_play_count") { _convMap[iNum].maxPlayCount = string::convert(value, -1); } else if (convSubString.substr(0, 6) == "actor_") { // This is an actor definition, extract the number int actorNum = string::convert(convSubString.substr(6), -1); if (actorNum == -1) { return; } // Store the actor in the map _convMap[iNum].actors.insert(Conversation::ActorMap::value_type(actorNum, value)); } else if (convSubString.substr(0, 4) == "cmd_") { // This is a conversation command, form a new regex static const std::regex reCommand("cmd_(\\d+)_(.*)"); std::smatch results; if (!std::regex_match(convSubString, results, reCommand)) { return; // not matching } int cmdIndex = string::convert(results[1].str()); std::string cmdSubStr = results[2]; ConversationCommandPtr command; Conversation::CommandMap::iterator found = _convMap[iNum].commands.find(cmdIndex); if (found != _convMap[iNum].commands.end()) { // Command already exists command = found->second; } else { // Command with the given index does not exist yet, create it command = ConversationCommandPtr(new ConversationCommand); // Insert this into the map _convMap[iNum].commands[cmdIndex] = command; } if (cmdSubStr == "type") { try { command->type = ConversationCommandLibrary::Instance().findCommandInfo(value).id; } catch (std::runtime_error& e) { rError() << e.what() << std::endl; return; } } else if (cmdSubStr == "actor") { command->actor = string::convert(value); } else if (cmdSubStr == "wait_until_finished") { command->waitUntilFinished = (value == "1"); } else if (cmdSubStr.substr(0,4) == "arg_") { int cmdArgIndex = string::convert(cmdSubStr.substr(4)); command->arguments[cmdArgIndex] = value; } } } } // namespace conversation DarkRadiant-2.5.0/plugins/dm.conversation/ConversationKeyExtractor.h000066400000000000000000000012341321750546400256730ustar00rootroot00000000000000#pragma once #include "Conversation.h" #include "ientity.h" namespace conversation { /** * Entity Visitor which extracts conversation keyvalues (of the form "conv__yyyy") * and populates the given ConversationMap with the parsed conversation objects. */ class ConversationKeyExtractor { // Map of number->Conversation objects ConversationMap& _convMap; public: /** * Constructor. Sets the map to populate. */ ConversationKeyExtractor(ConversationMap& map) : _convMap(map) { assert(_convMap.empty()); } /** * Required visit function. */ void operator()(const std::string& key, const std::string& value); }; } // namespace conversation DarkRadiant-2.5.0/plugins/dm.conversation/Makefile.am000066400000000000000000000015761321750546400225500ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs \ $(XML_CFLAGS) pluginsdir = $(pkglibdir)/plugins plugins_LTLIBRARIES = dm_conversation.la dm_conversation_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \ $(top_builddir)/libs/xmlutil/libxmlutil.la dm_conversation_la_LDFLAGS = -module -avoid-version \ $(WX_LIBS) $(XML_LIBS) dm_conversation_la_SOURCES = plugin.cpp \ ConversationKeyExtractor.cpp \ ConversationCommandInfo.cpp \ CommandArgumentItem.cpp \ ConversationEntity.cpp \ CommandEditor.cpp \ ConversationEditor.cpp \ ConversationCommand.cpp \ ConversationCommandLibrary.cpp \ ConversationDialog.cpp DarkRadiant-2.5.0/plugins/dm.conversation/plugin.cpp000066400000000000000000000033431321750546400225100ustar00rootroot00000000000000#include "imodule.h" #include "i18n.h" #include "itextstream.h" #include "ieventmanager.h" #include "icommandsystem.h" #include "iuimanager.h" #include "generic/callback.h" #include "debugging/debugging.h" #include "ConversationDialog.h" /** * Module to register the menu commands for the Conversation Editor class. */ class ConversationEditorModule : public RegisterableModule { public: // RegisterableModule implementation virtual const std::string& getName() const { static std::string _name("ConversationEditor"); return _name; } virtual const StringSet& getDependencies() const { static StringSet _dependencies; if (_dependencies.empty()) { _dependencies.insert(MODULE_EVENTMANAGER); _dependencies.insert(MODULE_UIMANAGER); _dependencies.insert(MODULE_COMMANDSYSTEM); } return _dependencies; } virtual void initialiseModule(const ApplicationContext& ctx) { rMessage() << getName() << "::initialiseModule called.\n"; // Add the callback event GlobalCommandSystem().addCommand("ConversationEditor", ui::ConversationDialog::ShowDialog); GlobalEventManager().addCommand("ConversationEditor", "ConversationEditor"); // Add the menu item IMenuManager& mm = GlobalUIManager().getMenuManager(); mm.add("main/map", // menu location path "ConversationEditor", // name ui::menuItem, // type _("Conversations..."), // caption "stimresponse.png", // icon "ConversationEditor"); // event name } }; typedef std::shared_ptr ConversationEditorModulePtr; extern "C" void DARKRADIANT_DLLEXPORT RegisterModule(IModuleRegistry& registry) { module::performDefaultInitialisation(registry); registry.registerModule(ConversationEditorModulePtr(new ConversationEditorModule)); } DarkRadiant-2.5.0/plugins/dm.difficulty/000077500000000000000000000000001321750546400201335ustar00rootroot00000000000000DarkRadiant-2.5.0/plugins/dm.difficulty/ClassNameStore.cpp000066400000000000000000000014701321750546400235240ustar00rootroot00000000000000#include "ClassNameStore.h" namespace ui { ClassNameStore::ClassNameStore() { populateListStore(); } const wxArrayString& ClassNameStore::getStringList() const { return _classNames; } void ClassNameStore::destroy() { InstancePtr().reset(); } ClassNameStore& ClassNameStore::Instance() { if (InstancePtr() == NULL) { InstancePtr().reset(new ClassNameStore); } return *InstancePtr(); } ClassNameStorePtr& ClassNameStore::InstancePtr() { static ClassNameStorePtr _instancePtr; return _instancePtr; } // EntityClassVisitor implementation void ClassNameStore::visit(const IEntityClassPtr& eclass) { _classNames.Add(eclass->getName()); } void ClassNameStore::populateListStore() { // Visit each entity class using as visitor GlobalEntityClassManager().forEachEntityClass(*this); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.difficulty/ClassNameStore.h000066400000000000000000000013051321750546400231660ustar00rootroot00000000000000#pragma once #include "ieclass.h" #include #include namespace ui { class ClassNameStore; typedef std::shared_ptr ClassNameStorePtr; class ClassNameStore : private EntityClassVisitor { private: wxArrayString _classNames; public: // Constructor, traverses the eclasses and fills the GtkListStore ClassNameStore(); const wxArrayString& getStringList() const; static ClassNameStore& Instance(); static void destroy(); private: static ClassNameStorePtr& InstancePtr(); // EntityClassVisitor implementation virtual void visit(const IEntityClassPtr& eclass); // Traverses all entities and fills the store void populateListStore(); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultyDialog.cpp000066400000000000000000000061101321750546400240570ustar00rootroot00000000000000#include "DifficultyDialog.h" #include "i18n.h" #include "iundo.h" #include "imainframe.h" #include "iuimanager.h" #include "iscenegraph.h" #include "gamelib.h" #include "registry/registry.h" #include "string/string.h" #include #include #include #include #include #include namespace ui { namespace { const char* const WINDOW_TITLE = N_("Difficulty Editor"); } DifficultyDialog::DifficultyDialog() : DialogBase(_(WINDOW_TITLE)) { // Load the settings _settingsManager.loadSettings(); // Create the widgets populateWindow(); } void DifficultyDialog::createDifficultyEditors() { int numLevels = game::current::getValue(GKEY_DIFFICULTY_LEVELS); for (int i = 0; i < numLevels; i++) { // Acquire the settings object difficulty::DifficultySettingsPtr settings = _settingsManager.getSettings(i); if (settings != NULL) { _editors.push_back( DifficultyEditorPtr(new DifficultyEditor( _notebook, _settingsManager.getDifficultyName(i), settings) ) ); } } // A new image list for the notebook tab icons _imageList.reset(new wxImageList(16, 16)); // Pack the editors into the notebook for (std::size_t i = 0; i < _editors.size(); i++) { DifficultyEditor& editor = *_editors[i]; wxWindow* editorWidget = editor.getEditor(); std::string icon = editor.getNotebookIconName(); // Load the icon int imageId = icon.empty() ? -1 : _imageList->Add(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + icon)); editorWidget->Reparent(_notebook); _notebook->AddPage(editorWidget, editor.getNotebookLabel(), false, imageId); } } void DifficultyDialog::populateWindow() { SetSizer(new wxBoxSizer(wxVERTICAL)); // Create the notebook and add it to the vbox _notebook = new wxNotebook(this, wxID_ANY); _notebook->SetMinClientSize(wxSize(800, 400)); // Create and pack the editors createDifficultyEditors(); GetSizer()->Add(_notebook, 1, wxEXPAND | wxALL, 12); wxButton* okButton = new wxButton(this, wxID_OK); wxButton* cancelButton = new wxButton(this, wxID_CANCEL); okButton->Bind(wxEVT_BUTTON, [&] (wxCommandEvent&) { EndModal(wxID_OK); }); cancelButton->Bind(wxEVT_BUTTON, [&] (wxCommandEvent&) { EndModal(wxID_CANCEL); }); wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL); buttonSizer->Add(cancelButton, 0, wxRIGHT, 6); buttonSizer->Add(okButton, 0, wxRIGHT, 6); GetSizer()->Add(buttonSizer, 0, wxALIGN_RIGHT | wxALL, 12); Layout(); Fit(); } void DifficultyDialog::save() { // Consistency check can go here // Scoped undo object UndoableCommand command("editDifficulty"); // Save the working set to the entity _settingsManager.saveSettings(); } int DifficultyDialog::ShowModal() { int returnCode = DialogBase::ShowModal(); if (returnCode == wxID_OK) { save(); } return returnCode; } // Static command target void DifficultyDialog::ShowDialog(const cmd::ArgumentList& args) { // Construct a new instance, this enters the main loop DifficultyDialog* editor = new DifficultyDialog; editor->ShowModal(); editor->Destroy(); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultyDialog.h000066400000000000000000000023331321750546400235270ustar00rootroot00000000000000#pragma once #include "ientity.h" #include "iradiant.h" #include "icommandsystem.h" #include "wxutil/dialog/DialogBase.h" #include "DifficultyEditor.h" #include "DifficultySettingsManager.h" #include #include namespace ui { class DifficultyDialog; typedef std::shared_ptr DifficultyDialogPtr; /** * greebo: A difficulty dialog is a modal top-level window which provides * views and controls facilitating the editing of difficulty settings. * * Maintains a certain number of DifficultyEditors which get packed into the * notebook tabs. */ class DifficultyDialog : public wxutil::DialogBase { private: wxNotebook* _notebook; // The difficulty settings manager difficulty::DifficultySettingsManager _settingsManager; std::vector _editors; std::unique_ptr _imageList; public: DifficultyDialog(); int ShowModal(); // Command target to toggle the dialog static void ShowDialog(const cmd::ArgumentList& args); private: // greebo: Saves the current working set to the entity void save(); // WIDGET POPULATION void populateWindow(); // Main window void createDifficultyEditors(); }; // class DifficultyDialog } // namespace ui DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultyEditor.cpp000066400000000000000000000211561321750546400241150ustar00rootroot00000000000000#include "DifficultyEditor.h" #include "i18n.h" #include "iuimanager.h" #include "wxutil/TreeView.h" #include "wxutil/ChoiceHelper.h" #include #include #include #include #include #include #include #include "ClassNameStore.h" namespace ui { namespace { const char* const DIFF_ICON = "sr_icon_custom.png"; } DifficultyEditor::DifficultyEditor(wxWindow* parent, const std::string& label, const difficulty::DifficultySettingsPtr& settings) : _settings(settings), _label(label), _settingsView(nullptr), _classCombo(nullptr), _spawnArgEntry(nullptr), _argumentEntry(nullptr), _appTypeCombo(nullptr), _saveSettingButton(nullptr), _deleteSettingButton(nullptr), _createSettingButton(nullptr), _refreshButton(nullptr), _noteText(nullptr), _updateActive(false) { // The actual editor pane _editor = loadNamedPanel(parent, "DifficultyEditorMainPanel"); _settings->updateTreeModel(); populateWindow(); updateEditorWidgets(); } wxWindow* DifficultyEditor::getEditor() { return _editor; } std::string DifficultyEditor::getNotebookLabel() { return _label; } std::string DifficultyEditor::getNotebookIconName() { return DIFF_ICON; } void DifficultyEditor::populateWindow() { wxPanel* viewPanel = findNamedObject(_editor, "DifficultyEditorTreeViewPanel"); _settingsView = wxutil::TreeView::CreateWithModel(viewPanel, _settings->getTreeStore()); _settingsView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(DifficultyEditor::onSettingSelectionChange), NULL, this); viewPanel->GetSizer()->Add(_settingsView, 1, wxEXPAND); _settingsView->AppendTextColumn(_("Setting"), _settings->getColumns().description.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT); // Save a few shortcuts _spawnArgEntry = findNamedObject(_editor, "DifficultyEditorSpawnarg"); _argumentEntry = findNamedObject(_editor, "DifficultyEditorArgument"); _classCombo = findNamedObject(_editor, "DifficultyEditorClassName"); _classCombo->Append(ClassNameStore::Instance().getStringList()); _classCombo->AutoComplete(ClassNameStore::Instance().getStringList()); // AppTypes _appTypeCombo = findNamedObject(_editor, "DifficultyEditorApplicationType"); _appTypeCombo->Append(_("Assign"), new wxStringClientData(string::to_string(difficulty::Setting::EAssign))); _appTypeCombo->Append(_("Add"), new wxStringClientData(string::to_string(difficulty::Setting::EAdd))); _appTypeCombo->Append(_("Multiply"), new wxStringClientData(string::to_string(difficulty::Setting::EMultiply))); _appTypeCombo->Append(_("Ignore"), new wxStringClientData(string::to_string(difficulty::Setting::EIgnore))); _appTypeCombo->Connect(wxEVT_CHOICE, wxCommandEventHandler(DifficultyEditor::onAppTypeChange), NULL, this); // Buttons _saveSettingButton = findNamedObject(_editor, "DifficultyEditorSaveSettingButton"); _saveSettingButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(DifficultyEditor::onSettingSave), NULL, this); _deleteSettingButton = findNamedObject(_editor, "DifficultyEditorDeleteSettingButton"); _deleteSettingButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(DifficultyEditor::onSettingDelete), NULL, this); _createSettingButton = findNamedObject(_editor, "DifficultyEditorAddSettingButton"); _createSettingButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(DifficultyEditor::onSettingCreate), NULL, this); _refreshButton = findNamedObject(_editor, "DifficultyEditorRefreshButton"); _refreshButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(DifficultyEditor::onRefresh), NULL, this); _noteText = findNamedObject(_editor, "DifficultyEditorNoteText"); makeLabelBold(_editor, "DifficultyEditorSettingLabel"); } int DifficultyEditor::getSelectedSettingId() { wxDataViewItem item = _settingsView->GetSelection(); if (item.IsOk()) { wxutil::TreeModel::Row row(item, *_settingsView->GetModel()); return row[_settings->getColumns().settingId].getInteger(); } return -1; } void DifficultyEditor::updateEditorWidgets() { _updateActive = true; int id = getSelectedSettingId(); bool editWidgetsSensitive = false; std::string noteText; if (id != -1) { // Lookup the setting using className/id combo difficulty::SettingPtr setting = _settings->getSettingById(id); if (setting != NULL) { // Activate editing pane editWidgetsSensitive = true; if (_settings->isOverridden(setting)) { editWidgetsSensitive = false; noteText += _("This default setting is overridden, cannot edit."); } _spawnArgEntry->SetValue(setting->spawnArg); _argumentEntry->SetValue(setting->argument); // Now select the eclass passed in the argument // Find the entity using a TreeModel traversor _classCombo->Select(_classCombo->FindString(setting->className)); wxutil::ChoiceHelper::SelectItemByStoredId(_appTypeCombo, static_cast(setting->appType)); // Set the sensitivity of the argument entry box _argumentEntry->Enable( (setting->appType == difficulty::Setting::EIgnore) ? false : true ); // We have a treeview selection, lock the classname _classCombo->Enable(false); // Disable the deletion of default settings _deleteSettingButton->Enable((setting->isDefault) ? false : true); _saveSettingButton->Enable(true); } } else { // Nothing selected, disable deletion _deleteSettingButton->Enable(false); _saveSettingButton->Enable(false); } // Set editing pane sensitivity findNamedObject(_editor, "DifficultyEditorSettingsPanel")->Enable(editWidgetsSensitive); // Set the note text in any case _noteText->SetLabelMarkup(noteText); _noteText->Wrap(_noteText->GetSize().GetWidth()); _updateActive = false; } void DifficultyEditor::createSetting() { // Unselect everything _settingsView->UnselectAll(); // Unlock editing widgets findNamedObject(_editor, "DifficultyEditorSettingsPanel")->Enable(true); // Unlock class combo _classCombo->Enable(true); _saveSettingButton->Enable(true); _spawnArgEntry->SetValue(""); _argumentEntry->SetValue(""); } void DifficultyEditor::saveSetting() { // Get the ID of the currently selected item (might be -1 if no selection) int id = getSelectedSettingId(); // Instantiate a new setting and fill the data in difficulty::SettingPtr setting(new difficulty::Setting); // Load the widget contents setting->className = _classCombo->GetStringSelection(); setting->spawnArg = _spawnArgEntry->GetValue(); setting->argument = _argumentEntry->GetValue(); // Get the apptype from the dropdown list setting->appType = difficulty::Setting::EAssign; if (_appTypeCombo->GetSelection() != wxNOT_FOUND) { int selected = wxutil::ChoiceHelper::GetSelectionId(_appTypeCombo); setting->appType = static_cast(selected); } // Pass the data to the DifficultySettings class to handle it id = _settings->save(id, setting); // Update the treemodel _settings->updateTreeModel(); // Highlight the setting, it might have been newly created selectSettingById(id); } void DifficultyEditor::deleteSetting() { // Get the ID of the currently selected item (might be -1 if no selection) int id = getSelectedSettingId(); // Instantiate a new setting and fill the data in difficulty::SettingPtr setting = _settings->getSettingById(id); if (setting == NULL || setting->isDefault) { // Don't delete NULL or default settings return; } // Remove the setting _settings->deleteSetting(id); } void DifficultyEditor::selectSettingById(int id) { wxDataViewItem found = _settings->getTreeStore()->FindInteger(id, _settings->getColumns().settingId); _settingsView->Select(found); _settingsView->EnsureVisible(found); } void DifficultyEditor::onSettingSelectionChange(wxDataViewEvent& ev) { // Update editor widgets updateEditorWidgets(); } void DifficultyEditor::onSettingSave(wxCommandEvent& ev) { saveSetting(); } void DifficultyEditor::onSettingDelete(wxCommandEvent& ev) { deleteSetting(); } void DifficultyEditor::onSettingCreate(wxCommandEvent& ev) { createSetting(); } void DifficultyEditor::onRefresh(wxCommandEvent& ev) { _settings->refreshTreeModel(); } void DifficultyEditor::onAppTypeChange(wxCommandEvent& ev) { if (_updateActive) return; // Update the sensitivity of the argument entry widget int selected = wxutil::ChoiceHelper::GetSelectionId(_appTypeCombo); difficulty::Setting::EApplicationType appType = static_cast(selected); _argumentEntry->Enable( (appType == difficulty::Setting::EIgnore) ? false : true ); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultyEditor.h000066400000000000000000000052401321750546400235560ustar00rootroot00000000000000#pragma once #include #include #include "DifficultySettings.h" #include "wxutil/XmlResourceBasedWidget.h" class wxPanel; class wxTextCtrl; class wxChoice; class wxButton; class wxStaticText; class wxComboBox; namespace wxutil { class TreeView; } namespace ui { /** * greebo: A Difficulty Editor provides the front-end for editing a * set of Difficulty Settings (e.g. "Easy" setting). The actual * data is stored in a DifficultySettings instance. */ class DifficultyEditor : public wxEvtHandler, private wxutil::XmlResourceBasedWidget { private: // The actual settings we're working with difficulty::DifficultySettingsPtr _settings; // GtkNotebook-related widgets wxPanel* _editor; std::string _label; // the actual label wxutil::TreeView* _settingsView; // The classname dropdown entry field wxComboBox* _classCombo; wxTextCtrl* _spawnArgEntry; wxTextCtrl* _argumentEntry; wxChoice* _appTypeCombo; wxButton* _saveSettingButton; wxButton* _deleteSettingButton; wxButton* _createSettingButton; wxButton* _refreshButton; // A label containing notes to the user wxStaticText* _noteText; // Mutex for avoiding loopbacks bool _updateActive; public: /** * greebo: Pass the label string and the difficulty settings object to the * constructor. The DifficultySettings should be populated first. */ DifficultyEditor(wxWindow* parent, const std::string& label, const difficulty::DifficultySettingsPtr& settings); // Returns the actual editor widget (contains all controls and views) wxWindow* getEditor(); // Returns the label for packing into a GtkNotebook tab. std::string getNotebookLabel(); std::string getNotebookIconName(); private: // Creates the widgets void populateWindow(); // Returns the ID of the selected setting (or -1) if no valid setting is selected int getSelectedSettingId(); // Loads the data from the treeview selection into the editor widgets void updateEditorWidgets(); // Prepares the widgets for addition of a new setting void createSetting(); // Saves the setting data from the widgets to the DifficultySettings object void saveSetting(); // Removes the setting selected in the treeview void deleteSetting(); // Highlights the setting (according to the given ) in the treeview void selectSettingById(int id); // Callback for treeview selection changes void onSettingSelectionChange(wxDataViewEvent& ev); void onSettingCreate(wxCommandEvent& ev); void onSettingSave(wxCommandEvent& ev); void onSettingDelete(wxCommandEvent& ev); void onRefresh(wxCommandEvent& ev); void onAppTypeChange(wxCommandEvent& ev); }; typedef std::shared_ptr DifficultyEditorPtr; } // namespace ui DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultyEntity.cpp000066400000000000000000000020471321750546400241410ustar00rootroot00000000000000#include "DifficultyEntity.h" #include "ientity.h" #include "string/convert.h" namespace difficulty { DifficultyEntity::DifficultyEntity(Entity* source) : _entity(source), _curId(0) {} void DifficultyEntity::clear() { // Find all spawnargs starting with a "diff_" Entity::KeyValuePairs pairs = _entity->getKeyValuePairs("diff_"); // Remove all (set the value to the empty string "") for (Entity::KeyValuePairs::iterator i = pairs.begin(); i != pairs.end(); i++) { _entity->setKeyValue(i->first, ""); } _curId = 0; } void DifficultyEntity::writeSetting(const SettingPtr& setting, int _level) { // Construct the prefix and index strings std::string prefix = "diff_" + string::to_string(_level) + "_"; std::string idx = string::to_string(_curId); // Save the spawnargs _entity->setKeyValue(prefix + "class_" + idx, setting->className); _entity->setKeyValue(prefix + "change_" + idx, setting->spawnArg); _entity->setKeyValue(prefix + "arg_" + idx, setting->getArgumentKeyValue()); // Increase the ID _curId++; } } // namespace difficulty DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultyEntity.h000066400000000000000000000014051321750546400236030ustar00rootroot00000000000000#ifndef DIFFICULTY_ENTITY_H_ #define DIFFICULTY_ENTITY_H_ #include "ientity.h" #include "Setting.h" namespace difficulty { /** * greebo: A DifficultyEntity encapsulates a doom3-compatible map entity * and provides methods to save/load difficulty settings to/from spawargs. */ class DifficultyEntity { // The actual entity we're working with Entity* _entity; // The unique ID needed to write the spawnargs int _curId; public: DifficultyEntity(Entity* source); // Removes all difficulty settings from the entity void clear(); // Write the setting to this entity void writeSetting(const SettingPtr& setting, int _level); }; typedef std::shared_ptr DifficultyEntityPtr; } // namespace difficulty #endif /* DIFFICULTY_ENTITY_H_ */ DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultyEntityFinder.h000066400000000000000000000020641321750546400247350ustar00rootroot00000000000000#pragma once #include "ientity.h" #include "gamelib.h" #include "DifficultySettings.h" namespace difficulty { class DifficultyEntityFinder : public scene::NodeVisitor { public: // Found difficulty entities are stored in this list typedef std::vector EntityList; private: // The entityClass name to search for std::string _entityClassName; // The list of found entities EntityList _foundEntities; public: DifficultyEntityFinder() : _entityClassName(game::current::getValue(GKEY_DIFFICULTY_ENTITYDEF_MAP)) {} // Return the list of found entites const EntityList& getEntities() const { return _foundEntities; } // Walker implementation bool pre(const scene::INodePtr& node) { // Check if we have an entity Entity* entity = Node_getEntity(node); if (entity != NULL) { // Check for a classname match if (entity->getKeyValue("classname") == _entityClassName) { _foundEntities.push_back(entity); } // Don't traverse the children of entities return false; } return true; } }; } // namespace difficulty DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultySettings.cpp000066400000000000000000000307411321750546400244670ustar00rootroot00000000000000#include "DifficultySettings.h" #include "i18n.h" #include "string/convert.h" #include "eclass.h" namespace difficulty { DifficultySettings::DifficultySettings(int level) : _level(level), _store(new wxutil::TreeModel(_columns)) {} DifficultySettings::~DifficultySettings() {} const DifficultySettings::TreeModelColumns& DifficultySettings::getColumns() const { return _columns; } wxutil::TreeModel::Ptr DifficultySettings::getTreeStore() const { return _store; } int DifficultySettings::getLevel() const { return _level; } void DifficultySettings::clear() { _settings.clear(); _settingIds.clear(); _iterMap.clear(); } SettingPtr DifficultySettings::getSettingById(int id) const { // Search all stored settings matching this classname SettingIdMap::const_iterator found = _settingIds.find(id); if (found != _settingIds.end()) { return found->second; } return SettingPtr(); // not found } SettingPtr DifficultySettings::findOrCreateOverrule(const SettingPtr& existing) { // Get the inheritancekey needed to lookup the classname std::string inheritanceKey = getInheritanceKey(existing->className); // Check if there is already an override active for the setting for (SettingsMap::iterator i = _settings.find(inheritanceKey); i != _settings.upper_bound(inheritanceKey) && i != _settings.end(); ++i) { // Avoid self==self comparisons if (i->second != existing && i->second->spawnArg == existing->spawnArg) { // spawnarg is set on a different setting, check if it is non-default if (!i->second->isDefault) { // non-default, overriding setting, return it return i->second; } } } // No overriding setting found, create a new one SettingPtr setting = createSetting(existing->className); setting->spawnArg = existing->spawnArg; setting->isDefault = false; setting->appType = Setting::EAssign; return setting; } int DifficultySettings::save(int id, const SettingPtr& setting) { if (id != -1) { // We're dealing with an existing setting, fetch it SettingPtr existing = getSettingById(id); if (existing == NULL) { return -1; } if (existing->isDefault) { // We're trying to save a default setting, go into override mode if (*setting == *existing) { // New settings are identical to the existing ones, skip return existing->id; } // Create a new setting SettingPtr overrule = findOrCreateOverrule(existing); // Transfer the argument/appType into the new setting overrule->argument = setting->argument; overrule->appType = setting->appType; return overrule->id; } else { // TODO: Check, if there is another, existing setting which we // override with this one and whether they are the same // Copy the settings over to the existing setting *existing = *setting; return existing->id; } } else { // No setting highlighted, create a new one SettingPtr newSetting = createSetting(setting->className); // Copy the settings over *newSetting = *setting; newSetting->isDefault = false; return newSetting->id; } } void DifficultySettings::deleteSetting(int id) { for (SettingsMap::iterator i = _settings.begin(); i != _settings.end(); ++i) { if (i->second->id == id) { // Found it, remove it from the tree and all maps _store->RemoveItem(i->second->iter); _settings.erase(i); _settingIds.erase(id); break; } } // Override settings might have been changed, update the model updateTreeModel(); } void DifficultySettings::updateTreeModel() { // Go through the settings and check the corresponding iters in the tree for (SettingsMap::iterator i = _settings.begin(); i != _settings.end(); ++i) { const std::string& className = i->second->className; Setting& setting = *i->second; // Ensure that the classname is in the map wxDataViewItem classIter = findOrInsertClassname(className); bool settingAdded = false; if (!setting.iter.IsOk()) { // No iter corresponding to this setting yet, insert it setting.iter = classIter.IsOk() ? _store->AddItem(classIter).getItem() : _store->AddItem().getItem(); settingAdded = true; } wxutil::TreeModel::Row row(setting.iter, *_store); bool overridden = isOverridden(i->second); wxDataViewItemAttr colour; colour.SetColour(setting.isDefault ? wxColor(112,112,112) : wxColor(0,0,0)); row[_columns.description] = setting.getDescString() + (overridden ? _(" (overridden)") : ""); row[_columns.description] = colour; row[_columns.classname] = setting.className; row[_columns.settingId] = setting.id; row[_columns.isOverridden] = overridden; if (settingAdded) { row.SendItemAdded(); } else { row.SendItemChanged(); } } } void DifficultySettings::clearTreeModel() { _iterMap.clear(); _store->Clear(); // Clear any iterators stored in the settings for (SettingsMap::iterator i = _settings.begin(); i != _settings.end(); ++i) { i->second->iter = wxDataViewItem(); } } void DifficultySettings::refreshTreeModel() { clearTreeModel(); updateTreeModel(); } bool DifficultySettings::isOverridden(const SettingPtr& setting) { if (!setting->isDefault) { return false; // not a default setting, return false } // Get the inheritancekey needed to lookup the classname std::string inheritanceKey = getInheritanceKey(setting->className); // Search all other settings for the same className/spawnArg combination for (SettingsMap::iterator i = _settings.find(inheritanceKey); i != _settings.upper_bound(inheritanceKey) && i != _settings.end(); ++i) { // Avoid self==self comparisons if (i->second != setting && i->second->spawnArg == setting->spawnArg) { // spawnarg is set on a different setting, check if it is non-default if (!i->second->isDefault) { // non-default, overriding setting, return true return true; } } } return false; } std::string DifficultySettings::getParentClass(const std::string& className) { // Get the parent eclass IEntityClassPtr eclass = GlobalEntityClassManager().findClass(className); if (eclass == NULL) { return ""; // Invalid! } EntityClassAttribute inheritAttr = eclass->getAttribute("inherit"); return inheritAttr.getValue(); } wxDataViewItem DifficultySettings::findOrInsertClassname(const std::string& className) { // Try to look up the classname in the tree TreeIterMap::iterator found = _iterMap.find(className); if (found != _iterMap.end()) { // Name exists, return this return found->second; } // This iter will hold the parent element, if such is found wxDataViewItem parentIter; // Classname is not yet registered, walk up the inheritance tree std::string parentClassName = getParentClass(className); while (!parentClassName.empty()) { // Try to look up the classname in the tree TreeIterMap::iterator found = _iterMap.find(parentClassName); if (found != _iterMap.end()) { parentIter = found->second; break; } parentClassName = getParentClass(parentClassName); } // Insert the map, using the found iter (or NULL, if nothing was found) wxDataViewItem inserted = insertClassName(className, parentIter); // Remember the iter _iterMap.insert(TreeIterMap::value_type(className, inserted)); return inserted; } wxDataViewItem DifficultySettings::insertClassName(const std::string& className, const wxDataViewItem& parent) { wxutil::TreeModel::Row row = parent.IsOk() ? _store->AddItem(parent) : _store->AddItem(); wxDataViewItemAttr black; black.SetColour(wxColor(0,0,0)); row[_columns.description] = className; row[_columns.description] = black; row[_columns.classname] = className; row[_columns.settingId] = -1; row.SendItemAdded(); return row.getItem(); } std::string DifficultySettings::getInheritanceKey(const std::string& className) { if (className.empty()) return ""; IEntityClassPtr eclass = GlobalEntityClassManager().findClass(className); // Get the inheritance chain of this class std::list inheritanceChain; for (const IEntityClass* currentClass = eclass.get(); currentClass != NULL; currentClass = currentClass->getParent()) { inheritanceChain.push_front(currentClass->getName()); } // Build the inheritance key std::string inheritanceKey; for (std::list::const_iterator c = inheritanceChain.begin(); c != inheritanceChain.end(); c++) { inheritanceKey += (inheritanceKey.empty()) ? "" : "_"; inheritanceKey += *c; } return inheritanceKey; } SettingPtr DifficultySettings::createSetting(const std::string& className) { SettingPtr setting(new Setting); setting->className = className; // Insert the parsed setting into our local map _settings.insert(SettingsMap::value_type(getInheritanceKey(className), setting)); _settingIds.insert(SettingIdMap::value_type(setting->id, setting)); return setting; } void DifficultySettings::parseFromEntityDef(const IEntityClassPtr& def) { // Construct the prefix for the desired difficulty level std::string diffPrefix = "diff_" + string::to_string(_level) + "_"; std::string prefix = diffPrefix + "change_"; eclass::AttributeList spawnargs = eclass::getSpawnargsWithPrefix(*def, prefix); for (eclass::AttributeList::iterator i = spawnargs.begin(); i != spawnargs.end(); ++i) { EntityClassAttribute& attr = *i; if (attr.getValue().empty()) { continue; // empty spawnarg attribute => invalid } // Get the index from the string's tail std::string indexStr = attr.getName().substr(prefix.length()); const EntityClassAttribute& classAttr = def->getAttribute(diffPrefix + "class_" + indexStr); const EntityClassAttribute& argAttr = def->getAttribute(diffPrefix + "arg_" + indexStr); SettingPtr setting = createSetting(classAttr.getValue()); setting->spawnArg = attr.getValue(); setting->argument = argAttr.getValue(); // This has been parsed from the default entityDef setting->isDefault = true; // Interpret/parse the argument string setting->parseAppType(); } clearTreeModel(); updateTreeModel(); } void DifficultySettings::parseFromMapEntity(Entity* entity) { // Construct the prefix for the desired difficulty level std::string diffPrefix = "diff_" + string::to_string(_level) + "_"; std::string prefix = diffPrefix + "change_"; Entity::KeyValuePairs spawnargs = entity->getKeyValuePairs(prefix); for (Entity::KeyValuePairs::iterator i = spawnargs.begin(); i != spawnargs.end(); ++i) { const std::string& key = i->first; const std::string& value = i->second; if (value.empty()) { continue; // empty spawnarg attribute => invalid } // Get the index from the string's tail std::string indexStr = key.substr(prefix.length()); std::string className = entity->getKeyValue(diffPrefix + "class_" + indexStr); SettingPtr setting = createSetting(className); setting->spawnArg = value; setting->argument = entity->getKeyValue(diffPrefix + "arg_" + indexStr); // This has been parsed from the default entityDef setting->isDefault = false; // Interpret/parse the argument string setting->parseAppType(); } clearTreeModel(); updateTreeModel(); } void DifficultySettings::saveToEntity(DifficultyEntity& target) { // Cycle through all settings for (SettingsMap::iterator i = _settings.begin(); i != _settings.end(); ++i) { const SettingPtr& setting = i->second; if (setting->isDefault) { // Don't save default settings continue; } // Write the setting to the entity target.writeSetting(setting, _level); } } } // namespace difficulty DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultySettings.h000066400000000000000000000112231321750546400241260ustar00rootroot00000000000000#pragma once #include "ieclass.h" #include "entitylib.h" #include #include #include "wxutil/TreeModel.h" #include "DifficultyEntity.h" #include "Setting.h" namespace { const std::string GKEY_DIFFICULTY_LEVELS("/difficulty/numLevels"); const std::string GKEY_DIFFICULTY_ENTITYDEF_DEFAULT("/difficulty/defaultSettingsEclass"); const std::string GKEY_DIFFICULTY_ENTITYDEF_MAP("/difficulty/mapSettingsEclass"); const std::string GKEY_DIFFICULTY_ENTITYDEF_MENU("/difficulty/difficultyMenuEclass"); } namespace difficulty { class DifficultySettings { public: struct TreeModelColumns : public wxutil::TreeModel::ColumnRecord { TreeModelColumns() : description(add(wxutil::TreeModel::Column::String)), classname(add(wxutil::TreeModel::Column::String)), settingId(add(wxutil::TreeModel::Column::Integer)), isOverridden(add(wxutil::TreeModel::Column::Boolean)) {} wxutil::TreeModel::Column description; wxutil::TreeModel::Column classname; wxutil::TreeModel::Column settingId; wxutil::TreeModel::Column isOverridden; }; private: // the difficulty level, these settings are referring to int _level; // The settings map associates classnames with spawnarg change records. // Multiple settings can be made for a single classname. typedef std::multimap SettingsMap; SettingsMap _settings; // A mapping between Setting ID and Setting (for faster lookup on GTK selection) typedef std::map SettingIdMap; SettingIdMap _settingIds; // This maps classnames to wxDataViewItems, for faster lookup typedef std::map TreeIterMap; TreeIterMap _iterMap; // The treemodel TreeModelColumns _columns; // This class holds a reference to the treemodel wxutil::TreeModel::Ptr _store; public: // Define the difficulty level in the constructor DifficultySettings(int level); ~DifficultySettings(); const TreeModelColumns& getColumns() const; // Get the treestore pointer for packing into a treeview // Note that this class will keep a reference to the model wxutil::TreeModel::Ptr getTreeStore() const; // Returns the Setting associated with the given className // and carrying the given ID (returned pointer might be NULL) SettingPtr getSettingById(int id) const; // Returns the level these settings are referring to int getLevel() const; // Empties the internal structures void clear(); /** * greebo: Saves the given setting. The ID specifices the setting currently * highlighted in the editor (pass -1 if nothing is selected). * * @returns: the ID of the saved Setting, can be used to select it. */ int save(int id, const SettingPtr& setting); // Removes the setting with the given ID void deleteSetting(int id); // Loads the data into the given treestore void updateTreeModel(); // Loads all settings (matching the internal _level) from the given entityDef. void parseFromEntityDef(const IEntityClassPtr& def); // Loads all settings (matching the internal _level) from the given entity. void parseFromMapEntity(Entity* entity); // Save the relevant settings to the given difficulty entity void saveToEntity(DifficultyEntity& target); // Returns true if the given setting is overridden by map-specific settings bool isOverridden(const SettingPtr& setting); // Clears and reloads the treemodel void refreshTreeModel(); private: // Clears the tree data void clearTreeModel(); // Get the inheritance key of the given classname (for sorting in the map) std::string getInheritanceKey(const std::string& className); // Creates a new setting (and updates the internal structures) // This needs the classname as argument (for the internal mapping) SettingPtr createSetting(const std::string& className); /** * greebo: Finds or creates a setting overruling the given one. */ SettingPtr findOrCreateOverrule(const SettingPtr& existing); /** * greebo: Returns the TreeIter pointing to the tree element in . * If the item is not yet existing, it gets inserted into the tree, according * to its inheritance tree. */ wxDataViewItem findOrInsertClassname(const std::string& className); /** * greebo: Inserts the given classname into the treestore, using the * given iter as insertion point. If is invalid, * the entry is inserted at root level. */ wxDataViewItem insertClassName(const std::string& className, const wxDataViewItem& parent = wxDataViewItem()); // returns the parent eclass name for the given or "" if no parent std::string getParentClass(const std::string& className); }; typedef std::shared_ptr DifficultySettingsPtr; } // namespace difficulty DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultySettingsManager.cpp000066400000000000000000000125471321750546400257660ustar00rootroot00000000000000#include "DifficultySettingsManager.h" #include "ieclass.h" #include "itextstream.h" #include "entitylib.h" #include "gamelib.h" #include "string/string.h" #include "registry/registry.h" #include "DifficultyEntity.h" #include "DifficultyEntityFinder.h" namespace difficulty { DifficultySettingsPtr DifficultySettingsManager::getSettings(int level) { for (std::size_t i = 0; i < _settings.size(); i++) { if (_settings[i]->getLevel() == level) { return _settings[i]; } } return DifficultySettingsPtr(); } void DifficultySettingsManager::loadSettings() { loadDefaultSettings(); loadMapSettings(); loadDifficultyNames(); } void DifficultySettingsManager::loadDefaultSettings() { // Try to lookup the given entityDef IEntityClassPtr eclass = GlobalEntityClassManager().findClass( game::current::getValue(GKEY_DIFFICULTY_ENTITYDEF_DEFAULT) ); if (eclass == NULL) { rError() << "Could not find default difficulty settings entityDef.\n"; return; } // greebo: Setup the default difficulty levels using the found entityDef int numLevels = game::current::getValue(GKEY_DIFFICULTY_LEVELS); for (int i = 0; i < numLevels; i++) { // Allocate a new settings object DifficultySettingsPtr settings(new DifficultySettings(i)); // Parse the settings from the given default settings entityDef settings->parseFromEntityDef(eclass); // Store the settings object in the local list _settings.push_back(settings); } } void DifficultySettingsManager::loadMapSettings() { // Construct a helper walker DifficultyEntityFinder finder; GlobalSceneGraph().root()->traverse(finder); const DifficultyEntityFinder::EntityList& found = finder.getEntities(); // Pop all entities into each difficulty setting for (DifficultyEntityFinder::EntityList::const_iterator ent = found.begin(); ent != found.end(); ent++) { for (std::size_t i = 0; i < _settings.size(); i++) { _settings[i]->parseFromMapEntity(*ent); } } } void DifficultySettingsManager::loadDifficultyNames() { // Locate the worldspawn entity Entity* worldspawn = Scene_FindEntityByClass("worldspawn"); // Try to locate the difficulty menu entity, where the default names are defined IEntityClassPtr eclass = GlobalEntityClassManager().findClass( game::current::getValue(GKEY_DIFFICULTY_ENTITYDEF_MENU) ); // greebo: Setup the default difficulty levels using the found entityDef int numLevels = game::current::getValue(GKEY_DIFFICULTY_LEVELS); for (int i = 0; i < numLevels; i++) { std::string nameKey = "diff" + string::to_string(i) + "default"; // First, try to find a map-specific name if (worldspawn != NULL) { std::string name = worldspawn->getKeyValue(nameKey); if (!name.empty()) { // Found a setting on worldspawn, take it _difficultyNames.push_back(name); continue; // done for this level } } // If the above failed, try to load the default setting if (eclass != NULL) { EntityClassAttribute attr = eclass->getAttribute(nameKey); if (!attr.getValue().empty()) { _difficultyNames.push_back(attr.getValue()); continue; } } // Fall back to a non-empty default _difficultyNames.push_back(string::to_string(i)); } } void DifficultySettingsManager::saveSettings() { // Locates all difficulty entities DifficultyEntityFinder finder; GlobalSceneGraph().root()->traverse(finder); // Copy the list from the finder to a local list DifficultyEntityFinder::EntityList entities = finder.getEntities(); if (entities.empty()) { // Create a new difficulty entity std::string eclassName = game::current::getValue(GKEY_DIFFICULTY_ENTITYDEF_MAP); IEntityClassPtr diffEclass = GlobalEntityClassManager().findClass(eclassName); if (diffEclass == NULL) { rError() << "[Diff]: Cannot create difficulty entity!\n"; return; } // Create and insert a new entity node into the scenegraph root IEntityNodePtr entNode = GlobalEntityCreator().createEntity(diffEclass); GlobalSceneGraph().root()->addChildNode(entNode); // Add the entity to the list entities.push_back(&entNode->getEntity()); } // Clear all difficulty-spawnargs from existing entities for (DifficultyEntityFinder::EntityList::const_iterator i = entities.begin(); i != entities.end(); i++) { // Construct a difficulty entity using the raw Entity* pointer DifficultyEntity diffEnt(*i); // Clear the difficulty-related spawnargs from the entity diffEnt.clear(); } // Take the first entity DifficultyEntity diffEnt(*entities.begin()); // Cycle through all settings objects and issue save call for (std::size_t i = 0; i < _settings.size(); i++) { _settings[i]->saveToEntity(diffEnt); } } std::string DifficultySettingsManager::getDifficultyName(int level) { if (level < 0 || level >= static_cast(_difficultyNames.size())) { return ""; } return _difficultyNames[level]; } } // namespace difficulty DarkRadiant-2.5.0/plugins/dm.difficulty/DifficultySettingsManager.h000066400000000000000000000022601321750546400254220ustar00rootroot00000000000000#ifndef DIFFICULTY_SETTINGS_MANAGER_H_ #define DIFFICULTY_SETTINGS_MANAGER_H_ #include "DifficultySettings.h" namespace difficulty { /** * greebo: The DifficultyManager contains all settings of all * available difficulty levels. The manager also provides * methods to fill in the settings data into a given * GtkTreeModel and to save the existing settings to the map/entityDef. **/ class DifficultySettingsManager { // This contains all the settings of all the difficulty levels std::vector _settings; // The names of the difficulty levels std::vector _difficultyNames; public: // Loads all settings from the entityDefs and the currently loaded map. void loadSettings(); // Saves the current working set into one map entity void saveSettings(); // Get the settings object for the given difficulty DifficultySettingsPtr getSettings(int level); // Get the display name of the given difficulty level std::string getDifficultyName(int level); private: void loadDefaultSettings(); void loadMapSettings(); void loadDifficultyNames(); }; } // namespace difficulty #endif /* DIFFICULTY_SETTINGS_MANAGER_H_ */ DarkRadiant-2.5.0/plugins/dm.difficulty/Makefile.am000066400000000000000000000011531321750546400221670ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs \ $(XML_CFLAGS) pluginsdir = $(pkglibdir)/plugins plugins_LTLIBRARIES = dm_difficulty.la dm_difficulty_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \ $(top_builddir)/libs/xmlutil/libxmlutil.la dm_difficulty_la_LDFLAGS = -module -avoid-version $(WX_LIBS) $(XML_LIBS) dm_difficulty_la_SOURCES = DifficultyEditor.cpp \ plugin.cpp \ DifficultySettingsManager.cpp \ Setting.cpp \ DifficultySettings.cpp \ ClassNameStore.cpp \ DifficultyDialog.cpp \ DifficultyEntity.cpp DarkRadiant-2.5.0/plugins/dm.difficulty/Setting.cpp000066400000000000000000000044351321750546400222620ustar00rootroot00000000000000#include "Setting.h" #include "i18n.h" #include "iregistry.h" #include "gamelib.h" namespace difficulty { namespace { const std::string GKEY_APPTYPE_IGNORE("/difficulty/appTypeIgnore"); } Setting::Setting() : id(++_highestId), isDefault(false) {} std::string Setting::getArgumentKeyValue() const { std::string returnValue(argument); switch (appType) { case EAssign: break; case EAdd: returnValue = "+" + returnValue; break; case EMultiply: returnValue = "*" + returnValue; break; case EIgnore: returnValue = game::current::getValue(GKEY_APPTYPE_IGNORE); break; default: break; }; return returnValue; } std::string Setting::getDescString() const { std::string returnValue(argument); switch (appType) { case EAssign: returnValue = " = " + returnValue; break; case EAdd: returnValue = " += " + returnValue; break; case EMultiply: returnValue = " *= " + returnValue; break; case EIgnore: returnValue = " = [IGNORE]"; break; default: break; }; returnValue = spawnArg + returnValue; return returnValue; } bool Setting::operator==(const Setting& rhs) const { return className == rhs.className && spawnArg == rhs.spawnArg && argument == rhs.argument && appType == rhs.appType; } bool Setting::operator!=(const Setting& rhs) const { return !operator==(rhs); } Setting& Setting::operator=(const Setting& rhs) { className = rhs.className; spawnArg = rhs.spawnArg; argument = rhs.argument; appType = rhs.appType; isDefault = rhs.isDefault; return *this; } void Setting::parseAppType() { appType = EAssign; if (!argument.empty()) { // Check for ignore argument if (argument == game::current::getValue(GKEY_APPTYPE_IGNORE)) { appType = EIgnore; argument.clear(); // clear the argument } // Check for special modifiers else if (argument[0] == '+') { appType = EAdd; // Remove the first character argument = argument.substr(1); } else if (argument[0] == '*') { appType = EMultiply; // Remove the first character argument = argument.substr(1); } else if (argument[0] == '-') { appType = EAdd; // Leave the "-" sign, it will be the sign of the parsed int } } } // Initialise the static member int Setting::_highestId = 0; } // namespace difficulty DarkRadiant-2.5.0/plugins/dm.difficulty/Setting.h000066400000000000000000000030571321750546400217260ustar00rootroot00000000000000#pragma once #include #include #include namespace difficulty { /** * greebo: A Setting represents a spawnarg change. * This can be an assignment, addition, multiplication or NOP (ignore). */ class Setting { public: enum EApplicationType { EAssign, EAdd, EMultiply, EIgnore, ENumAppTypes, }; // ID of this setting (unique for each difficulty level) int id; // The classname this setting applies to std::string className; // The target spawnarg to be changed std::string spawnArg; // The parsed argument (the specifier (+/*) has already been removed) std::string argument; // How the argument should be applied EApplicationType appType; // Whether this setting is a default setting or map-specific bool isDefault; // The tree iter this setting is stored at wxDataViewItem iter; // Constructor (assigns a unique ID automatically) Setting(); // Comparison operators, return true if spawnarg/classname/argument/appType are the same bool operator==(const Setting& rhs) const; bool operator!=(const Setting& rhs) const; // Assignment operator (leaves the ID untouched) Setting& operator=(const Setting& rhs); // Interprets the argument string void parseAppType(); // Get the string suitable for saving to a spawnarg (e.g. "+50") std::string getArgumentKeyValue() const; // Assemble a description string for the contained spawnArg/argument combo. std::string getDescString() const; private: static int _highestId; }; typedef std::shared_ptr SettingPtr; } // namespace difficulty DarkRadiant-2.5.0/plugins/dm.difficulty/plugin.cpp000066400000000000000000000035031321750546400221360ustar00rootroot00000000000000#include "imodule.h" #include "i18n.h" #include "ieventmanager.h" #include "itextstream.h" #include "ieclass.h" #include "iscenegraph.h" #include "iuimanager.h" #include "iregistry.h" #include "iselection.h" #include "iradiant.h" #include "iundo.h" #include "ClassNameStore.h" #include "DifficultyDialog.h" /** * Module to register the menu commands for the Difficulty Editor class. */ class DifficultyEditorModule : public RegisterableModule { public: // RegisterableModule implementation const std::string& getName() const { static std::string _name("DifficultyEditor"); return _name; } const StringSet& getDependencies() const { static StringSet _dependencies; if (_dependencies.empty()) { _dependencies.insert(MODULE_EVENTMANAGER); _dependencies.insert(MODULE_UIMANAGER); _dependencies.insert(MODULE_COMMANDSYSTEM); } return _dependencies; } void initialiseModule(const ApplicationContext& ctx) { rMessage() << getName() << "::initialiseModule called." << std::endl; // Add the callback event GlobalCommandSystem().addCommand("DifficultyEditor", ui::DifficultyDialog::ShowDialog); GlobalEventManager().addCommand("DifficultyEditor", "DifficultyEditor"); // Add the menu item IMenuManager& mm = GlobalUIManager().getMenuManager(); mm.add("main/map", // menu location path "DifficultyEditor", // name ui::menuItem, // type _("Difficulty..."), // caption "stimresponse.png", // icon "DifficultyEditor"); // event name } void shutdownModule() { ui::ClassNameStore::destroy(); } }; typedef std::shared_ptr DifficultyEditorModulePtr; extern "C" void DARKRADIANT_DLLEXPORT RegisterModule(IModuleRegistry& registry) { module::performDefaultInitialisation(registry); registry.registerModule(DifficultyEditorModulePtr(new DifficultyEditorModule)); } DarkRadiant-2.5.0/plugins/dm.editing/000077500000000000000000000000001321750546400174145ustar00rootroot00000000000000DarkRadiant-2.5.0/plugins/dm.editing/AIEditingPanel.cpp000066400000000000000000000402601321750546400226770ustar00rootroot00000000000000#include "AIEditingPanel.h" #include "i18n.h" #include "iselection.h" #include "ieclass.h" #include "itextstream.h" #include "ientityinspector.h" #include "iradiant.h" #include "iundo.h" #include "igroupdialog.h" #include "iuimanager.h" #include "selectionlib.h" #include "SpawnargLinkedCheckbox.h" #include "SpawnargLinkedSpinButton.h" #include #include #include #include #include #include #include #include namespace ui { AIEditingPanel::AIEditingPanel() : _tempParent(new wxFrame(NULL, wxID_ANY, "")), _mainPanel(new wxScrolledWindow(_tempParent, wxID_ANY)), _queueUpdate(true), _entity(nullptr) { _tempParent->SetName("AIEditingPanelTemporaryParent"); _tempParent->Hide(); _mainPanel->Connect(wxEVT_PAINT, wxPaintEventHandler(AIEditingPanel::OnPaint), NULL, this); constructWidgets(); GlobalRadiant().signal_radiantShutdown().connect( sigc::mem_fun(*this, &AIEditingPanel::onRadiantShutdown) ); _selectionChangedSignal = GlobalSelectionSystem().signal_selectionChanged().connect( sigc::mem_fun(*this, &AIEditingPanel::onSelectionChanged) ); } void AIEditingPanel::constructWidgets() { _mainPanel->SetScrollRate(0, 3); _mainPanel->SetSizer(new wxBoxSizer(wxVERTICAL)); wxBoxSizer* vbox = new wxBoxSizer(wxVERTICAL); _mainPanel->GetSizer()->Add(vbox, 1, wxEXPAND | wxALL, 12); // Populate the map with all the widgets linked to 1/0 spawnargs _checkboxes["canOperateDoors"] = new SpawnargLinkedCheckbox(_mainPanel, _("Can operate Doors"), "canOperateDoors"); _checkboxes["canLightTorches"] = new SpawnargLinkedCheckbox(_mainPanel, _("Can light Torches"), "canLightTorches"); _checkboxes["canOperateSwitchLights"] = new SpawnargLinkedCheckbox(_mainPanel, _("Can operate Switch Lights"), "canOperateSwitchLights"); _checkboxes["canOperateElevators"] = new SpawnargLinkedCheckbox(_mainPanel, _("Can operate Elevators"), "canOperateElevators"); _checkboxes["canGreet"] = new SpawnargLinkedCheckbox(_mainPanel, _("Can greet others"), "canGreet"); _checkboxes["canSearch"] = new SpawnargLinkedCheckbox(_mainPanel, _("Can search"), "canSearch"); _checkboxes["is_civilian"] = new SpawnargLinkedCheckbox(_mainPanel, _("AI is civilian"), "is_civilian"); _checkboxes["sleeping"] = new SpawnargLinkedCheckbox(_mainPanel, _("Start sleeping"), "sleeping"); _checkboxes["lay_down_left"] = new SpawnargLinkedCheckbox(_mainPanel, _("Lay down to the left"), "lay_down_left"); _checkboxes["sitting"] = new SpawnargLinkedCheckbox(_mainPanel, _("Start sitting"), "sitting"); _checkboxes["patrol"] = new SpawnargLinkedCheckbox(_mainPanel, _("Patrol"), "patrol"); _checkboxes["animal_patrol"] = new SpawnargLinkedCheckbox(_mainPanel, _("Animal Patrol Mode"), "animal_patrol"); _checkboxes["alert_idle"] = new SpawnargLinkedCheckbox(_mainPanel, _("Start in Alert Idle State"), "alert_idle"); _checkboxes["disable_alert_idle"] = new SpawnargLinkedCheckbox(_mainPanel, _("Disable Alert Idle State"), "disable_alert_idle"); _checkboxes["drunk"] = new SpawnargLinkedCheckbox(_mainPanel, _("Drunk"), "drunk"); _checkboxes["shoulderable"] = new SpawnargLinkedCheckbox(_mainPanel, _("Body is shoulderable"), "shoulderable"); _checkboxes["neverdormant"] = new SpawnargLinkedCheckbox(_mainPanel, _("AI doesn't think outside the player PVS"), "neverdormant", true); // inverse logic _checkboxes["can_drown"] = new SpawnargLinkedCheckbox(_mainPanel, _("AI can drown"), "can_drown"); _checkboxes["can_drown"]->setDefaultValueForMissingKeyValue(true); _checkboxes["can_be_flatfooted"] = new SpawnargLinkedCheckbox(_mainPanel, _("AI can be flatfooted"), "can_be_flatfooted"); _checkboxes["ko_alert_immune"] = new SpawnargLinkedCheckbox(_mainPanel, _("AI is immune to KOs at high alert levels"), "ko_alert_immune"); _checkboxes["ko_immune"] = new SpawnargLinkedCheckbox(_mainPanel, _("AI is immune to KOs"), "ko_immune"); _checkboxes["gas_immune"] = new SpawnargLinkedCheckbox(_mainPanel, _("AI is immune to Gas"), "gas_immune"); _spinButtons["team"] = new SpawnargLinkedSpinButton(_mainPanel, _("Team"), "team", 0, 99, 1, 0); _spinButtons["rank"] = new SpawnargLinkedSpinButton(_mainPanel, _("Rank"), "rank", 0, 99, 1, 0); _spinButtons["sit_down_angle"] = new SpawnargLinkedSpinButton(_mainPanel, _("Sitting Angle"), "sit_down_angle", -179, 180, 1, 0); _spinButtons["drunk_acuity_factor"] = new SpawnargLinkedSpinButton(_mainPanel, _("Drunk Acuity Factor"), "drunk_acuity_factor", 0, 10, 0.1, 2); _spinButtons["acuity_vis"] = new SpawnargLinkedSpinButton(_mainPanel, _("Visual Acuity"), "acuity_vis", 0, 100, 1, 0); _spinButtons["acuity_aud"] = new SpawnargLinkedSpinButton(_mainPanel, _("Audio Acuity"), "acuity_aud", 0, 100, 1, 0); _spinButtons["fov"] = new SpawnargLinkedSpinButton(_mainPanel, _("Horizontal FOV"), "fov", 0, 360, 1, 0); _spinButtons["fov_vert"] = new SpawnargLinkedSpinButton(_mainPanel, _("Vertical FOV"), "fov_vert", 0, 180, 1, 0); _spinButtons["min_interleave_think_dist"] = new SpawnargLinkedSpinButton(_mainPanel, _("Min. Interleave Distance"), "min_interleave_think_dist", 0, 60000, 50, 0); _spinButtons["max_interleave_think_dist"] = new SpawnargLinkedSpinButton(_mainPanel, _("Max. Interleave Distance"), "max_interleave_think_dist", 0, 60000, 50, 0); _spinButtons["health"] = new SpawnargLinkedSpinButton(_mainPanel, _("Health"), "health", 0, 9999999, 5, 0); _spinButtons["health_critical"] = new SpawnargLinkedSpinButton(_mainPanel, _("Critical Health"), "health_critical", 0, 1000, 5, 0); _spinButtons["melee_range"] = new SpawnargLinkedSpinButton(_mainPanel, _("Melee Range"), "melee_range", 0, 200, 1, 0); { // Appearance widgets vbox->Add(createSectionLabel(_("Appearance")), 0, wxTOP | wxBOTTOM, 6); wxFlexGridSizer* table = new wxFlexGridSizer(3, 3, 4, 12); vbox->Add(table, 0, wxEXPAND | wxLEFT, 18); createChooserRow(table, _("Skin: "), _("Choose skin..."), "icon_skin.png", "skin"); createChooserRow(table, _("Head: "), _("Choose AI head..."), "icon_model.png", "def_head"); createChooserRow(table, _("Vocal Set: "), _("Choose Vocal Set..."), "icon_sound.png", "def_vocal_set"); } { // Behaviour widgets vbox->Add(createSectionLabel(_("Behaviour")), 0, wxTOP | wxBOTTOM, 6); wxGridSizer* table = new wxGridSizer(10, 2, 4, 12); vbox->Add(table, 0, wxLEFT, 18); // Team & Rank table->Add(createSpinButtonHbox(_spinButtons["team"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); table->Add(createSpinButtonHbox(_spinButtons["rank"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); // Civilian flag table->Add(_checkboxes["is_civilian"], 0, wxALIGN_CENTER_VERTICAL); table->Add(new wxPanel(_mainPanel, wxID_ANY), 0); // empty dummy // Sitting table->Add(_checkboxes["sitting"], 0, wxALIGN_CENTER_VERTICAL); table->Add(createSpinButtonHbox(_spinButtons["sit_down_angle"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); // Sleeping table->Add(_checkboxes["sleeping"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["lay_down_left"], 0, wxALIGN_CENTER_VERTICAL); // Patrolling table->Add(_checkboxes["patrol"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["animal_patrol"], 0, wxALIGN_CENTER_VERTICAL); // Acuity table->Add(createSpinButtonHbox(_spinButtons["acuity_vis"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); table->Add(createSpinButtonHbox(_spinButtons["acuity_aud"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); // FOV table->Add(createSpinButtonHbox(_spinButtons["fov"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); table->Add(createSpinButtonHbox(_spinButtons["fov_vert"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); // Drunk table->Add(_checkboxes["drunk"], 0, wxALIGN_CENTER_VERTICAL); table->Add(createSpinButtonHbox(_spinButtons["drunk_acuity_factor"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); // Alert Idle Control table->Add(_checkboxes["alert_idle"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["disable_alert_idle"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["shoulderable"], 0, wxALIGN_CENTER_VERTICAL); } { // Abilities widgets vbox->Add(createSectionLabel(_("Abilities")), 0, wxTOP | wxBOTTOM, 6); wxGridSizer* table = new wxGridSizer(3, 2, 4, 12); vbox->Add(table, 0, wxLEFT, 18); table->Add(_checkboxes["canOperateDoors"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["canOperateElevators"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["canLightTorches"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["canOperateSwitchLights"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["canGreet"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["canSearch"], 0, wxALIGN_CENTER_VERTICAL); } { // Optimization widgets vbox->Add(createSectionLabel(_("Optimization")), 0, wxTOP | wxBOTTOM, 6); wxGridSizer* table = new wxGridSizer(3, 1, 4, 12); vbox->Add(table, 0, wxLEFT, 18); table->Add(_checkboxes["neverdormant"], 0, wxALIGN_CENTER_VERTICAL); table->Add(createSpinButtonHbox(_spinButtons["min_interleave_think_dist"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); table->Add(createSpinButtonHbox(_spinButtons["max_interleave_think_dist"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); } { // Health / Combat widgets vbox->Add(createSectionLabel(_("Health / Combat")), 0, wxTOP | wxBOTTOM, 6); wxGridSizer* table = new wxGridSizer(5, 2, 4, 12); vbox->Add(table, 0, wxLEFT, 18); table->Add(createSpinButtonHbox(_spinButtons["health"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); table->Add(createSpinButtonHbox(_spinButtons["health_critical"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); table->Add(createSpinButtonHbox(_spinButtons["melee_range"]), 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); table->Add(_checkboxes["can_drown"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["can_be_flatfooted"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["ko_immune"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["gas_immune"], 0, wxALIGN_CENTER_VERTICAL); table->Add(_checkboxes["ko_alert_immune"], 0, wxALIGN_CENTER_VERTICAL); } } wxSizer* AIEditingPanel::createSpinButtonHbox(SpawnargLinkedSpinButton* spinButton) { wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL); wxStaticText* label = new wxStaticText(spinButton->GetParent(), wxID_ANY, spinButton->getLabel() + ":"); hbox->Add(label, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6); hbox->Add(spinButton, 0, wxALIGN_CENTER_VERTICAL); return hbox; } void AIEditingPanel::createChooserRow(wxSizer* table, const std::string& rowLabel, const std::string& buttonLabel, const std::string& buttonIcon, const std::string& key) { table->Add(new wxStaticText(_mainPanel, wxID_ANY, rowLabel), 0, wxALIGN_CENTER_VERTICAL); _labels[key] = new wxStaticText(_mainPanel, wxID_ANY, ""); table->Add(_labels[key], 1, wxALIGN_CENTER_VERTICAL); // Create the skin browse button wxButton* browseButton = new wxButton(_mainPanel, wxID_ANY, buttonLabel); browseButton->SetBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + buttonIcon)); browseButton->Bind(wxEVT_BUTTON, std::bind(&AIEditingPanel::onBrowseButton, this, std::placeholders::_1, key)); table->Add(browseButton, 0, wxALIGN_RIGHT); } wxStaticText* AIEditingPanel::createSectionLabel(const std::string& text) { wxStaticText* label = new wxStaticText(_mainPanel, wxID_ANY, text); label->SetFont(label->GetFont().Bold()); return label; } AIEditingPanel& AIEditingPanel::Instance() { AIEditingPanelPtr& instance = InstancePtr(); if (!instance) { instance.reset(new AIEditingPanel); } return *instance; } void AIEditingPanel::Shutdown() { if (InstancePtr() != NULL) { //Instance().shutdown(); InstancePtr().reset(); } } void AIEditingPanel::onRadiantStartup() { IGroupDialog::PagePtr page(new IGroupDialog::Page); page->name = "aieditingpanel"; page->windowLabel = _("AI"); page->page = Instance()._mainPanel; page->tabIcon = "icon_ai.png"; page->tabLabel = _("AI"); page->position = IGroupDialog::Page::Position::MediaBrowser - 10; // Add the page GlobalGroupDialog().addPage(page); // Delete the temporary parent Instance()._tempParent->Destroy(); Instance()._tempParent = nullptr; Instance()._undoHandler = GlobalUndoSystem().signal_postUndo().connect( sigc::mem_fun(Instance(), &AIEditingPanel::updateWidgetsFromSelection)); Instance()._redoHandler = GlobalUndoSystem().signal_postRedo().connect( sigc::mem_fun(Instance(), &AIEditingPanel::updateWidgetsFromSelection)); } void AIEditingPanel::onRadiantShutdown() { _undoHandler.disconnect(); _redoHandler.disconnect(); _selectionChangedSignal.disconnect(); } Entity* AIEditingPanel::getEntityFromSelection() { Entity* entity = NULL; const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo(); if (info.entityCount == 1 && info.totalCount == info.entityCount) { GlobalSelectionSystem().foreachSelected([&] (const scene::INodePtr& node) { Entity* candidate = Node_getEntity(node); if (candidate->isOfType("atdm:ai_base")) { entity = candidate; } }); } return entity; } void AIEditingPanel::updatePanelSensitivity() { _mainPanel->Enable(_entity != nullptr); _mainPanel->Layout(); } void AIEditingPanel::onKeyInsert(const std::string& key, EntityKeyValue& value) { // On entity key change, we load all values afresh updateWidgetsFromSelection(); } void AIEditingPanel::onKeyChange(const std::string& key, const std::string& val) { // On entity key change, we load all values afresh updateWidgetsFromSelection(); } void AIEditingPanel::onKeyErase(const std::string& key, EntityKeyValue& value) { // On entity key change, we load all values afresh updateWidgetsFromSelection(); } void AIEditingPanel::postUndo() { updateWidgetsFromSelection(); } void AIEditingPanel::postRedo() { updateWidgetsFromSelection(); } void AIEditingPanel::updateWidgetsFromSelection() { // Write the entity object to each checkbox even if it is NULL // This will update the widget state from the entity's properties std::for_each(_checkboxes.begin(), _checkboxes.end(), [&] (CheckboxMap::value_type& pair) { pair.second->setEntity(_entity); }); std::for_each(_spinButtons.begin(), _spinButtons.end(), [&] (SpinButtonMap::value_type& pair) { pair.second->setEntity(_entity); }); // Some dependencies _checkboxes["lay_down_left"]->Enable(_checkboxes["sleeping"]->GetValue()); _spinButtons["sit_down_angle"]->Enable(_checkboxes["sitting"]->GetValue()); _spinButtons["drunk_acuity_factor"]->Enable(_checkboxes["drunk"]->GetValue()); std::for_each(_labels.begin(), _labels.end(), [&] (LabelMap::value_type& pair) { pair.second->SetLabelText(_entity != nullptr ? _entity->getKeyValue(pair.first) : ""); }); } void AIEditingPanel::rescanSelection() { _queueUpdate = false; // Load the new entity from the selection _entity = getEntityFromSelection(); if (_entity != nullptr) { _entity->attachObserver(this); } updatePanelSensitivity(); updateWidgetsFromSelection(); } void AIEditingPanel::onSelectionChanged(const ISelectable& selectable) { // Immediately disconnect from the current entity in any case if (_entity != nullptr) { _entity->detachObserver(this); _entity = nullptr; // issue #4282 } if (GlobalGroupDialog().getPage() == _mainPanel) { rescanSelection(); } else { // We don't scan the selection if the page is not visible _queueUpdate = true; } } void AIEditingPanel::OnPaint(wxPaintEvent& ev) { if (_queueUpdate) { // Wake up from sleep mode and inspect the current selection rescanSelection(); } ev.Skip(); } void AIEditingPanel::onBrowseButton(wxCommandEvent& ev, const std::string& key) { if (_entity == nullptr) return; // Look up the property editor dialog IPropertyEditorPtr editor = GlobalEntityInspector().getRegisteredPropertyEditor(key); IPropertyEditorDialogPtr dialog = std::dynamic_pointer_cast(editor); if (dialog) { std::string oldValue = _entity->getKeyValue(key); std::string newValue = dialog->runDialog(_entity, key); if (newValue != oldValue) { UndoableCommand cmd("editAIProperty"); _entity->setKeyValue(key, newValue); _mainPanel->Layout(); } } else { rError() << "Could not find a property editor implementing the IPropertyEditorDialog interface for key " << key << std::endl; } } AIEditingPanelPtr& AIEditingPanel::InstancePtr() { static AIEditingPanelPtr _instancePtr; return _instancePtr; } } // namespace DarkRadiant-2.5.0/plugins/dm.editing/AIEditingPanel.h000066400000000000000000000040521321750546400223430ustar00rootroot00000000000000#pragma once #include #include "ientity.h" #include #include #include #include class ISelectable; class Entity; class wxStaticText; class wxScrolledWindow; class wxSizer; namespace ui { class AIEditingPanel; typedef std::shared_ptr AIEditingPanelPtr; class SpawnargLinkedCheckbox; class SpawnargLinkedSpinButton; class AIEditingPanel : public Entity::Observer, public sigc::trackable, public wxEvtHandler { private: sigc::connection _selectionChangedSignal; wxWindow* _tempParent; wxScrolledWindow* _mainPanel; bool _queueUpdate; typedef std::map CheckboxMap; CheckboxMap _checkboxes; typedef std::map SpinButtonMap; SpinButtonMap _spinButtons; typedef std::map LabelMap; LabelMap _labels; Entity* _entity; sigc::connection _undoHandler; sigc::connection _redoHandler; public: AIEditingPanel(); static AIEditingPanel& Instance(); static void Shutdown(); static void onRadiantStartup(); void onKeyInsert(const std::string& key, EntityKeyValue& value); void onKeyChange(const std::string& key, const std::string& val); void onKeyErase(const std::string& key, EntityKeyValue& value); void postUndo(); void postRedo(); protected: void OnPaint(wxPaintEvent& ev); void onBrowseButton(wxCommandEvent& ev, const std::string& key); private: static AIEditingPanelPtr& InstancePtr(); void constructWidgets(); wxSizer* createSpinButtonHbox(SpawnargLinkedSpinButton* spinButton); wxStaticText* createSectionLabel(const std::string& text); void createChooserRow(wxSizer* table, const std::string& rowLabel, const std::string& buttonLabel, const std::string& buttonIcon, const std::string& key); void onRadiantShutdown(); void onSelectionChanged(const ISelectable& selectable); void rescanSelection(); Entity* getEntityFromSelection(); void updateWidgetsFromSelection(); void updatePanelSensitivity(); }; } // namespace DarkRadiant-2.5.0/plugins/dm.editing/AIHeadChooserDialog.cpp000066400000000000000000000121011321750546400236310ustar00rootroot00000000000000#include "AIHeadChooserDialog.h" #include "i18n.h" #include "imainframe.h" #include "iuimanager.h" #include "ieclass.h" #include "eclass.h" #include #include namespace ui { namespace { const char* const WINDOW_TITLE = N_("Choose AI Head"); } AIHeadChooserDialog::AIHeadChooserDialog() : DialogBase(_(WINDOW_TITLE)), _headStore(new wxutil::TreeModel(_columns, true)), _headsView(NULL), _description(NULL) { SetSizer(new wxBoxSizer(wxVERTICAL)); wxSplitterWindow* splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE); splitter->SetMinimumPaneSize(10); // disallow unsplitting GetSizer()->Add(splitter, 1, wxEXPAND | wxALL, 12); GetSizer()->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT | wxBOTTOM | wxRIGHT, 12); _headsView = wxutil::TreeView::CreateWithModel(splitter, _headStore, wxDV_NO_HEADER); _headsView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(AIHeadChooserDialog::onHeadSelectionChanged), NULL, this); // Head Name column _headsView->AppendTextColumn("", _columns.name.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); FitToScreen(0.7f, 0.6f); wxPanel* previewPanel = new wxPanel(splitter, wxID_ANY); // Set the default rotation to something better suitable for the head models _preview.reset(new wxutil::ModelPreview(previewPanel)); _preview->setDefaultCamDistanceFactor(9.0f); _description = new wxTextCtrl(previewPanel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_LEFT | wxTE_MULTILINE | wxTE_READONLY | wxTE_WORDWRAP); _description->SetMinClientSize(wxSize(-1, 60)); previewPanel->SetSizer(new wxBoxSizer(wxVERTICAL)); previewPanel->GetSizer()->Add(_description, 0, wxEXPAND | wxBOTTOM, 6); previewPanel->GetSizer()->Add(_preview->getWidget(), 1, wxEXPAND); splitter->SplitVertically(_headsView, previewPanel); // Set the default size of the window splitter->SetSashPosition(static_cast(GetSize().GetWidth() * 0.3f)); // Check if the liststore is populated findAvailableHeads(); // Load the found heads into the GtkListStore populateHeadStore(); } void AIHeadChooserDialog::setSelectedHead(const std::string& headDef) { _selectedHead = headDef; if (_selectedHead.empty()) { _headsView->UnselectAll(); return; } wxDataViewItem found = _headStore->FindString(headDef, _columns.name); // Lookup the model path in the treemodel if (found.IsOk()) { _headsView->Select(found); _headsView->EnsureVisible(found); handleSelectionChanged(); } } std::string AIHeadChooserDialog::getSelectedHead() { return _selectedHead; } void AIHeadChooserDialog::handleSelectionChanged() { // Prepare to check for a selection wxDataViewItem item = _headsView->GetSelection(); // Add button is enabled if there is a selection and it is not a folder. if (item.IsOk()) { // Make the OK button active FindWindowById(wxID_OK, this)->Enable(true); _description->Enable(true); // Set the panel text with the usage information wxutil::TreeModel::Row row(item, *_headStore); _selectedHead = row[_columns.name]; // Lookup the IEntityClass instance IEntityClassPtr ecls = GlobalEntityClassManager().findClass(_selectedHead); if (ecls) { _preview->setModel(ecls->getAttribute("model").getValue()); _preview->setSkin(ecls->getAttribute("skin").getValue()); // Update the usage panel _description->SetValue(eclass::getUsage(*ecls)); } } else { _selectedHead = ""; _preview->setModel(""); FindWindowById(wxID_OK, this)->Enable(false); _description->Enable(false); } } void AIHeadChooserDialog::onHeadSelectionChanged(wxDataViewEvent& ev) { handleSelectionChanged(); } void AIHeadChooserDialog::populateHeadStore() { // Clear the head list to be safe _headStore->Clear(); for (HeadList::const_iterator i = _availableHeads.begin(); i != _availableHeads.end(); ++i) { // Add the entity to the list wxutil::TreeModel::Row row = _headStore->AddItem(); row[_columns.name] = *i; row.SendItemAdded(); } } namespace { class HeadEClassFinder : public EntityClassVisitor { AIHeadChooserDialog::HeadList& _list; public: HeadEClassFinder(AIHeadChooserDialog::HeadList& list) : _list(list) {} void visit(const IEntityClassPtr& eclass) { if (eclass->getAttribute("editor_head").getValue() == "1") { _list.insert(eclass->getName()); } } }; } // namespace void AIHeadChooserDialog::findAvailableHeads() { if (!_availableHeads.empty()) { return; } // Instantiate a finder class and traverse all eclasses HeadEClassFinder visitor(_availableHeads); GlobalEntityClassManager().forEachEntityClass(visitor); } // Init static class member AIHeadChooserDialog::HeadList AIHeadChooserDialog::_availableHeads; } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/AIHeadChooserDialog.h000066400000000000000000000023161321750546400233050ustar00rootroot00000000000000#pragma once #include "wxutil/dialog/DialogBase.h" #include "wxutil/preview/ModelPreview.h" #include "wxutil/TreeView.h" #include #include namespace ui { class AIHeadChooserDialog : public wxutil::DialogBase { public: typedef std::set HeadList; private: struct ListStoreColumns : public wxutil::TreeModel::ColumnRecord { ListStoreColumns() : name(add(wxutil::TreeModel::Column::String)) {} wxutil::TreeModel::Column name; }; ListStoreColumns _columns; wxutil::TreeModel::Ptr _headStore; wxutil::TreeView* _headsView; wxTextCtrl* _description; // The model preview wxutil::ModelPreviewPtr _preview; // The name of the currently selected head std::string _selectedHead; static HeadList _availableHeads; public: AIHeadChooserDialog(); // Set the selection to a given entityDef void setSelectedHead(const std::string& headDef); // Get the currently selected head (is empty when nothing is selected) std::string getSelectedHead(); private: void populateHeadStore(); // Searches all entity classes for available heads static void findAvailableHeads(); void handleSelectionChanged(); void onHeadSelectionChanged(wxDataViewEvent& ev); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/AIHeadPropertyEditor.cpp000066400000000000000000000044671321750546400241220ustar00rootroot00000000000000#include "AIHeadPropertyEditor.h" #include "i18n.h" #include "ieclass.h" #include "iuimanager.h" #include "ientity.h" #include #include #include #include #include "AIHeadChooserDialog.h" namespace ui { AIHeadPropertyEditor::AIHeadPropertyEditor() : _widget(NULL), _entity(NULL) {} AIHeadPropertyEditor::AIHeadPropertyEditor(wxWindow* parent, Entity* entity, const std::string& key, const std::string& options) : _entity(entity) { // Construct the main widget (will be managed by the base class) _widget = new wxPanel(parent, wxID_ANY); _widget->SetSizer(new wxBoxSizer(wxHORIZONTAL)); // Create the browse button wxButton* browseButton = new wxButton(_widget, wxID_ANY, _("Choose AI head...")); browseButton->SetBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "icon_model.png")); browseButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(AIHeadPropertyEditor::onChooseButton), NULL, this); _widget->GetSizer()->Add(browseButton, 0, wxALIGN_CENTER_VERTICAL); } AIHeadPropertyEditor::~AIHeadPropertyEditor() { if (_widget != NULL) { _widget->Destroy(); } } wxPanel* AIHeadPropertyEditor::getWidget() { return _widget; } void AIHeadPropertyEditor::updateFromEntity() { // nothing to do } IPropertyEditorPtr AIHeadPropertyEditor::createNew(wxWindow* parent, Entity* entity, const std::string& key, const std::string& options) { return IPropertyEditorPtr(new AIHeadPropertyEditor(parent, entity, key, options)); } void AIHeadPropertyEditor::onChooseButton(wxCommandEvent& ev) { // Construct a new head chooser dialog AIHeadChooserDialog* dialog = new AIHeadChooserDialog; dialog->setSelectedHead(_entity->getKeyValue(DEF_HEAD_KEY)); // Show and block if (dialog->ShowModal() == wxID_OK) { _entity->setKeyValue(DEF_HEAD_KEY, dialog->getSelectedHead()); } dialog->Destroy(); } std::string AIHeadPropertyEditor::runDialog(Entity* entity, const std::string& key) { // Construct a new head chooser dialog AIHeadChooserDialog* dialog = new AIHeadChooserDialog; std::string prevHead = entity->getKeyValue(key); dialog->setSelectedHead(prevHead); // Show and block std::string selected = prevHead; if (dialog->ShowModal() == wxID_OK) { selected = dialog->getSelectedHead(); } dialog->Destroy(); return selected; } } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/AIHeadPropertyEditor.h000066400000000000000000000015641321750546400235620ustar00rootroot00000000000000#pragma once #include "ientityinspector.h" #include namespace ui { namespace { const std::string DEF_HEAD_KEY = "def_head"; } class AIHeadPropertyEditor : public IPropertyEditor, public IPropertyEditorDialog, public wxEvtHandler { private: // The top-level widget wxPanel* _widget; Entity* _entity; public: // Default constructor AIHeadPropertyEditor(); ~AIHeadPropertyEditor(); wxPanel* getWidget() override; void updateFromEntity() override; AIHeadPropertyEditor(wxWindow* parent, Entity* entity, const std::string& key, const std::string& options); IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity, const std::string& key, const std::string& options) override; std::string runDialog(Entity* entity, const std::string& key) override; private: void onChooseButton(wxCommandEvent& ev); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/AIVocalSetChooserDialog.cpp000066400000000000000000000117531321750546400245240ustar00rootroot00000000000000#include "AIVocalSetChooserDialog.h" #include "i18n.h" #include "imainframe.h" #include "iuimanager.h" #include "ieclass.h" #include "isound.h" #include "eclass.h" #include #include namespace ui { namespace { const char* const WINDOW_TITLE = N_("Choose AI Vocal Set"); } AIVocalSetChooserDialog::AIVocalSetChooserDialog() : DialogBase(_(WINDOW_TITLE)), _setStore(new wxutil::TreeModel(_columns, true)), _preview(NULL) { SetSizer(new wxBoxSizer(wxVERTICAL)); if (module::GlobalModuleRegistry().moduleExists(MODULE_SOUNDMANAGER)) { _preview = new AIVocalSetPreview(this); } _setView = wxutil::TreeView::CreateWithModel(this, _setStore, wxDV_NO_HEADER); _setView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(AIVocalSetChooserDialog::onSetSelectionChanged), NULL, this); // Head Name column _setView->AppendTextColumn("", _columns.name.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); wxBoxSizer* vbox1 = new wxBoxSizer(wxVERTICAL); wxStaticText* label1 = new wxStaticText(this, wxID_ANY, _("Available Sets")); label1->SetFont(label1->GetFont().Bold()); vbox1->Add(label1, 0, wxBOTTOM, 6); vbox1->Add(_setView, 1, wxEXPAND); // Right: the description wxBoxSizer* vbox2 = new wxBoxSizer(wxVERTICAL); wxStaticText* label2 = new wxStaticText(this, wxID_ANY, _("Description")); label2->SetFont(label2->GetFont().Bold()); _description = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_LEFT | wxTE_MULTILINE | wxTE_READONLY | wxTE_WORDWRAP); _description->SetMinClientSize(wxSize(-1, 60)); vbox2->Add(label2, 0, wxBOTTOM, 6); vbox2->Add(_description, 1, wxEXPAND | wxBOTTOM, 6); // Right: the preview control panel if (_preview != NULL) { vbox2->Add(_preview, 0, wxEXPAND); } // dialog hbox, left is the treeview, right is the preview panel wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL); hbox->Add(vbox1, 3, wxEXPAND | wxRIGHT, 6); hbox->Add(vbox2, 1, wxEXPAND | wxRIGHT, 6); GetSizer()->Add(hbox, 1, wxEXPAND | wxALL, 12); GetSizer()->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT | wxBOTTOM | wxRIGHT, 12); FitToScreen(0.7f, 0.6f); // Check if the liststore is populated findAvailableSets(); // Load the found sets into the GtkListStore populateSetStore(); } void AIVocalSetChooserDialog::setSelectedVocalSet(const std::string& setName) { _selectedSet = setName; if (_selectedSet.empty()) { _setView->UnselectAll(); return; } wxDataViewItem found = _setStore->FindString(setName, _columns.name); // Lookup the model path in the treemodel if (found.IsOk()) { _setView->Select(found); _setView->EnsureVisible(found); handleSetSelectionChanged(); } } std::string AIVocalSetChooserDialog::getSelectedVocalSet() { return _selectedSet; } void AIVocalSetChooserDialog::handleSetSelectionChanged() { // Prepare to check for a selection wxDataViewItem item = _setView->GetSelection(); // Add button is enabled if there is a selection if (item.IsOk()) { // Make the OK button active FindWindowById(wxID_OK, this)->Enable(true); _description->Enable(true); // Set the panel text with the usage information wxutil::TreeModel::Row row(item, *_setStore); _selectedSet = row[_columns.name]; // Lookup the IEntityClass instance IEntityClassPtr ecls = GlobalEntityClassManager().findClass(_selectedSet); if (ecls) { // Update the preview pane if (_preview != NULL) { _preview->setVocalSetEclass(ecls); } // Update the usage panel _description->SetValue(eclass::getUsage(*ecls)); } } else { _selectedSet = ""; if (_preview != NULL) { _preview->setVocalSetEclass(IEntityClassPtr()); } FindWindowById(wxID_OK, this)->Enable(false); _description->Enable(false); } } void AIVocalSetChooserDialog::onSetSelectionChanged(wxDataViewEvent& ev) { handleSetSelectionChanged(); } void AIVocalSetChooserDialog::populateSetStore() { // Clear the head list to be safe _setStore->Clear(); for (SetList::const_iterator i = _availableSets.begin(); i != _availableSets.end(); ++i) { // Add the entity to the list wxutil::TreeModel::Row row = _setStore->AddItem(); row[_columns.name] = *i; row.SendItemAdded(); } } namespace { class VocalSetEClassFinder : public EntityClassVisitor { AIVocalSetChooserDialog::SetList& _list; public: VocalSetEClassFinder(AIVocalSetChooserDialog::SetList& list) : _list(list) {} void visit(const IEntityClassPtr& eclass) { if (eclass->getAttribute("editor_vocal_set").getValue() == "1") { _list.insert(eclass->getName()); } } }; } // namespace void AIVocalSetChooserDialog::findAvailableSets() { if (!_availableSets.empty()) { return; } // Instantiate a finder class and traverse all eclasses VocalSetEClassFinder visitor(_availableSets); GlobalEntityClassManager().forEachEntityClass(visitor); } // Init static class member AIVocalSetChooserDialog::SetList AIVocalSetChooserDialog::_availableSets; } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/AIVocalSetChooserDialog.h000066400000000000000000000023001321750546400241550ustar00rootroot00000000000000#pragma once #include "wxutil/dialog/DialogBase.h" #include #include #include "wxutil/TreeView.h" #include "AIVocalSetPreview.h" class wxTextCtrl; namespace ui { class AIVocalSetChooserDialog : public wxutil::DialogBase { public: typedef std::set SetList; private: struct ListStoreColumns : public wxutil::TreeModel::ColumnRecord { ListStoreColumns() : name(add(wxutil::TreeModel::Column::String)) {} wxutil::TreeModel::Column name; }; ListStoreColumns _columns; wxutil::TreeModel::Ptr _setStore; wxutil::TreeView* _setView; wxTextCtrl* _description; // The name of the currently selected set std::string _selectedSet; static SetList _availableSets; AIVocalSetPreview* _preview; public: AIVocalSetChooserDialog(); // Set the selection to a given entityDef void setSelectedVocalSet(const std::string& setName); // Get the currently selected set (is empty when nothing is selected) std::string getSelectedVocalSet(); private: void populateSetStore(); // Searches all entity classes for available sets static void findAvailableSets(); void handleSetSelectionChanged(); void onSetSelectionChanged(wxDataViewEvent& ev); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/AIVocalSetPreview.cpp000066400000000000000000000056731321750546400234270ustar00rootroot00000000000000#include "AIVocalSetPreview.h" #include "i18n.h" #include "isound.h" #include "iuimanager.h" #include "eclass.h" #include #include #include #include #include #include #include namespace ui { AIVocalSetPreview::AIVocalSetPreview(wxWindow* parent) : wxPanel(parent, wxID_ANY) { createControlPanel(); // Trigger the initial update of the widgets update(); srand(static_cast(time(NULL))); } void AIVocalSetPreview::createControlPanel() { SetMinClientSize(wxSize(200, -1)); SetSizer(new wxBoxSizer(wxVERTICAL)); // Create the playback button _playButton = new wxButton(this, wxID_ANY); _playButton->SetBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "media-playback-start-ltr.png")); _stopButton = new wxButton(this, wxID_ANY); _stopButton->SetBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "media-playback-stop.png")); _playButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(AIVocalSetPreview::onPlay), NULL, this); _stopButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(AIVocalSetPreview::onStop), NULL, this); wxBoxSizer* btnHBox = new wxBoxSizer(wxHORIZONTAL); btnHBox->Add(_playButton, 1, wxRIGHT, 6); btnHBox->Add(_stopButton, 1); _statusLabel = new wxStaticText(this, wxID_ANY, ""); GetSizer()->Add(_statusLabel); GetSizer()->Add(btnHBox); } void AIVocalSetPreview::setVocalSetEclass(const IEntityClassPtr& vocalSetDef) { _vocalSetDef = vocalSetDef; update(); } void AIVocalSetPreview::update() { _setShaders.clear(); if (_vocalSetDef != NULL) { eclass::AttributeList sndAttrs = eclass::getSpawnargsWithPrefix( *_vocalSetDef, "snd_" ); for (eclass::AttributeList::const_iterator i = sndAttrs.begin(); i != sndAttrs.end(); ++i) { _setShaders.push_back(i->getValue()); } } // If the soundshader string is empty, desensitise the widgets Enable(_vocalSetDef != NULL && !_setShaders.empty()); } std::string AIVocalSetPreview::getRandomSoundFile() { // get a random sound shader std::size_t idx = static_cast(rand()) % _setShaders.size(); ISoundShaderPtr soundShader = GlobalSoundManager().getSoundShader(_setShaders[idx]); if (soundShader == NULL) return ""; SoundFileList files = soundShader->getSoundFileList(); if (files.empty()) return ""; std::size_t fileIdx = static_cast(rand()) % files.size(); return files[fileIdx]; } void AIVocalSetPreview::onPlay(wxCommandEvent& ev) { _statusLabel->SetLabelMarkup(""); std::string file = getRandomSoundFile(); if (!file.empty()) { // Pass the call to the sound manager if (!GlobalSoundManager().playSound(file)) { _statusLabel->SetLabelMarkup(_("Error: File not found.")); } } } void AIVocalSetPreview::onStop(wxCommandEvent& ev) { // Pass the call to the sound manager GlobalSoundManager().stopSound(); _statusLabel->SetLabelMarkup(""); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/AIVocalSetPreview.h000066400000000000000000000024431321750546400230640ustar00rootroot00000000000000#pragma once #include "ieclass.h" #include class wxStaticText; class wxButton; namespace ui { /** * greebo: This class provides the UI elements to listen * to a a given AI vocal set. On clicking the playback button * a random sound is chosen from the vocal set. * * Use the getWidget() method to pack this into a * parent container. */ class AIVocalSetPreview : public wxPanel { private: wxButton* _playButton; wxButton* _stopButton; wxStaticText* _statusLabel; // The currently "previewed" vocal set IEntityClassPtr _vocalSetDef; typedef std::vector SoundShaderList; SoundShaderList _setShaders; public: AIVocalSetPreview(wxWindow* parent); /** * greebo: Sets the vocal set to preview. Set NULL to disable this panel. */ void setVocalSetEclass(const IEntityClassPtr& vocalSetDef); private: /** * greebo: Returns a random file from the selected vocal set. * @returns: the filename as defined in one of the shaders or "". */ std::string getRandomSoundFile(); /** greebo: Creates the control widgets (play button) and such. */ void createControlPanel(); /** greebo: Updates the list according to the active soundshader */ void update(); // Callbacks void onPlay(wxCommandEvent& ev); void onStop(wxCommandEvent& ev); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/AIVocalSetPropertyEditor.cpp000066400000000000000000000046441321750546400247760ustar00rootroot00000000000000#include "AIVocalSetPropertyEditor.h" #include "i18n.h" #include "ieclass.h" #include "iuimanager.h" #include "ientity.h" #include #include #include #include #include "AIVocalSetChooserDialog.h" namespace ui { AIVocalSetPropertyEditor::AIVocalSetPropertyEditor() : _widget(NULL), _entity(NULL) {} AIVocalSetPropertyEditor::AIVocalSetPropertyEditor(wxWindow* parent, Entity* entity, const std::string& key, const std::string& options) : _entity(entity) { // Construct the main widget (will be managed by the base class) _widget = new wxPanel(parent, wxID_ANY); _widget->SetSizer(new wxBoxSizer(wxHORIZONTAL)); // Create the browse button wxButton* browseButton = new wxButton(_widget, wxID_ANY, _("Select Vocal Set...")); browseButton->SetBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "icon_sound.png")); browseButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(AIVocalSetPropertyEditor::onChooseButton), NULL, this); _widget->GetSizer()->Add(browseButton, 0, wxALIGN_CENTER_VERTICAL); } AIVocalSetPropertyEditor::~AIVocalSetPropertyEditor() { if (_widget != NULL) { _widget->Destroy(); } } wxPanel* AIVocalSetPropertyEditor::getWidget() { return _widget; } void AIVocalSetPropertyEditor::updateFromEntity() { // Nothing to do } IPropertyEditorPtr AIVocalSetPropertyEditor::createNew(wxWindow* parent, Entity* entity, const std::string& key, const std::string& options) { return IPropertyEditorPtr(new AIVocalSetPropertyEditor(parent, entity, key, options)); } void AIVocalSetPropertyEditor::onChooseButton(wxCommandEvent& ev) { // Construct a new vocal set chooser dialog AIVocalSetChooserDialog* dialog = new AIVocalSetChooserDialog; dialog->setSelectedVocalSet(_entity->getKeyValue(DEF_VOCAL_SET_KEY)); // Show and block if (dialog->ShowModal() == wxID_OK) { _entity->setKeyValue(DEF_VOCAL_SET_KEY, dialog->getSelectedVocalSet()); } dialog->Destroy(); } std::string AIVocalSetPropertyEditor::runDialog(Entity* entity, const std::string& key) { // Construct a new vocal set chooser dialog AIVocalSetChooserDialog* dialog = new AIVocalSetChooserDialog; std::string oldValue = entity->getKeyValue(DEF_VOCAL_SET_KEY); dialog->setSelectedVocalSet(oldValue); // Show and block std::string rv = oldValue; if (dialog->ShowModal() == wxID_OK) { rv = dialog->getSelectedVocalSet(); } dialog->Destroy(); return rv; } } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/AIVocalSetPropertyEditor.h000066400000000000000000000016141321750546400244350ustar00rootroot00000000000000#pragma once #include #include "ientityinspector.h" namespace ui { namespace { const std::string DEF_VOCAL_SET_KEY = "def_vocal_set"; } class AIVocalSetPropertyEditor : public IPropertyEditor, public IPropertyEditorDialog, public wxEvtHandler { private: // The top-level widget wxPanel* _widget; Entity* _entity; public: // Default constructor AIVocalSetPropertyEditor(); ~AIVocalSetPropertyEditor(); AIVocalSetPropertyEditor(wxWindow* parent, Entity* entity, const std::string& key, const std::string& options); wxPanel* getWidget() override; void updateFromEntity() override; IPropertyEditorPtr createNew(wxWindow* parent, Entity* entity, const std::string& key, const std::string& options) override; std::string runDialog(Entity* entity, const std::string& key) override; private: void onChooseButton(wxCommandEvent& ev); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/DarkmodTxt.cpp000066400000000000000000000152451321750546400222100ustar00rootroot00000000000000#include "DarkmodTxt.h" #include "i18n.h" #include "iarchive.h" #include "itextstream.h" #include "ifilesystem.h" #include #include "string/trim.h" #include "string/convert.h" #include "string/case_conv.h" namespace map { std::string DarkmodTxt::getFilename() { return NAME(); } const std::string& DarkmodTxt::getTitle() { return _title; } void DarkmodTxt::setTitle(const std::string& title) { _title = title; } const std::string& DarkmodTxt::getAuthor() { return _author; } void DarkmodTxt::setAuthor(const std::string& author) { _author = author; } const std::string& DarkmodTxt::getDescription() { return _description; } void DarkmodTxt::setDescription(const std::string& desc) { _description = desc; } const std::string& DarkmodTxt::getVersion() { return _version; } void DarkmodTxt::setVersion(const std::string& version) { _version = version; } const std::string& DarkmodTxt::getReqTdmVersion() { return _reqTdmVersion; } void DarkmodTxt::setReqTdmVersion(const std::string& reqVersion) { _reqTdmVersion = reqVersion; } const DarkmodTxt::TitleList& DarkmodTxt::getMissionTitles() { return _missionTitles; } void DarkmodTxt::setMissionTitles(const DarkmodTxt::TitleList& list) { _missionTitles = list; } void DarkmodTxt::ParseMissionTitles(std::vector& titleList, const std::string& source) { std::size_t titleNum = 1; std::size_t endIndex = 0; while (true) { std::string start = fmt::format("Mission {0:d} Title:", titleNum); std::string end = fmt::format("Mission {0:d} Title:", titleNum + 1); std::size_t startIndex = source.find(start, endIndex); if (startIndex == std::string::npos) break; endIndex = source.find(end, startIndex); // Extract next title string std::string title = source.substr(startIndex, (endIndex != std::string::npos) ? endIndex - startIndex : source.size() - startIndex); string::trim_left(title, start); string::trim(title); titleList.push_back(title); ++titleNum; } } DarkmodTxtPtr DarkmodTxt::CreateFromString(const std::string& contents) { DarkmodTxtPtr info(new DarkmodTxt); try { // Determine the positions in the file std::size_t titlePos = contents.find("Title:"); std::size_t missionTitlesPos = contents.find("Mission 1 Title:"); std::size_t descPos = contents.find("Description:"); std::size_t authorPos = contents.find("Author:"); std::size_t versionPos = contents.find("\nVersion:"); std::size_t reqVersionPos = contents.find("Required TDM Version:"); // Validate the order of the markers in the file bool positionsValid = titlePos != std::string::npos && titlePos < descPos && // Title is required & before description (or EOF) (missionTitlesPos == std::string::npos || missionTitlesPos < descPos) && // Optional Mission Titles & before description (or EOF) (descPos == std::string::npos || descPos < authorPos) && // Optional description & before author (or EOF) (authorPos == std::string::npos || authorPos < versionPos) && // Author optional & before description (or EOF) (versionPos == std::string::npos || versionPos < reqVersionPos); // Version optional & before req version (or EOF) if (!positionsValid) { throw ParseException(_("Order of the elements Title/Description/Author/etc. is incorrect")); } std::size_t len = contents.size(); if (titlePos != std::string::npos) { std::size_t endPos = (missionTitlesPos != std::string::npos) ? missionTitlesPos : descPos; info->_title = contents.substr(titlePos, (endPos != std::string::npos) ? endPos - titlePos : len - titlePos); string::trim_left(info->_title, "Title:"); string::trim(info->_title); } info->_missionTitles.clear(); info->_missionTitles.push_back(info->_title); // [0] is title by default if (missionTitlesPos != std::string::npos) { std::string missionTitles = contents.substr(missionTitlesPos, (descPos != std::string::npos) ? descPos - missionTitlesPos: len - missionTitlesPos); ParseMissionTitles(info->_missionTitles, missionTitles); } if (descPos != std::string::npos) { info->_description = contents.substr(descPos, (authorPos != std::string::npos) ? authorPos - descPos : len - descPos); string::trim_left(info->_description, "Description:"); string::trim(info->_description); } if (authorPos != std::string::npos) { std::size_t endPos = (versionPos != std::string::npos) ? versionPos : reqVersionPos; info->_author = contents.substr(authorPos, (endPos != std::string::npos) ? endPos - authorPos : len - authorPos); string::trim_left(info->_author, "Author:"); string::trim(info->_author); } if (versionPos != std::string::npos) { info->_version = contents.substr(versionPos, (reqVersionPos != std::string::npos) ? reqVersionPos - versionPos : len - versionPos); string::trim_left(info->_version, "\nVersion:"); string::trim(info->_version); } if (reqVersionPos != std::string::npos) { info->_reqTdmVersion = contents.substr(reqVersionPos, len - reqVersionPos); string::trim_left(info->_reqTdmVersion, "Required TDM Version:"); string::trim_left(info->_reqTdmVersion, "v"); string::trim(info->_reqTdmVersion); } } catch (const std::exception& ex) { // Convert ordinary exceptions in ParseExceptions rError() << "Exception parsing darkmod.txt: " << ex.what() << std::endl; throw ParseException(ex.what()); } return info; } DarkmodTxtPtr DarkmodTxt::CreateFromStream(std::istream& stream) { // Read all the stream contents into a string std::string str(std::istreambuf_iterator(stream), {}); return CreateFromString(str); } DarkmodTxtPtr DarkmodTxt::LoadForCurrentMod() { std::string darkmodTxtPath = GetOutputPathForCurrentMod() + NAME(); rMessage() << "Trying to open file " << darkmodTxtPath << std::endl; ArchiveTextFilePtr file = GlobalFileSystem().openTextFileInAbsolutePath(darkmodTxtPath); if (file) { std::istream stream(&(file->getInputStream())); return CreateFromStream(stream); } return std::make_shared(); } std::string DarkmodTxt::toString() { std::string output; if (!_title.empty()) { output += fmt::format("Title: {0}", _title); } if (_missionTitles.size() > 1) { // Skip the first string, which is the same as the title for (std::size_t i = 1; i < _missionTitles.size(); ++i) { output += fmt::format("\nMission {1:d} Title: {0}", _missionTitles[i], i); } } if (!_description.empty()) { output += fmt::format("\nDescription: {0}", _description); } if (!_author.empty()) { output += fmt::format("\nAuthor: {0}", _author); } if (!_version.empty()) { output += fmt::format("\nVersion: {0}", _version); } if (!_reqTdmVersion.empty()) { output += fmt::format("\nRequired TDM Version: {0}", _reqTdmVersion); } return output; } } DarkRadiant-2.5.0/plugins/dm.editing/DarkmodTxt.h000066400000000000000000000040111321750546400216420ustar00rootroot00000000000000#pragma once #include #include "MissionInfoTextFile.h" namespace map { class DarkmodTxt; typedef std::shared_ptr DarkmodTxtPtr; /** * An object representing the darkmod.txt file as found in the * mission folder's root directory. It contains information shown * in the "New Mission" section in TDM's main menu. */ class DarkmodTxt : public MissionInfoTextFile { public: typedef std::vector TitleList; private: std::string _title; std::string _author; std::string _description; std::string _version; std::string _reqTdmVersion; TitleList _missionTitles; public: static const char* NAME() { return "darkmod.txt"; } std::string getFilename() override; const std::string& getTitle(); void setTitle(const std::string& title); const std::string& getAuthor(); void setAuthor(const std::string& author); const std::string& getDescription(); void setDescription(const std::string& desc); const std::string& getVersion(); void setVersion(const std::string& version); const std::string& getReqTdmVersion(); void setReqTdmVersion(const std::string& reqVersion); // Returns the mission titles (the first element is the same as getTitle()) const TitleList& getMissionTitles(); void setMissionTitles(const TitleList& list); // Named constructor parsing the given string into a DarkmodTxt instance // A parse exception will be thrown if the file is not compliant static DarkmodTxtPtr CreateFromString(const std::string& contents); // Named constructor parsing the given stream into a DarkmodTxt instance // A parse exception will be thrown if the file is not compliant static DarkmodTxtPtr CreateFromStream(std::istream& stream); // A parse exception will be thrown if the file is not compliant static DarkmodTxtPtr LoadForCurrentMod(); // Retrieves the text representation of this instance, as it will be written to the darkmod.txt file std::string toString() override; private: static void ParseMissionTitles(std::vector& titleList, const std::string& source); }; } DarkRadiant-2.5.0/plugins/dm.editing/DeprecatedEclassCollector.h000066400000000000000000000012131321750546400246240ustar00rootroot00000000000000#pragma once #include "ieclass.h" namespace { const std::string MATERIAL_PREFIX("MATERIAL: "); const std::string ENTITYDEF_PREFIX("ENTITYDEF: "); } class DeprecatedEclassCollector : public EntityClassVisitor { private: std::string _fixupCode; public: void visit(const IEntityClassPtr& eclass) { const EntityClassAttribute& attr = eclass->getAttribute("editor_replacement"); if (attr.getValue().empty()) { return; } // Non-empty editor_replacement, add fixup code _fixupCode += ENTITYDEF_PREFIX + eclass->getName() + " => " + attr.getValue() + "\n"; } const std::string& getFixupCode() const { return _fixupCode; } }; DarkRadiant-2.5.0/plugins/dm.editing/FixupMap.cpp000066400000000000000000000104361321750546400216550ustar00rootroot00000000000000#include "FixupMap.h" #include #include #include #include "i18n.h" #include "iscenegraph.h" #include "iundo.h" #include "imainframe.h" #include "ieclass.h" #include "wxutil/dialog/MessageBox.h" #include "os/file.h" #include "string/string.h" #include "parser/Tokeniser.h" #include "ShaderReplacer.h" #include "SpawnargReplacer.h" #include "DeprecatedEclassCollector.h" #include #include "string/predicate.h" FixupMap::FixupMap(const std::string& filename) : _filename(filename), _progress(_("Fixup in progress")) {} FixupMap::Result FixupMap::perform() { UndoableCommand cmd("performFixup"); // Load contents loadFixupFile(); // Load deprecated entities loadDeprecatedEntities(); // Instantiate a line tokeniser parser::BasicStringTokeniser tokeniser(_contents, "\n\r"); _curLineNumber = 0; std::size_t parsedSize = 0; while (tokeniser.hasMoreTokens()) { _curLineNumber++; std::string line = tokeniser.nextToken(); performFixup(line); // Approximate the progress parsedSize += line.size(); try { double fraction = static_cast(parsedSize) / _contents.size(); _progress.setTextAndFraction( fmt::format(_("Processing line {0}..."), _curLineNumber), fraction); } catch (wxutil::ModalProgressDialog::OperationAbortedException& ex) { wxutil::Messagebox box(_("Fixup cancelled"), ex.what(), ui::IDialog::MESSAGE_ERROR); box.run(); return _result; } } _progress.setTextAndFraction(_("Completed"), 1.0); return _result; } void FixupMap::performFixup(const std::string& line) { // Skip empty lines and comments if (line.empty() || string::starts_with(line, "#") || string::starts_with(line, "//")) { return; } std::regex expr("^" + MATERIAL_PREFIX + "(.*)\\s=>\\s(.*)$"); std::smatch matches; if (std::regex_match(line, matches, expr)) { // Fixup a specific shader std::string oldShader = matches[1]; std::string newShader = matches[2]; replaceShader(oldShader, newShader); return; } expr = std::regex("^" + ENTITYDEF_PREFIX + "(.*)\\s=>\\s(.*)$"); if (std::regex_match(line, matches, expr)) { // Fixup a specific entitydef std::string oldDef = matches[1]; std::string newDef = matches[2]; // Search all spawnargs replaceSpawnarg(oldDef, newDef); return; } // No specific prefix, this can be everything: spawnarg or texture expr = std::regex("^(.*)\\s=>\\s(.*)$"); if (std::regex_match(line, matches, expr)) { std::string oldStr = matches[1]; std::string newStr = matches[2]; // First, try to use these strings as shader replacement replaceShader(oldStr, newStr); // Second, traverse all entities and fix them up replaceSpawnarg(oldStr, newStr); return; } } void FixupMap::replaceSpawnarg(const std::string& oldVal, const std::string& newVal) { SpawnargReplacer replacer(oldVal, newVal); GlobalSceneGraph().root()->traverseChildren(replacer); replacer.processEntities(); _result.replacedModels += replacer.getModelCount(); _result.replacedEntities += replacer.getEclassCount(); _result.replacedMisc += replacer.getOtherCount(); } void FixupMap::replaceShader(const std::string& oldShader, const std::string& newShader) { // Instantiate a walker ShaderReplacer replacer(oldShader, newShader); GlobalSceneGraph().root()->traverseChildren(replacer); _result.replacedShaders += replacer.getReplaceCount(); } void FixupMap::loadFixupFile() { // Sanity-check the file if (!os::fileOrDirExists(_filename) || !os::fileIsReadable(_filename)) { wxutil::Messagebox::Show(_("File not readable"), _("The specified file doesn't exist."), ui::IDialog::MESSAGE_ERROR); return; } // Load the file's contents std::ifstream input; input.open(_filename.c_str(), std::ios::in|std::ios::ate); if (!input) { wxutil::Messagebox::Show(_("File not readable"), _("The specified file can't be opened."), ui::IDialog::MESSAGE_ERROR); return; } std::vector buffer; buffer.resize(input.tellg()); input.seekg(0, std::ios::beg); input.read(&buffer.front(), buffer.size()); input.close(); _contents = &buffer.front(); } void FixupMap::loadDeprecatedEntities() { // Traverse all eclasses and collect replacements DeprecatedEclassCollector collector; GlobalEntityClassManager().forEachEntityClass(collector); _contents += "\n"; _contents += collector.getFixupCode(); } DarkRadiant-2.5.0/plugins/dm.editing/FixupMap.h000066400000000000000000000020571321750546400213220ustar00rootroot00000000000000#pragma once #include #include #include "wxutil/ModalProgressDialog.h" class FixupMap { public: struct Result { std::size_t replacedEntities; std::size_t replacedShaders; std::size_t replacedModels; std::size_t replacedMisc; // Errors, sorted by line typedef std::map ErrorMap; ErrorMap errors; Result() : replacedEntities(0), replacedShaders(0), replacedModels(0), replacedMisc(0) {} }; private: // Path to fixup file std::string _filename; // Fixup contents std::string _contents; std::size_t _curLineNumber; Result _result; wxutil::ModalProgressDialog _progress; public: // Pass the fixup filename to the constructor FixupMap(const std::string& filename); // Run the fixup process Result perform(); private: void loadFixupFile(); void loadDeprecatedEntities(); void performFixup(const std::string& line); void replaceShader(const std::string& oldShader, const std::string& newShader); void replaceSpawnarg(const std::string& oldVal, const std::string& newVal); }; DarkRadiant-2.5.0/plugins/dm.editing/FixupMapDialog.cpp000066400000000000000000000030711321750546400227720ustar00rootroot00000000000000#include "FixupMapDialog.h" #include "i18n.h" #include "imainframe.h" #include "wxutil/dialog/MessageBox.h" #include "string/string.h" #include "FixupMap.h" namespace ui { namespace { const char* const WINDOW_TITLE = N_("Fixup Map"); const char* const FIXUP_FILE_LABEL = N_("Fixup File"); } FixupMapDialog::FixupMapDialog() : Dialog(_(WINDOW_TITLE)) { // Add the path entry _pathEntry = addPathEntry(FIXUP_FILE_LABEL, false); } std::string FixupMapDialog::getFixupFilePath() { return getElementValue(_pathEntry); } void FixupMapDialog::RunDialog(const cmd::ArgumentList& args) { FixupMapDialog dialog; if (dialog.run() == RESULT_OK) { std::string filename = dialog.getFixupFilePath(); // Run fixup script FixupMap fixup(filename); FixupMap::Result result = fixup.perform(); // Show popup with results std::string msg; msg += fmt::format(_("{0} shaders replaced."), result.replacedShaders) + "\n"; msg += fmt::format(_("{0} entities replaced."), result.replacedEntities) + "\n"; msg += fmt::format(_("{0} models replaced."), result.replacedModels) + "\n"; msg += fmt::format(_("{0} spawnargs replaced."), result.replacedMisc) + "\n"; if (!result.errors.empty()) { msg += "\n\n"; msg += _("Errors occurred:"); msg += "\n"; for (FixupMap::Result::ErrorMap::const_iterator i = result.errors.begin(); i != result.errors.end(); ++i) { msg += fmt::format(_("(Line {0}): {1}"), i->first, i->second); msg += "\n"; } } wxutil::Messagebox::Show(_("Fixup Results"), msg, IDialog::MESSAGE_CONFIRM); } } } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/FixupMapDialog.h000066400000000000000000000005001321750546400224310ustar00rootroot00000000000000#pragma once #include "icommandsystem.h" #include "wxutil/dialog/Dialog.h" namespace ui { class FixupMapDialog : public wxutil::Dialog { private: IDialog::Handle _pathEntry; public: FixupMapDialog(); std::string getFixupFilePath(); static void RunDialog(const cmd::ArgumentList& args); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.editing/Makefile.am000066400000000000000000000020221321750546400214440ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs \ $(XML_CFLAGS) pluginsdir = $(pkglibdir)/plugins plugins_LTLIBRARIES = dm_editing.la dm_editing_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \ $(top_builddir)/libs/xmlutil/libxmlutil.la dm_editing_la_LDFLAGS = -module -avoid-version \ $(WX_LIBS) \ $(XML_LIBS) \ $(FILESYSTEM_LIBS) dm_editing_la_SOURCES = plugin.cpp \ AIHeadPropertyEditor.cpp \ AIEditingPanel.cpp \ FixupMap.cpp \ DarkmodTxt.cpp \ ReadmeTxt.cpp \ MissionInfoTextFile.cpp \ MissionInfoEditDialog.cpp \ MissionReadmeDialog.cpp \ MissionInfoGuiView.cpp \ FixupMapDialog.cpp \ AIVocalSetChooserDialog.cpp \ AIVocalSetPropertyEditor.cpp \ AIVocalSetPreview.cpp \ AIHeadChooserDialog.cpp DarkRadiant-2.5.0/plugins/dm.editing/MissionInfoEditDialog.cpp000066400000000000000000000217621321750546400243130ustar00rootroot00000000000000#include "MissionInfoEditDialog.h" #include "igui.h" #include "itextstream.h" #include "i18n.h" #include #include #include #include #include #include "wxutil/TreeView.h" #include "wxutil/preview/GuiView.h" #include "wxutil/dialog/MessageBox.h" #include "wxutil/menu/IconTextMenuItem.h" #include "MissionInfoGuiView.h" #include "MissionReadmeDialog.h" namespace ui { namespace { const char* const WINDOW_TITLE = N_("Mission Info Editor (darkmod.txt)"); } MissionInfoEditDialog::MissionInfoEditDialog(wxWindow* parent) : DialogBase(_(WINDOW_TITLE), parent), _missionTitleStore(new wxutil::TreeModel(_missionTitleColumns, true)), _updateInProgress(false) { populateWindow(); try { _darkmodTxt = map::DarkmodTxt::LoadForCurrentMod(); } catch (map::DarkmodTxt::ParseException& ex) { rError() << "Failed to parse darkmod.txt: " << ex.what() << std::endl; wxutil::Messagebox::ShowError( fmt::format(_("Failed to parse darkmod.txt:\n{0}"), ex.what()), this); // Reset the file to defaults _darkmodTxt = std::make_shared(); } _guiView->setGui(GlobalGuiManager().getGui("guis/mainmenu.gui")); _guiView->setMissionInfoFile(_darkmodTxt); updateValuesFromDarkmodTxt(); } void MissionInfoEditDialog::updateValuesFromDarkmodTxt() { _missionTitleStore->Clear(); assert(_darkmodTxt); // this should be non-NULL at all times if (!_darkmodTxt) return; _updateInProgress = true; findNamedObject(this, "MissionInfoEditDialogTitleEntry")->SetValue(_darkmodTxt->getTitle()); findNamedObject(this, "MissionInfoEditDialogAuthorEntry")->SetValue(_darkmodTxt->getAuthor()); findNamedObject(this, "MissionInfoEditDialogDescriptionEntry")->SetValue(_darkmodTxt->getDescription()); findNamedObject(this, "MissionInfoEditDialogVersionEntry")->SetValue(_darkmodTxt->getVersion()); findNamedObject(this, "MissionInfoEditDialogReqTdmVersionEntry")->SetValue(_darkmodTxt->getReqTdmVersion()); findNamedObject(this, "MissionInfoEditDialogOutputPath")->SetLabelText(_darkmodTxt->getFullOutputPath()); const map::DarkmodTxt::TitleList& titles = _darkmodTxt->getMissionTitles(); // We skip the first entry, which is the campaign title for (std::size_t i = 1; i < titles.size(); ++i) { const std::string& title = titles[i]; wxutil::TreeModel::Row row = _missionTitleStore->AddItem(); row[_missionTitleColumns.number] = static_cast(i); row[_missionTitleColumns.title] = title; row.SendItemAdded(); } _guiView->updateGuiState(); _updateInProgress = false; } void MissionInfoEditDialog::populateWindow() { SetSizer(new wxBoxSizer(wxVERTICAL)); wxPanel* panel = loadNamedPanel(this, "MissionInfoEditDialogMainPanel"); GetSizer()->Add(panel, 1, wxEXPAND); // Replace the list control with our own TreeView wxWindow* existing = findNamedObject(this, "MissionInfoEditDialogMissionTitleList"); wxutil::TreeView* treeview = wxutil::TreeView::CreateWithModel(existing->GetParent(), _missionTitleStore, wxDV_SINGLE); treeview->SetName("MissionInfoEditDialogMissionTitleList"); treeview->SetMinSize(wxSize(-1, 150)); // Display name column with icon treeview->AppendTextColumn(_("#"), _missionTitleColumns.number.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT); treeview->AppendTextColumn(_("Title"), _missionTitleColumns.title.getColumnIndex(), wxDATAVIEW_CELL_EDITABLE, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT); treeview->Connect(wxEVT_DATAVIEW_ITEM_EDITING_DONE, wxDataViewEventHandler(MissionInfoEditDialog::onTitleEdited), nullptr, this); treeview->Connect(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, wxDataViewEventHandler(MissionInfoEditDialog::onTitleContextMenu), nullptr, this); existing->GetContainingSizer()->Replace(existing, treeview); existing->Destroy(); // Add the preview widget wxPanel* previewPanel = findNamedObject(this, "MissionInfoEditDialogPreviewPanel"); _guiView = new DarkmodTxtGuiView(previewPanel); previewPanel->GetSizer()->Add(_guiView, 1, wxEXPAND); makeLabelBold(this, "MissionInfoLabel"); wxButton* saveButton = findNamedObject(this, "MissionInfoEditDialogSaveButton"); wxButton* cancelButton = findNamedObject(this, "MissionInfoEditDialogCancelButton"); saveButton->Bind(wxEVT_BUTTON, sigc::mem_fun(this, &MissionInfoEditDialog::onSave)); cancelButton->Bind(wxEVT_BUTTON, sigc::mem_fun(this, &MissionInfoEditDialog::onCancel)); // Popup Menu _missionTitlesContextMenu.reset(new wxutil::PopupMenu); _missionTitlesContextMenu->addItem( new wxutil::StockIconTextMenuItem(_("Add Title"), wxART_PLUS), std::bind(&MissionInfoEditDialog::onAddTitle, this) ); _missionTitlesContextMenu->addItem( new wxutil::StockIconTextMenuItem(_("Delete Title"), wxART_MINUS), std::bind(&MissionInfoEditDialog::onDeleteTitle, this), std::bind(&MissionInfoEditDialog::testDeleteTitle, this) ); // Wire up the text entry boxes to update the preview setupNamedEntryBox("MissionInfoEditDialogTitleEntry"); setupNamedEntryBox("MissionInfoEditDialogAuthorEntry"); setupNamedEntryBox("MissionInfoEditDialogDescriptionEntry"); setupNamedEntryBox("MissionInfoEditDialogVersionEntry"); // Setup the event for the readme.txt button wxButton* editReadmeButton = findNamedObject(this, "MissionInfoEditDialogEditReadmeButton"); editReadmeButton->Bind(wxEVT_BUTTON, sigc::mem_fun(this, &MissionInfoEditDialog::onEditReadme)); Layout(); Fit(); CenterOnScreen(); } void MissionInfoEditDialog::setupNamedEntryBox(const std::string& ctrlName) { wxTextCtrl* ctrl = findNamedObject(this, ctrlName); assert(ctrl != nullptr); if (ctrl == nullptr) return; ctrl->Bind(wxEVT_TEXT, [this](wxCommandEvent& ev) { if (_updateInProgress) return; // Load the values from the UI to the DarkmodTxt instance _darkmodTxt->setTitle(findNamedObject(this, "MissionInfoEditDialogTitleEntry")->GetValue().ToStdString()); _darkmodTxt->setAuthor(findNamedObject(this, "MissionInfoEditDialogAuthorEntry")->GetValue().ToStdString()); _darkmodTxt->setDescription(findNamedObject(this, "MissionInfoEditDialogDescriptionEntry")->GetValue().ToStdString()); _darkmodTxt->setVersion(findNamedObject(this, "MissionInfoEditDialogVersionEntry")->GetValue().ToStdString()); _darkmodTxt->setReqTdmVersion(findNamedObject(this, "MissionInfoEditDialogReqTdmVersionEntry")->GetValue().ToStdString()); _guiView->updateGuiState(); }); } void MissionInfoEditDialog::onSave(wxCommandEvent& ev) { try { // DarkmodTxt is kept in sync all the time, no need to load anything, just save to disk _darkmodTxt->saveToCurrentMod(); // Close the dialog EndModal(wxID_OK); } catch (std::runtime_error& err) { wxutil::Messagebox::ShowError(err.what(), this); } } void MissionInfoEditDialog::onCancel(wxCommandEvent& ev) { // destroy dialog without saving EndModal(wxID_CANCEL); } void MissionInfoEditDialog::onEditReadme(wxCommandEvent& ev) { MissionReadmeDialog* dialog = new MissionReadmeDialog(this); dialog->ShowModal(); dialog->Destroy(); } void MissionInfoEditDialog::onTitleEdited(wxDataViewEvent& ev) { wxutil::TreeModel::Row row(ev.GetItem(), *_missionTitleStore); int titleNum = row[_missionTitleColumns.number].getInteger(); map::DarkmodTxt::TitleList list = _darkmodTxt->getMissionTitles(); assert(titleNum >= 0 && titleNum < static_cast(list.size())); if (ev.GetColumn() == _missionTitleColumns.title.getColumnIndex()) { list[titleNum] = static_cast(ev.GetValue()); _darkmodTxt->setMissionTitles(list); } } void MissionInfoEditDialog::onTitleContextMenu(wxDataViewEvent& ev) { _missionTitlesContextMenu->show(findNamedObject(this, "MissionInfoEditDialogMissionTitleList")); } void MissionInfoEditDialog::onAddTitle() { map::DarkmodTxt::TitleList list = _darkmodTxt->getMissionTitles(); list.push_back("Click to edit Title"); _darkmodTxt->setMissionTitles(list); updateValuesFromDarkmodTxt(); } void MissionInfoEditDialog::onDeleteTitle() { wxutil::TreeView* treeView = findNamedObject(this, "MissionInfoEditDialogMissionTitleList"); wxDataViewItem item = treeView->GetSelection(); if (!item.IsOk()) return; wxutil::TreeModel::Row row(item, *_missionTitleStore); int titleNum = row[_missionTitleColumns.number].getInteger(); map::DarkmodTxt::TitleList list = _darkmodTxt->getMissionTitles(); assert(titleNum >= 0 && titleNum < static_cast(list.size())); list.erase(list.begin() + titleNum); _darkmodTxt->setMissionTitles(list); updateValuesFromDarkmodTxt(); } bool MissionInfoEditDialog::testDeleteTitle() { wxutil::TreeView* treeView = findNamedObject(this, "MissionInfoEditDialogMissionTitleList"); wxDataViewItem item = treeView->GetSelection(); return item.IsOk(); } void MissionInfoEditDialog::ShowDialog(const cmd::ArgumentList& args) { MissionInfoEditDialog* instance = new MissionInfoEditDialog; instance->ShowModal(); instance->Destroy(); } } DarkRadiant-2.5.0/plugins/dm.editing/MissionInfoEditDialog.h000066400000000000000000000027611321750546400237560ustar00rootroot00000000000000#pragma once #include "icommandsystem.h" #include "wxutil/dialog/DialogBase.h" #include "wxutil/XmlResourceBasedWidget.h" #include "wxutil/TreeModel.h" #include "wxutil/menu/PopupMenu.h" #include "DarkmodTxt.h" #include "MissionInfoGuiView.h" namespace wxutil { class GuiView; } namespace ui { class MissionInfoEditDialog : public wxutil::DialogBase, private wxutil::XmlResourceBasedWidget { private: // The file we're editing map::DarkmodTxtPtr _darkmodTxt; // Treemodel definition struct MissionTitleColumns : public wxutil::TreeModel::ColumnRecord { MissionTitleColumns() : number(add(wxutil::TreeModel::Column::Integer)), title(add(wxutil::TreeModel::Column::String)) {} wxutil::TreeModel::Column number; wxutil::TreeModel::Column title; }; MissionTitleColumns _missionTitleColumns; wxutil::TreeModel::Ptr _missionTitleStore; // Context menu wxutil::PopupMenuPtr _missionTitlesContextMenu; DarkmodTxtGuiView* _guiView; bool _updateInProgress; public: // Constructor MissionInfoEditDialog(wxWindow* parent = nullptr); static void ShowDialog(const cmd::ArgumentList& args); private: void populateWindow(); void updateValuesFromDarkmodTxt(); void setupNamedEntryBox(const std::string& ctrlName); void onSave(wxCommandEvent& ev); void onCancel(wxCommandEvent& ev); void onEditReadme(wxCommandEvent& ev); void onTitleEdited(wxDataViewEvent& ev); void onTitleContextMenu(wxDataViewEvent& ev); void onAddTitle(); void onDeleteTitle(); bool testDeleteTitle(); }; }DarkRadiant-2.5.0/plugins/dm.editing/MissionInfoGuiView.cpp000066400000000000000000000066761321750546400236740ustar00rootroot00000000000000#include "MissionInfoGuiView.h" #include "math/Vector4.h" namespace ui { MissionInfoGuiView::MissionInfoGuiView(wxWindow* parent) : GuiView(parent) {} void MissionInfoGuiView::setGLViewPort() { double width = _windowDims[0]; double height = _windowDims[1]; double aspectRatio = _bgDims[0] / _bgDims[1]; if (width / height > aspectRatio) { width = height * aspectRatio; } else { height = width / aspectRatio; } SetSize(static_cast(width), -1); glViewport(0, 0, static_cast(width), static_cast(height)); } void MissionInfoGuiView::setGui(const gui::IGuiPtr& gui) { // Call the base class first GuiView::setGui(gui); Vector2 topLeft(0, 0); Vector2 bottomRight(640, 480); if (_gui != NULL) { gui::IGuiWindowDefPtr bgWindowDef = _gui->findWindowDef(getTargetWindowDefName()); if (bgWindowDef) { const Vector4& rect = bgWindowDef->rect; topLeft = Vector2(rect[0], rect[1]); bottomRight = Vector2(rect[0] + rect[2], rect[1] + rect[3]); } } _bgDims = bottomRight - topLeft; _renderer.setVisibleArea(topLeft, bottomRight); // Only draw a certain windowDef setWindowDefFilter(getTargetWindowDefName()); } // ---------- Darkmod.xt ---------------- DarkmodTxtGuiView::DarkmodTxtGuiView(wxWindow* parent) : MissionInfoGuiView(parent) {} void DarkmodTxtGuiView::setMissionInfoFile(const map::DarkmodTxtPtr& file) { _file = file; } void DarkmodTxtGuiView::updateGuiState() { const gui::IGuiPtr& gui = getGui(); if (!_file || !gui) return; // This is a localised value, hardcode some for the moment being gui->setStateString("details_posx", "100"); gui->findWindowDef("modTitle")->text.setValue(_file->getTitle()); gui->findWindowDef("modDescription")->text.setValue(_file->getDescription()); gui->findWindowDef("modAuthor")->text.setValue(_file->getAuthor()); // These are internationalised strings in the GUI code, let's hardcode some for the preview gui->findWindowDef("modLastPlayedTitle")->text.setValue("Last played:"); gui->findWindowDef("modCompletedTitle")->text.setValue("Completed:"); gui->findWindowDef("modLastPlayedValue")->text.setValue("2017-11-19"); gui->findWindowDef("modCompletedValue")->text.setValue("2017-11-26"); gui->findWindowDef("modSizeTitle")->text.setValue("Space used:"); gui->findWindowDef("modSizeValue")->text.setValue("123 MB"); gui->findWindowDef("modSizeEraseFromDiskAction")->text.setValue("[Erase from disk]"); gui->findWindowDef("modLoadN")->text.setValue("Install Mission"); gui->findWindowDef("modLoadH")->text.setValue("Install Mission"); gui->findWindowDef("modLoad")->text.setValue("Install Mission"); gui->findWindowDef("moreInfoH")->text.setValue("Notes"); gui->findWindowDef("moreInfoN")->text.setValue("Notes"); gui->findWindowDef("moreInfo")->text.setValue("Notes"); redraw(); } std::string DarkmodTxtGuiView::getTargetWindowDefName() { return "ModToInstallParent"; } // ---------- Readme.xt ---------------- ReadmeTxtGuiView::ReadmeTxtGuiView(wxWindow* parent) : MissionInfoGuiView(parent) {} void ReadmeTxtGuiView::setMissionInfoFile(const map::ReadmeTxtPtr& file) { _file = file; } void ReadmeTxtGuiView::updateGuiState() { const gui::IGuiPtr& gui = getGui(); if (!_file || !gui) return; gui->setStateString("ModNotesText", _file->getContents()); gui->findWindowDef("ModInstallationNotesButtonOK")->text.setValue("OK"); redraw(); } std::string ReadmeTxtGuiView::getTargetWindowDefName() { return "ModInstallationNotesParchment"; } } // namespace DarkRadiant-2.5.0/plugins/dm.editing/MissionInfoGuiView.h000066400000000000000000000033371321750546400233300ustar00rootroot00000000000000#pragma once #include "igui.h" #include "wxutil/preview/GuiView.h" #include "DarkmodTxt.h" #include "ReadmeTxt.h" namespace ui { /** * greebo: Specialisation of the generic GuiView with regard to the mission info dialog. * The viewport is cropped to the size of a certain windowDef. */ class MissionInfoGuiView : public wxutil::GuiView { protected: Vector2 _bgDims; std::vector backgroundDefList; public: MissionInfoGuiView(wxWindow* parent); virtual void setGui(const gui::IGuiPtr& gui) override; // Loads values into the GUI (state) variables virtual void updateGuiState() = 0; protected: virtual void setGLViewPort(); // Returns the name of the single window def to display virtual std::string getTargetWindowDefName() = 0; }; // Specialisation for displaying info in darkmod.txt // which will filter itself to the "ModToInstallParent" windowDef class DarkmodTxtGuiView : public MissionInfoGuiView { private: // The file containing the mission info this view is previewing map::DarkmodTxtPtr _file; public: DarkmodTxtGuiView(wxWindow* parent); void updateGuiState() override; void setMissionInfoFile(const map::DarkmodTxtPtr& file); protected: virtual std::string getTargetWindowDefName() override; }; // Specialisation for displaying info in readme.txt // which will filter itself to the "ModInstallationNotesParchment" windowDef class ReadmeTxtGuiView : public MissionInfoGuiView { private: // The file containing the mission info this view is previewing map::ReadmeTxtPtr _file; public: ReadmeTxtGuiView(wxWindow* parent); void updateGuiState() override; void setMissionInfoFile(const map::ReadmeTxtPtr& file); protected: virtual std::string getTargetWindowDefName() override; }; } // namespace DarkRadiant-2.5.0/plugins/dm.editing/MissionInfoTextFile.cpp000066400000000000000000000030331321750546400240210ustar00rootroot00000000000000#include "MissionInfoTextFile.h" #include #include #include #include "i18n.h" #include "itextstream.h" #include "igame.h" #include "os/path.h" namespace map { std::string MissionInfoTextFile::getFullOutputPath() { return GetOutputPathForCurrentMod() + getFilename(); } void MissionInfoTextFile::saveToCurrentMod() { std::string outputPath = getFullOutputPath(); rMessage() << "Writing " << getFilename() << " contents to " << outputPath << std::endl; std::ofstream outputStream; // Let the stream throw exceptions std::ios_base::iostate exceptionMask = outputStream.exceptions() | std::ios::failbit; outputStream.exceptions(exceptionMask); try { outputStream.open(outputPath); outputStream << toString(); outputStream.close(); rMessage() << "Successfully wrote " << getFilename() << " contents to " << outputPath << std::endl; } catch (std::ios_base::failure& ex) { throw std::runtime_error(fmt::format(_("Could not write {0} contents:\n{1}"), getFilename(), ex.what())); } } std::string MissionInfoTextFile::GetOutputPathForCurrentMod() { std::string modPath = GlobalGameManager().getModPath(); if (modPath.empty()) { rMessage() << "Mod path empty, falling back to mod base path..." << std::endl; modPath = GlobalGameManager().getModBasePath(); if (modPath.empty()) { rMessage() << "Mod base path empty as well, falling back to user engine path..." << std::endl; modPath = GlobalGameManager().getUserEnginePath(); } } return os::standardPathWithSlash(modPath); } } DarkRadiant-2.5.0/plugins/dm.editing/MissionInfoTextFile.h000066400000000000000000000020161321750546400234660ustar00rootroot00000000000000#pragma once #include #include #include namespace map { /** * Base declaration of a text file containing mission * information such as readme.txt and darkmod.txt. */ class MissionInfoTextFile { public: class ParseException : public std::runtime_error { public: ParseException(const std::string& msg) : runtime_error(msg.c_str()) {} }; // The filename of this file, e.g. "darkmod.txt" virtual std::string getFilename() = 0; // The full path to the description file virtual std::string getFullOutputPath(); // Saves the contents of this instance to the path applicable to the current mod // Throws a std::runtime_error if the file couldn't be written virtual void saveToCurrentMod(); // Retrieves the text representation of this instance, as it will be written to the darkmod.txt file virtual std::string toString() = 0; protected: // Returns the mod output path (normalised with trailing slash, without the filename part) static std::string GetOutputPathForCurrentMod(); }; } DarkRadiant-2.5.0/plugins/dm.editing/MissionReadmeDialog.cpp000066400000000000000000000075061321750546400240070ustar00rootroot00000000000000#include "MissionReadmeDialog.h" #include "igui.h" #include "itextstream.h" #include "i18n.h" #include #include #include #include #include #include #include "wxutil/preview/GuiView.h" #include "wxutil/dialog/MessageBox.h" #include "MissionInfoGuiView.h" namespace ui { namespace { const char* const WINDOW_TITLE = N_("Mission Readme Editor (readme.txt)"); } MissionReadmeDialog::MissionReadmeDialog(wxWindow* parent) : DialogBase(_(WINDOW_TITLE), parent), _updateInProgress(false) { populateWindow(); try { _readmeFile = map::ReadmeTxt::LoadForCurrentMod(); } catch (map::ReadmeTxt::ParseException& ex) { rError() << "Failed to parse readme.txt: " << ex.what() << std::endl; wxutil::Messagebox::ShowError( fmt::format(_("Failed to parse readme.txt:\n{0}"), ex.what()), this); // Reset the file to defaults _readmeFile.reset(); } _guiView->setGui(GlobalGuiManager().getGui("guis/mainmenu.gui")); _guiView->setMissionInfoFile(_readmeFile); updateValuesFromReadmeFile(); } void MissionReadmeDialog::updateValuesFromReadmeFile() { assert(_readmeFile); // this should be non-NULL at all times if (!_readmeFile) return; _updateInProgress = true; findNamedObject(this, "MissionInfoReadmeContentsEntry")->SetValue(_readmeFile->getContents()); findNamedObject(this, "MissionInfoReadmeOutputPath")->SetLabelText(_readmeFile->getFullOutputPath()); _guiView->updateGuiState(); _updateInProgress = false; } void MissionReadmeDialog::populateWindow() { SetSizer(new wxBoxSizer(wxVERTICAL)); wxPanel* panel = loadNamedPanel(this, "MissionInfoReadmeDialogMainPanel"); GetSizer()->Add(panel, 1, wxEXPAND); // Add the preview widget wxPanel* previewPanel = findNamedObject(this, "MissionInfoReadmeDialogPreviewPanel"); _guiView = new ReadmeTxtGuiView(previewPanel); previewPanel->GetSizer()->Add(_guiView, 1, wxEXPAND); makeLabelBold(this, "MissionReadmeLabel"); wxButton* saveButton = findNamedObject(this, "MissionInfoReadmeDialogSaveButton"); wxButton* cancelButton = findNamedObject(this, "MissionInfoReadmeDialogCancelButton"); saveButton->Bind(wxEVT_BUTTON, sigc::mem_fun(this, &MissionReadmeDialog::onSave)); cancelButton->Bind(wxEVT_BUTTON, sigc::mem_fun(this, &MissionReadmeDialog::onCancel)); // Wire up the text entry boxes to update the preview setupNamedEntryBox("MissionInfoReadmeContentsEntry"); // Make this dialog LARGE Layout(); FitToScreen(0.9f, 0.8f); // Set the sash to 50% wxSplitterWindow* splitter = findNamedObject(this, "MissionInfoReadmeSplitter"); splitter->SetSashPosition(this->GetSize().GetWidth() / 2); } void MissionReadmeDialog::setupNamedEntryBox(const std::string& ctrlName) { wxTextCtrl* ctrl = findNamedObject(this, ctrlName); assert(ctrl != nullptr); if (ctrl == nullptr) return; ctrl->Bind(wxEVT_TEXT, [this](wxCommandEvent& ev) { if (_updateInProgress) return; // Load the values from the UI to the ReadmeTxt instance _readmeFile->setContents(findNamedObject(this, "MissionInfoReadmeContentsEntry")->GetValue().ToStdString()); _guiView->updateGuiState(); }); } void MissionReadmeDialog::onSave(wxCommandEvent& ev) { try { // ReadmeTxt is kept in sync all the time, no need to load anything, just save to disk _readmeFile->saveToCurrentMod(); // Close the dialog EndModal(wxID_OK); } catch (std::runtime_error& err) { wxutil::Messagebox::ShowError(err.what(), this); } } void MissionReadmeDialog::onCancel(wxCommandEvent& ev) { // destroy dialog without saving EndModal(wxID_CANCEL); } void MissionReadmeDialog::ShowDialog(const cmd::ArgumentList& args) { MissionReadmeDialog* instance = new MissionReadmeDialog; instance->ShowModal(); instance->Destroy(); } } DarkRadiant-2.5.0/plugins/dm.editing/MissionReadmeDialog.h000066400000000000000000000013731321750546400234500ustar00rootroot00000000000000#pragma once #include "icommandsystem.h" #include "wxutil/dialog/DialogBase.h" #include "wxutil/XmlResourceBasedWidget.h" #include "MissionInfoGuiView.h" #include "ReadmeTxt.h" namespace wxutil { class GuiView; } namespace ui { class MissionReadmeDialog : public wxutil::DialogBase, private wxutil::XmlResourceBasedWidget { private: ReadmeTxtGuiView* _guiView; map::ReadmeTxtPtr _readmeFile; bool _updateInProgress; public: // Constructor MissionReadmeDialog(wxWindow* parent = nullptr); static void ShowDialog(const cmd::ArgumentList& args); private: void populateWindow(); void updateValuesFromReadmeFile(); void setupNamedEntryBox(const std::string& ctrlName); void onSave(wxCommandEvent& ev); void onCancel(wxCommandEvent& ev); }; } DarkRadiant-2.5.0/plugins/dm.editing/ReadmeTxt.cpp000066400000000000000000000023301321750546400220130ustar00rootroot00000000000000#include "ReadmeTxt.h" #include "i18n.h" #include "iarchive.h" #include "itextstream.h" #include "ifilesystem.h" namespace map { std::string ReadmeTxt::getFilename() { return NAME(); } const std::string& ReadmeTxt::getContents() { return _contents; } void ReadmeTxt::setContents(const std::string& contents) { _contents = contents; } ReadmeTxtPtr ReadmeTxt::CreateFromString(const std::string& contents) { ReadmeTxtPtr info(new ReadmeTxt); // The readme.txt parser appears to be rather simple info->_contents = contents; return info; } ReadmeTxtPtr ReadmeTxt::CreateFromStream(std::istream& stream) { // Read all the stream contents into a string std::string str(std::istreambuf_iterator(stream), {}); return CreateFromString(str); } ReadmeTxtPtr ReadmeTxt::LoadForCurrentMod() { std::string readmeTxtPath = GetOutputPathForCurrentMod() + NAME(); rMessage() << "Trying to open file " << readmeTxtPath << std::endl; ArchiveTextFilePtr file = GlobalFileSystem().openTextFileInAbsolutePath(readmeTxtPath); if (file) { std::istream stream(&(file->getInputStream())); return CreateFromStream(stream); } return std::make_shared(); } std::string ReadmeTxt::toString() { return _contents; } } DarkRadiant-2.5.0/plugins/dm.editing/ReadmeTxt.h000066400000000000000000000023711321750546400214650ustar00rootroot00000000000000#pragma once #include #include "MissionInfoTextFile.h" namespace map { class ReadmeTxt; typedef std::shared_ptr ReadmeTxtPtr; /** * An object representing the readme.txt file as found in the * mission folder's root directory. It contains detailed info * and/or instructions for the player installing the mission. */ class ReadmeTxt : public MissionInfoTextFile { private: std::string _contents; public: static const char* NAME() { return "readme.txt"; } std::string getFilename() override; const std::string& getContents(); void setContents(const std::string& contents); // Named constructor parsing the given string into a DarkmodTxt instance // A parse exception will be thrown if the file is not compliant static ReadmeTxtPtr CreateFromString(const std::string& contents); // Named constructor parsing the given stream into a DarkmodTxt instance // A parse exception will be thrown if the file is not compliant static ReadmeTxtPtr CreateFromStream(std::istream& stream); // A parse exception will be thrown if the file is not compliant static ReadmeTxtPtr LoadForCurrentMod(); // Retrieves the text representation of this instance, as it will be written to the readme.txt file std::string toString() override; }; } DarkRadiant-2.5.0/plugins/dm.editing/ShaderReplacer.h000066400000000000000000000021601321750546400224500ustar00rootroot00000000000000#ifndef _SHADER_REPLACER_H_ #define _SHADER_REPLACER_H_ #include "inode.h" #include "ipatch.h" #include "ibrush.h" class ShaderReplacer : public scene::NodeVisitor { std::string _oldShader; std::string _newShader; std::size_t _count; public: ShaderReplacer(const std::string& oldShader, const std::string& newShader) : _oldShader(oldShader), _newShader(newShader), _count(0) {} bool pre(const scene::INodePtr& node) { // Check for patch IPatchNodePtr patchNode = std::dynamic_pointer_cast(node); if (patchNode != NULL) { if (patchNode->getPatch().getShader() == _oldShader) { patchNode->getPatch().setShader(_newShader); _count++; } } else { // Check for brush IBrush* brush = Node_getIBrush(node); if (brush != NULL) { for (std::size_t i = 0; i < brush->getNumFaces(); ++i) { IFace& face = brush->getFace(i); if (face.getShader() == _oldShader) { face.setShader(_newShader); _count++; } } } } return true; } std::size_t getReplaceCount() const { return _count; } }; #endif /* _SHADER_REPLACER_H_ */ DarkRadiant-2.5.0/plugins/dm.editing/SpawnargLinkedCheckbox.h000066400000000000000000000045651321750546400241570ustar00rootroot00000000000000#pragma once #include "iundo.h" #include "ieclass.h" #include "ientity.h" #include namespace ui { /** * An enhanced checkbox that is updating the named * entity property (spawnarg) when toggled. * * The logic toggled = "1" can be optionally inversed such that * an unchecked box reflects a property value of "1". */ class SpawnargLinkedCheckbox : public wxCheckBox { private: bool _inverseLogic; std::string _propertyName; Entity* _entity; bool _updateLock; bool _defaultValueForMissingKeyValue; public: SpawnargLinkedCheckbox(wxWindow* parent, const std::string& label, const std::string& propertyName, bool inverseLogic = false) : wxCheckBox(parent, wxID_ANY, label), _inverseLogic(inverseLogic), _propertyName(propertyName), _entity(NULL), _updateLock(false), _defaultValueForMissingKeyValue(false) { Connect(wxEVT_CHECKBOX, wxCommandEventHandler(SpawnargLinkedCheckbox::onToggle), NULL, this); } void setDefaultValueForMissingKeyValue(bool defaultValue) { _defaultValueForMissingKeyValue = defaultValue; } // Sets the edited Entity object void setEntity(Entity* entity) { _entity = entity; if (_entity == NULL) { SetToolTip(""); return; } SetToolTip(_propertyName + ": " + _entity->getEntityClass()->getAttribute(_propertyName).getDescription()); std::string keyValue = _entity->getKeyValue(_propertyName); bool value = _entity->getKeyValue(_propertyName) == "1"; // Missing spawnargs (value is empty) get the default value assigned if (keyValue.empty()) { value = _defaultValueForMissingKeyValue; } _updateLock = true; SetValue(_inverseLogic ? !value : value); _updateLock = false; } protected: void onToggle(wxCommandEvent& ev) { ev.Skip(); // Update the spawnarg if we have a valid entity if (!_updateLock && _entity != NULL) { UndoableCommand cmd("editAIProperties"); std::string newValue = ""; if (_inverseLogic) { newValue = GetValue() ? "0" : "1"; // Active => "0" } else { newValue = GetValue() ? "1" : "0"; } // Check if the new value conincides with an inherited one if (_entity->getEntityClass()->getAttribute(_propertyName).getValue() == newValue) { // in which case the property just gets removed from the entity newValue = ""; } _entity->setKeyValue(_propertyName, newValue); } } }; } // namespace DarkRadiant-2.5.0/plugins/dm.editing/SpawnargLinkedSpinButton.h000066400000000000000000000055571321750546400245400ustar00rootroot00000000000000#pragma once #include "iundo.h" #include "ieclass.h" #include "ientity.h" #include "string/convert.h" #include "util/ScopedBoolLock.h" #include #include #include #include namespace ui { /** * An enhanced spin button that is updating the named * entity property (spawnarg) when toggled. * * Due to some weird bug that prevented the wxSpinCtrlDouble * to be rendered with a wxScrolledWindow as immediate parent, * I had to work around it by putting the wxSpinCtrlDouble into * a parent wxPanel first. */ class SpawnargLinkedSpinButton : public wxPanel { private: wxSpinCtrlDouble* _spinCtrl; std::string _label; std::string _propertyName; Entity* _entity; bool _updateLock; public: SpawnargLinkedSpinButton(wxWindow* parent, const std::string& label, const std::string& propertyName, double min, double max, double increment = 1, unsigned int digits = 0) : wxPanel(parent, wxID_ANY), _spinCtrl(new wxSpinCtrlDouble(this, wxID_ANY)), _label(label), _propertyName(propertyName), _entity(NULL), _updateLock(false) { this->SetSizer(new wxBoxSizer(wxHORIZONTAL)); this->GetSizer()->Add(_spinCtrl, 1, wxEXPAND); _spinCtrl->SetIncrement(increment); _spinCtrl->SetRange(min, max); _spinCtrl->SetDigits(digits); // 6 chars wide _spinCtrl->SetMaxSize(wxSize(GetCharWidth() * 9, -1)); _spinCtrl->Connect(wxEVT_SPINCTRLDOUBLE, wxSpinDoubleEventHandler(SpawnargLinkedSpinButton::onSpinButtonChanged), NULL, this); } const std::string& getLabel() const { return _label; } // Sets the edited Entity object void setEntity(Entity* entity) { _entity = entity; if (_entity == NULL) { SetToolTip(""); return; } std::string desc = _propertyName + ": " + _entity->getEntityClass()->getAttribute(_propertyName).getDescription(); _spinCtrl->SetToolTip(desc); if (_updateLock) return; util::ScopedBoolLock lock(_updateLock); _spinCtrl->SetValue(string::convert(_entity->getKeyValue(_propertyName))); } protected: void onSpinButtonChanged(wxSpinDoubleEvent& ev) { ev.Skip(); // Update the spawnarg if we have a valid entity if (!_updateLock && _entity != NULL) { util::ScopedBoolLock lock(_updateLock); UndoableCommand cmd("editAIProperties"); double floatVal = _spinCtrl->GetValue(); std::string newValue = fmt::format("{0:." + string::to_string(_spinCtrl->GetDigits()) + "f}", floatVal); // Check if the new value conincides with an inherited one const EntityClassAttribute& attr = _entity->getEntityClass()->getAttribute(_propertyName); if (!attr.getValue().empty() && string::to_float(attr.getValue()) == floatVal) { // in which case the property just gets removed from the entity newValue = ""; } _entity->setKeyValue(_propertyName, newValue); } } }; } // namespace DarkRadiant-2.5.0/plugins/dm.editing/SpawnargReplacer.h000066400000000000000000000042441321750546400230310ustar00rootroot00000000000000#pragma once #include "inode.h" #include "entitylib.h" class SpawnargReplacer : public scene::NodeVisitor { std::string _oldVal; std::string _newVal; std::size_t _modelCount; std::size_t _otherCount; std::size_t _eclassCount; // A list of keys to be replaced typedef std::vector KeyList; typedef std::map EntityKeyMap; EntityKeyMap _entityMap; KeyList _curKeys; public: SpawnargReplacer(const std::string& oldVal, const std::string& newVal) : _oldVal(oldVal), _newVal(newVal), _modelCount(0), _otherCount(0), _eclassCount(0) {} bool pre(const scene::INodePtr& node) { Entity* ent = Node_getEntity(node); if (ent != NULL) { _curKeys.clear(); // Traverse the entity's spawnargs to check for keys to be replaced ent->forEachKeyValue([&](const std::string& key, const std::string& value) { if (value == _oldVal) { // Matching value, remember this key _curKeys.push_back(key); } }); // Save the result of the spawnarg search if (!_curKeys.empty()) { _entityMap[node] = _curKeys; } _curKeys.clear(); } return false; } void processEntities() { for (EntityKeyMap::const_iterator e = _entityMap.begin(); e != _entityMap.end(); ++e) { const scene::INodePtr& ent = e->first; const KeyList& keys = e->second; for (KeyList::const_iterator i = keys.begin(); i != keys.end(); ++i) { // We have a match, check which key is affected if (*i == "classname") { // Classname change changeEntityClassname(ent, _newVal); _eclassCount++; } else { Entity* entity = Node_getEntity(ent); assert(entity != NULL); entity->setKeyValue(*i, _newVal); if (*i == "model") { _modelCount++; } else { _otherCount++; } } } } _entityMap.clear(); } std::size_t getModelCount() const { return _modelCount; } std::size_t getOtherCount() const { return _otherCount; } std::size_t getEclassCount() const { return _eclassCount; } }; DarkRadiant-2.5.0/plugins/dm.editing/plugin.cpp000066400000000000000000000055771321750546400214340ustar00rootroot00000000000000#include "imodule.h" #include "i18n.h" #include "ieventmanager.h" #include "iradiant.h" #include "iuimanager.h" #include "iselection.h" #include "ientityinspector.h" #include "icommandsystem.h" #include "itextstream.h" #include "imainframe.h" #include "debugging/debugging.h" #include "AIHeadPropertyEditor.h" #include "AIVocalSetPropertyEditor.h" #include "FixupMapDialog.h" #include "AIEditingPanel.h" #include "MissionInfoEditDialog.h" class EditingModule : public RegisterableModule { public: // RegisterableModule implementation virtual const std::string& getName() const { static std::string _name("DarkMod Editing"); return _name; } virtual const StringSet& getDependencies() const { static StringSet _dependencies; if (_dependencies.empty()) { _dependencies.insert(MODULE_ENTITYINSPECTOR); _dependencies.insert(MODULE_EVENTMANAGER); _dependencies.insert(MODULE_UIMANAGER); _dependencies.insert(MODULE_SELECTIONSYSTEM); _dependencies.insert(MODULE_COMMANDSYSTEM); _dependencies.insert(MODULE_MAINFRAME); _dependencies.insert(MODULE_RADIANT); } return _dependencies; } virtual void initialiseModule(const ApplicationContext& ctx) { rMessage() << getName() << "::initialiseModule called." << std::endl; // Associated "def_head" with an empty property editor instance GlobalEntityInspector().registerPropertyEditor( ui::DEF_HEAD_KEY, ui::IPropertyEditorPtr(new ui::AIHeadPropertyEditor()) ); GlobalEntityInspector().registerPropertyEditor( ui::DEF_VOCAL_SET_KEY, ui::IPropertyEditorPtr(new ui::AIVocalSetPropertyEditor()) ); GlobalCommandSystem().addCommand("FixupMapDialog", ui::FixupMapDialog::RunDialog); GlobalEventManager().addCommand("FixupMapDialog", "FixupMapDialog"); GlobalUIManager().getMenuManager().add("main/map", "FixupMapDialog", ui::menuItem, _("Fixup Map..."), // caption "", // icon "FixupMapDialog" ); GlobalCommandSystem().addCommand("MissionInfoEditDialog", ui::MissionInfoEditDialog::ShowDialog); GlobalEventManager().addCommand("MissionInfoEditDialog", "MissionInfoEditDialog"); GlobalUIManager().getMenuManager().add("main/map", "MissionInfoEditDialog", ui::menuItem, _("Edit Package Info (darkmod.txt)..."), // caption "sr_icon_readable.png", // icon "MissionInfoEditDialog" ); GlobalRadiant().signal_radiantStarted().connect( sigc::ptr_fun(ui::AIEditingPanel::onRadiantStartup) ); } void shutdownModule() { ui::AIEditingPanel::Shutdown(); // Remove associated property keys GlobalEntityInspector().unregisterPropertyEditor(ui::DEF_VOCAL_SET_KEY); GlobalEntityInspector().unregisterPropertyEditor(ui::DEF_HEAD_KEY); } }; typedef std::shared_ptr EditingModulePtr; extern "C" void DARKRADIANT_DLLEXPORT RegisterModule(IModuleRegistry& registry) { module::performDefaultInitialisation(registry); registry.registerModule(EditingModulePtr(new EditingModule)); } DarkRadiant-2.5.0/plugins/dm.gui/000077500000000000000000000000001321750546400165555ustar00rootroot00000000000000DarkRadiant-2.5.0/plugins/dm.gui/GuiSelector.cpp000066400000000000000000000131361321750546400215120ustar00rootroot00000000000000#include "GuiSelector.h" #include "wxutil/VFSTreePopulator.h" #include "i18n.h" #include "imainframe.h" #include "iuimanager.h" #include "gui/GuiManager.h" #include "wxutil/dialog/MessageBox.h" #include "ReadablePopulator.h" #include "ReadableEditorDialog.h" #include #include namespace ui { namespace { const char* const WINDOW_TITLE = N_("Choose a Gui Definition..."); const int WINDOW_WIDTH = 400; const int WINDOW_HEIGHT = 500; const char* const GUI_ICON = "sr_icon_readable.png"; const char* const FOLDER_ICON = "folder16.png"; } GuiSelector::GuiSelector(bool twoSided, ReadableEditorDialog* editorDialog) : DialogBase(_(WINDOW_TITLE), editorDialog), _editorDialog(editorDialog), _notebook(NULL), _oneSidedStore(new wxutil::TreeModel(_columns)), _twoSidedStore(new wxutil::TreeModel(_columns)), _oneSidedView(NULL), _twoSidedView(NULL) { _guiIcon.CopyFromBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + GUI_ICON)); _folderIcon.CopyFromBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + FOLDER_ICON)); // Set the windowsize and default border width in accordance to the HIG SetSize(WINDOW_WIDTH, WINDOW_HEIGHT); populateWindow(); // Set the current page and connect the switch-page signal afterwards. _notebook->SetSelection(twoSided ? 1 : 0); _notebook->Connect(wxEVT_NOTEBOOK_PAGE_CHANGED, wxBookCtrlEventHandler(GuiSelector::onPageSwitch), NULL, this); // We start with an empty selection, so de-sensitise the OK button FindWindowById(wxID_OK, this)->Enable(false); } bool GuiSelector::Destroy() { // Prevent the page switch event from firing after window destruction // In wxGTK the window might not be destroyed right away (only later in an idle event) // which will trigger page switch events and inadvertently change our GUI selection. _notebook->Disconnect(wxEVT_NOTEBOOK_PAGE_CHANGED, wxBookCtrlEventHandler(GuiSelector::onPageSwitch), NULL, this); return wxutil::DialogBase::Destroy(); } std::string GuiSelector::Run(bool twoSided, ReadableEditorDialog* editorDialog) { GuiSelector* dialog = new GuiSelector(twoSided, editorDialog); std::string rv = ""; try { dialog->fillTrees(); // may throw OperationAbortedException if (dialog->ShowModal() == wxID_OK) { rv = "guis/" + dialog->_name; } } catch (wxutil::ModalProgressDialog::OperationAbortedException&) { rv = ""; } dialog->Destroy(); return rv; } void GuiSelector::visit(wxutil::TreeModel& /* store */, wxutil::TreeModel::Row& row, const std::string& path, bool isExplicit) { // Get the display name by stripping off everything before the last // slash std::string displayName = path.substr(path.rfind("/") + 1); displayName = displayName.substr(0,displayName.rfind(".")); // Fill in the column values row[_columns.name] = wxVariant(wxDataViewIconText(displayName, isExplicit ? _guiIcon : _folderIcon)); row[_columns.fullName] = path; row[_columns.isFolder] = !isExplicit; row.SendItemAdded(); } void GuiSelector::fillTrees() { wxutil::VFSTreePopulator popOne(_oneSidedStore); wxutil::VFSTreePopulator popTwo(_twoSidedStore); ReadablePopulator walker(popOne, popTwo); GlobalGuiManager().foreachGui(walker); popOne.forEachNode(*this); popTwo.forEachNode(*this); _oneSidedStore->SortModelFoldersFirst(_columns.name, _columns.isFolder); _twoSidedStore->SortModelFoldersFirst(_columns.name, _columns.isFolder); _oneSidedView->AssociateModel(_oneSidedStore.get()); _twoSidedView->AssociateModel(_twoSidedStore.get()); } void GuiSelector::populateWindow() { // Add a vbox for the dialog elements SetSizer(new wxBoxSizer(wxVERTICAL)); // Add a vbox for the dialog elements wxBoxSizer* vbox = new wxBoxSizer(wxVERTICAL); GetSizer()->Add(vbox, 1, wxEXPAND | wxALL, 12); // Create the tabs _notebook = new wxNotebook(this, wxID_ANY); // One-Sided Readables Tab _oneSidedView = wxutil::TreeView::Create(_notebook, wxDV_NO_HEADER); _oneSidedView->AppendIconTextColumn(_("Gui Path"), _columns.name.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); _oneSidedView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(GuiSelector::onSelectionChanged), NULL, this); _notebook->AddPage(_oneSidedView, _("One-Sided Readable Guis")); // Two-Sided Readables Tab _twoSidedView = wxutil::TreeView::Create(_notebook, wxDV_NO_HEADER); _twoSidedView->AppendIconTextColumn(_("Gui Path"), _columns.name.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); _twoSidedView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(GuiSelector::onSelectionChanged), NULL, this); _notebook->AddPage(_twoSidedView, _("Two-Sided Readable Guis")); // Packing vbox->Add(_notebook, 1, wxEXPAND | wxBOTTOM, 6); vbox->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT); } void GuiSelector::onPageSwitch(wxBookCtrlEvent& ev) { if (ev.GetSelection() == 0) { _editorDialog->useOneSidedEditing(); } else { _editorDialog->useTwoSidedEditing(); } } void GuiSelector::onSelectionChanged(wxDataViewEvent& ev) { wxutil::TreeView* view = dynamic_cast(ev.GetEventObject()); assert(view != NULL); wxDataViewItem item = view->GetSelection(); if (item.IsOk()) { wxutil::TreeModel::Row row(item, *view->GetModel()); if (!row[_columns.isFolder].getBool()) { _name = row[_columns.fullName]; std::string guiPath = "guis/" + _name; _editorDialog->updateGuiView(this, guiPath); FindWindowById(wxID_OK, this)->Enable(true); return; } } FindWindowById(wxID_OK, this)->Enable(false); } } // namespace DarkRadiant-2.5.0/plugins/dm.gui/GuiSelector.h000066400000000000000000000042061321750546400211550ustar00rootroot00000000000000#pragma once #include "wxutil/dialog/DialogBase.h" #include "wxutil/VFSTreePopulator.h" #include "wxutil/TreeView.h" class wxNotebook; class wxBookCtrlEvent; namespace ui { // Forward decl. class ReadableEditorDialog; ///////////////////////////// XDataSelector: // Selector-Dialog for TwoSided and OneSided readable guis. Switching the pages of the notebook // also toggles the according editing mode on the ReadableEditorDialog (TwoSided or OneSided). // Selecting a gui definition updates the guiView for previewing. class GuiSelector : public wxutil::DialogBase, public wxutil::VFSTreePopulator::Visitor { public: // Treestore enum struct GuiTreeModelColumns : public wxutil::TreeModel::ColumnRecord { GuiTreeModelColumns() : name(add(wxutil::TreeModel::Column::IconText)), fullName(add(wxutil::TreeModel::Column::String)), isFolder(add(wxutil::TreeModel::Column::Boolean)) {} wxutil::TreeModel::Column name; wxutil::TreeModel::Column fullName; wxutil::TreeModel::Column isFolder; }; private: // Reference to the calling ReadableEditorDialog ReadableEditorDialog* _editorDialog; // The name that was picked. std::string _name; // The notebook holding the tabs for one-sided and two-sided readables. wxNotebook* _notebook; GuiTreeModelColumns _columns; wxutil::TreeModel::Ptr _oneSidedStore; wxutil::TreeModel::Ptr _twoSidedStore; wxutil::TreeView* _oneSidedView; wxutil::TreeView* _twoSidedView; wxIcon _guiIcon; wxIcon _folderIcon; public: // Starts the GuiSelector and returns the name of the selected GUI or an empty string if the user canceled. // The dialog shows the twoSided treeview if twoSided is true. static std::string Run(bool twoSided, ReadableEditorDialog* editorDialog); void visit(wxutil::TreeModel& store, wxutil::TreeModel::Row& row, const std::string& path, bool isExplicit); // Disconnect all events when the dialog is destroyed virtual bool Destroy() override; private: GuiSelector(bool twoSided, ReadableEditorDialog* editorDialog); void fillTrees(); void populateWindow(); void onSelectionChanged(wxDataViewEvent& ev); void onPageSwitch(wxBookCtrlEvent& ev); }; } // namespace DarkRadiant-2.5.0/plugins/dm.gui/Makefile.am000066400000000000000000000021111321750546400206040ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs \ $(XML_CFLAGS) pluginsdir = $(pkglibdir)/plugins plugins_LTLIBRARIES = dm_gui.la dm_gui_la_LIBADD = $(top_builddir)/libs/wxutil/libwxutil.la \ $(top_builddir)/libs/xmlutil/libxmlutil.la dm_gui_la_LDFLAGS = -module -avoid-version \ -lpthread \ $(WX_LIBS) \ $(XML_LIBS) \ $(GLEW_LIBS) \ $(GL_LIBS) \ $(FILESYSTEM_LIBS) dm_gui_la_SOURCES = GuiSelector.cpp \ plugin.cpp \ ReadableEditorDialog.cpp \ ReadableGuiView.cpp \ XData.cpp \ XDataLoader.cpp \ XDataSelector.cpp \ XdFileChooserDialog.cpp \ gui/Gui.cpp \ gui/GuiExpression.cpp \ gui/GuiManager.cpp \ gui/GuiScript.cpp \ gui/GuiWindowDef.cpp \ gui/RenderableCharacterBatch.cpp \ gui/RenderableText.cpp \ gui/Variable.cpp DarkRadiant-2.5.0/plugins/dm.gui/ReadableEditorDialog.cpp000066400000000000000000001343011321750546400232510ustar00rootroot00000000000000// Project related #include "ReadableEditorDialog.h" #include "XdFileChooserDialog.h" #include "XDataSelector.h" #include "GuiSelector.h" #include "gui/GuiManager.h" #include "TextViewInfoDialog.h" #include "ReadableGuiView.h" // General #include "selectionlib.h" #include "wxutil/dialog/MessageBox.h" #include "registry/registry.h" #include "string/string.h" #include "string/convert.h" #include "os/file.h" #include "os/path.h" #include "i18n.h" #include "itextstream.h" // Modules #include "iundo.h" #include "imainframe.h" #include "ientity.h" #include "imap.h" #include "igame.h" #include "idialogmanager.h" #include #include #include #include #include #include namespace ui { // consts: namespace { const char* const WINDOW_TITLE = N_("Readable Editor"); const char* const NO_ENTITY_ERROR = N_("Cannot run Readable Editor on this selection.\n" "Please select a single XData entity."); enum MenuItemId { InsertWholePage = 1, InsertLeft, InsertRight, DeleteWholePage, DeleteLeft, DeleteRight, AppendPage, PrependPage, ShowXDataSummary, ShowDuplicatedDefs, ShowGuiImportSummary, }; } // namespace ////////////////////////////////////////////////////////////////////////////// // Public and protected methods: ReadableEditorDialog::ReadableEditorDialog(Entity* entity) : DialogBase(_(WINDOW_TITLE)), _guiView(NULL), _entity(entity), _xdLoader(new XData::XDataLoader()), _currentPageIndex(0), _xdNameSpecified(false), _runningGuiLayoutCheck(false), _runningXDataUniquenessCheck(false), _useDefaultFilename(true), _saveInProgress(false) { wxPanel* mainPanel = loadNamedPanel(this, "ReadableEditorMainPanel"); wxPanel* previewPanel = findNamedObject(this, "ReadableEditorPreviewPanel"); _guiView = new gui::ReadableGuiView(previewPanel); previewPanel->GetSizer()->Add(_guiView, 1, wxEXPAND); setupGeneralPropertiesInterface(); setupPageRelatedInterface(); setupButtonPanel(); createMenus(); mainPanel->Layout(); mainPanel->Fit(); Fit(); CenterOnParent(); } int ReadableEditorDialog::ShowModal() { // Load the initial values from the entity if (!initControlsFromEntity()) { // User clicked cancel, so destroy the window. return wxID_CANCEL; } // Initialize proper editing controls. populateControlsFromXData(); // Initialise the GL widget after the widgets have been shown _guiView->initialiseView(); return DialogBase::ShowModal(); } void ReadableEditorDialog::RunDialog(const cmd::ArgumentList& args) { // Check prerequisites const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo(); if (info.entityCount == 1 && info.totalCount == info.entityCount) { // Check the entity type Entity* entity = Node_getEntity(GlobalSelectionSystem().ultimateSelected()); if (entity != NULL && entity->getKeyValue("editor_readable") == "1") { // Show the dialog ReadableEditorDialog* dialog = new ReadableEditorDialog(entity); dialog->ShowModal(); dialog->Destroy(); return; } } // Exactly one redable entity must be selected. wxutil::Messagebox::ShowError(_(NO_ENTITY_ERROR)); } // UI Creation void ReadableEditorDialog::setupGeneralPropertiesInterface() { makeLabelBold(this, "ReadableEditorGeneralLabel"); makeLabelBold(this, "ReadableEditorPageLabel"); // Readable Name _nameEntry = findNamedObject(this, "ReadableEditorInventoryName"); _nameEntry->Connect(wxEVT_CHAR, wxKeyEventHandler(ReadableEditorDialog::onChar), NULL, this); // XData Name _xDataNameEntry = findNamedObject(this, "ReadableEditorXDataName"); _xDataNameEntry->Connect(wxEVT_CHAR, wxKeyEventHandler(ReadableEditorDialog::onChar), NULL, this); _xDataNameEntry->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(ReadableEditorDialog::onFocusOut), NULL, this); // Add a browse-button. findNamedObject(this, "ReadableEditorXDBrowseButton")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onBrowseXd), NULL, this); // Page count _numPages = findNamedObject(this, "ReadableEditorNumPages"); _numPages->SetRange(1, 20); _numPages->Connect(wxEVT_SPINCTRL, wxSpinEventHandler(ReadableEditorDialog::onNumPagesChanged), NULL, this); _numPages->Connect(wxEVT_CHAR, wxKeyEventHandler(ReadableEditorDialog::onChar), NULL, this); // Page Layout: _oneSidedButton = findNamedObject(this, "ReadableEditorOneSided"); _oneSidedButton->Connect(wxEVT_RADIOBUTTON, wxCommandEventHandler(ReadableEditorDialog::onOneSided), NULL, this); _twoSidedButton = findNamedObject(this, "ReadableEditorTwoSided"); _twoSidedButton->Connect(wxEVT_RADIOBUTTON, wxCommandEventHandler(ReadableEditorDialog::onTwoSided), NULL, this); // Pageturn Sound _pageTurnEntry = findNamedObject(this, "ReadableEditorPageTurnSound"); } void ReadableEditorDialog::setupPageRelatedInterface() { // Insert Button findNamedObject(this, "ReadableEditorInsertPage")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onInsert), NULL, this); // Delete Button findNamedObject(this, "ReadableEditorDeletePage")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onDelete), NULL, this); // Page Switcher findNamedObject(this, "ReadableEditorGotoFirstPage")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onFirstPage), NULL, this); findNamedObject(this, "ReadableEditorGotoPreviousPage")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onPrevPage), NULL, this); findNamedObject(this, "ReadableEditorGotoNextPage")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onNextPage), NULL, this); findNamedObject(this, "ReadableEditorGotoLastPage")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onLastPage), NULL, this); _curPageDisplay = findNamedObject(this, "ReadableEditorCurPage"); // Add a gui chooser with a browse-button _guiEntry = findNamedObject(this, "ReadableEditorGuiDefinition"); _guiEntry->Connect(wxEVT_CHAR, wxKeyEventHandler(ReadableEditorDialog::onChar), NULL, this); _guiEntry->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(ReadableEditorDialog::onFocusOut), NULL, this); findNamedObject(this, "ReadableEditorGuiBrowseButton")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onBrowseGui), NULL, this); // Create "left" and "right" labels and add them to the first row of the table _pageLeftLabel = findNamedObject(this, "ReadableEditorPageLeftLabel"); _pageRightLabel = findNamedObject(this, "ReadableEditorPageRightLabel"); _textViewTitle = findNamedObject(this, "ReadableEditorTitleLeft"); _textViewTitle->Connect(wxEVT_TEXT, wxCommandEventHandler(ReadableEditorDialog::onTextChanged), NULL, this); _textViewRightTitle = findNamedObject(this, "ReadableEditorTitleRight"); _textViewRightTitle->Connect(wxEVT_TEXT, wxCommandEventHandler(ReadableEditorDialog::onTextChanged), NULL, this); // Create "body" label and body-textViews and add them to the third row of the table. Add the key-press-event. _textViewBody = findNamedObject(this, "ReadableEditorBodyLeft"); _textViewBody->Connect(wxEVT_TEXT, wxCommandEventHandler(ReadableEditorDialog::onTextChanged), NULL, this); _textViewRightBody = findNamedObject(this, "ReadableEditorBodyRight"); _textViewRightBody->Connect(wxEVT_TEXT, wxCommandEventHandler(ReadableEditorDialog::onTextChanged), NULL, this); } void ReadableEditorDialog::createMenus() { // Insert menu _insertMenu.reset(new wxMenu); _insertMenu->Append(InsertWholePage, _("Insert whole Page"), ""); _insertMenu->Append(InsertLeft, _("Insert on left Side"), ""); _insertMenu->Append(InsertRight, _("Insert on right Side"), ""); _insertMenu->Connect(wxEVT_MENU, wxCommandEventHandler(ReadableEditorDialog::onMenuItemClick), NULL, this); // Delete Menu _deleteMenu.reset(new wxMenu); _deleteMenu->Append(DeleteWholePage, _("Delete whole Page"), ""); _deleteMenu->Append(DeleteLeft, _("Delete on left Side"), ""); _deleteMenu->Append(DeleteRight, _("Delete on right Side"), ""); _deleteMenu->Connect(wxEVT_MENU, wxCommandEventHandler(ReadableEditorDialog::onMenuItemClick), NULL, this); // Append Menu _appendMenu.reset(new wxMenu); _appendMenu->Append(AppendPage, _("Append Page"), ""); _appendMenu->Connect(wxEVT_MENU, wxCommandEventHandler(ReadableEditorDialog::onMenuItemClick), NULL, this); // Prepend Menu _prependMenu.reset(new wxMenu); _prependMenu->Append(PrependPage, _("Prepend Page"), ""); _prependMenu->Connect(wxEVT_MENU, wxCommandEventHandler(ReadableEditorDialog::onMenuItemClick), NULL, this); // Tools Menu _toolsMenu.reset(new wxMenu); _toolsMenu->Append(ShowXDataSummary, _("Show last XData import summary"), ""); _toolsMenu->Append(ShowDuplicatedDefs, _("Show duplicated definitions"), ""); _toolsMenu->Append(ShowGuiImportSummary, _("Show Gui import summary"), ""); _toolsMenu->Connect(wxEVT_MENU, wxCommandEventHandler(ReadableEditorDialog::onMenuItemClick), NULL, this); } void ReadableEditorDialog::setupButtonPanel() { findNamedObject(this, "ReadableEditorSave")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onSave), NULL, this); findNamedObject(this, "ReadableEditorCancel")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onCancel), NULL, this); findNamedObject(this, "ReadableEditorSaveAndClose")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onSaveClose), NULL, this); findNamedObject(this, "ReadableEditorTools")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onToolsClicked), NULL, this); } ////////////////////////////////////////////////////////////////////////////// // Private Methods: bool ReadableEditorDialog::initControlsFromEntity() { // Inv_name _nameEntry->SetValue(_entity->getKeyValue("inv_name")); // Xdata contents _xDataNameEntry->SetValue(_entity->getKeyValue("xdata_contents")); // Construct the map-based Filename _mapBasedFilename = GlobalMapModule().getMapName(); std::size_t nameStartPos = _mapBasedFilename.rfind("/") + 1; if (nameStartPos != std::string::npos) { _mapBasedFilename = _mapBasedFilename.substr(nameStartPos, _mapBasedFilename.rfind(".") - nameStartPos); } std::string defaultXdName = "readables/" + _mapBasedFilename + "/" + _(""); _mapBasedFilename += ".xd"; // Load xdata if (!_entity->getKeyValue("xdata_contents").empty()) { _xdNameSpecified = true; try { int result = XdFileChooserDialog::Import( _entity->getKeyValue("xdata_contents"), _xData, _xdFilename, _xdLoader, this ); if (result == wxID_OK) { _useDefaultFilename = false; refreshWindowTitle(); return true; } return false; // cancelled } catch (XdFileChooserDialog::ImportFailedException&) { std::string msg = fmt::format(_("Failed to import {0}."), _entity->getKeyValue("xdata_contents")); msg += "\n"; msg += _("Creating a new XData definition..."); msg += "\n\n"; msg += _("Do you want to open the import summary?"); wxutil::Messagebox dialog(_("Import failed"), msg, ui::IDialog::MESSAGE_ASK, this); if (dialog.run() == ui::IDialog::RESULT_YES) { showXdImportSummary(); } updateGuiView(); } } //No Xdata definition was defined or failed to import. Use default filename and create a OneSidedXData-object if (_entity->getKeyValue("name").find("book") == std::string::npos) { if (!_xdNameSpecified) _xData.reset(new XData::OneSidedXData(defaultXdName)); else _xData.reset(new XData::OneSidedXData(_entity->getKeyValue("xdata_contents"))); } else { if (!_xdNameSpecified) _xData.reset(new XData::TwoSidedXData(defaultXdName)); else _xData.reset(new XData::OneSidedXData(_entity->getKeyValue("xdata_contents"))); } _xData->setNumPages(1); refreshWindowTitle(); return true; } bool ReadableEditorDialog::save() { _saveInProgress = true; UndoableCommand cmd("editReadable"); // Name _entity->setKeyValue("inv_name", _nameEntry->GetValue().ToStdString()); // Xdata contents _entity->setKeyValue("xdata_contents", _xDataNameEntry->GetValue().ToStdString()); // Current content to XData Object storeXData(); // Get the storage path and check its validity std::string storagePath = constructStoragePath(); if (!_useDefaultFilename && !os::fileOrDirExists(storagePath)) { // The file does not exist, so we have imported a definition contained inside a PK4. wxutil::Messagebox::ShowError( _("You have imported an XData definition that is contained in a PK4, which can't be accessed for saving.") + std::string("\n\n") + _("Please rename your XData definition, so that it is stored under a different filename."), this ); _saveInProgress = false; return false; } // Start exporting XData::FileStatus fst = _xData->xport(storagePath, XData::Merge); if (fst == XData::DefinitionExists) { switch (_xData->xport( storagePath, XData::MergeOverwriteExisting)) { case XData::OpenFailed: wxutil::Messagebox::ShowError( fmt::format(_("Failed to open {0} for saving."), _xdFilename), this ); _saveInProgress = false; return false; case XData::MergeFailed: wxutil::Messagebox::ShowError( _("Merging failed, because the length of the definition to be overwritten could not be retrieved."), this ); _saveInProgress = false; return false; default: //success! _saveInProgress = false; return true; } } else if (fst == XData::OpenFailed) { wxutil::Messagebox::ShowError( fmt::format(_("Failed to open {0} for saving."), _xdFilename), this ); } _saveInProgress = false; return false; } std::string ReadableEditorDialog::constructStoragePath() { // Construct the storage path from Registry keys. std::string storagePath; if (_useDefaultFilename) { bool checkVFS = true; switch (registry::getValue(RKEY_READABLES_STORAGE_FOLDER)) { case 0: // Use Mod dir storagePath = GlobalGameManager().getModPath(); if (storagePath.empty()) { // Mod path not defined. Use base Path storagePath = GlobalRegistry().get(RKEY_ENGINE_PATH) + "base/"; wxutil::Messagebox::ShowError(_("Mod path not defined. Using Base path..."), this); } storagePath += XData::XDATA_DIR; break; case 1: // Use Mod Base dir storagePath = GlobalGameManager().getModBasePath(); if (storagePath.empty()) { // Mod Base Path not defined. Use Mod path or base path successively. storagePath = GlobalGameManager().getModPath(); if (storagePath.empty()) { storagePath = GlobalRegistry().get(RKEY_ENGINE_PATH) + "base/"; wxutil::Messagebox::ShowError(_("Mod Base path not defined, neither is Mod path. Using Engine path..."), this); storagePath += XData::XDATA_DIR; break; } wxutil::Messagebox::ShowError(_("Mod Base path not defined. Using Mod path..."), this); } storagePath += XData::XDATA_DIR; break; default: // Use custom folder storagePath = GlobalRegistry().get(RKEY_READABLES_CUSTOM_FOLDER); if (storagePath.empty()) { // Custom path not defined. Use Mod path or base path successively. storagePath = GlobalGameManager().getModPath(); if (storagePath.empty()) { storagePath = GlobalRegistry().get(RKEY_ENGINE_PATH) + "base/"; wxutil::Messagebox::ShowError(_("Mod Base path not defined, neither is Mod path. Using Engine path..."), this); storagePath += XData::XDATA_DIR; break; } storagePath += XData::XDATA_DIR; wxutil::Messagebox::ShowError(_("Mod Base path not defined. Using Mod path..."), this); break; } storagePath += "/" +_mapBasedFilename; checkVFS = false; break; } // Check whether this path already exists in the VFS in another absolute folder. If so, rename it! if (checkVFS) { std::string compPath = GlobalFileSystem().findFile(XData::XDATA_DIR + _mapBasedFilename); std::string newFileName = _mapBasedFilename; if (!compPath.empty()) // VFS-path does exist, now check whether it is in another absolute folder. { int fileIdx = 2; compPath += XData::XDATA_DIR + _mapBasedFilename; while ( (compPath.compare(storagePath + newFileName) != 0) ) { newFileName = _mapBasedFilename.substr(0, _mapBasedFilename.rfind(".")) + string::to_string(fileIdx++) + ".xd"; compPath = GlobalFileSystem().findFile(XData::XDATA_DIR + newFileName); if (compPath.empty()) { wxutil::Messagebox::ShowError( fmt::format(_("{0}{1} already exists in another path.\n\nXData will be stored in {2}{3}!"), XData::XDATA_DIR, _mapBasedFilename, XData::XDATA_DIR, newFileName), this ); break; } compPath += XData::XDATA_DIR + newFileName; } } storagePath += newFileName; } } else { // We are exporting a previously imported XData definition. // The _xdFilename itself can be a VFS path or an absolute path, depending on the user's settings if (path_is_absolute(_xdFilename.c_str())) { storagePath = _xdFilename; } else { // Retrieve engine path and append _xdFilename storagePath = GlobalRegistry().get(RKEY_ENGINE_PATH) + _xdFilename; } } return storagePath; } void ReadableEditorDialog::refreshWindowTitle() { std::string title = constructStoragePath(); title = title.substr( title.find_first_not_of( GlobalRegistry().get(RKEY_ENGINE_PATH) ) ); title = std::string(_("Readable Editor")) + " - " + title; SetTitle(title); } void ReadableEditorDialog::storeXData() { //NumPages does not need storing, because it's stored directly after changing it. _xData->setName(_xDataNameEntry->GetValue().ToStdString()); _xData->setSndPageTurn(_pageTurnEntry->GetValue().ToStdString()); storeCurrentPage(); } void ReadableEditorDialog::toggleTwoSidedEditingInterface(bool show) { if (show) { _textViewRightTitle->GetContainingSizer()->Show(_textViewRightTitle); _textViewRightBody->GetContainingSizer()->Show(_textViewRightBody); _pageLeftLabel->GetContainingSizer()->Show(_pageLeftLabel); _pageRightLabel->GetContainingSizer()->Show(_pageRightLabel); } else { _textViewRightTitle->GetContainingSizer()->Hide(_textViewRightTitle); _textViewRightBody->GetContainingSizer()->Hide(_textViewRightBody); _pageLeftLabel->GetContainingSizer()->Hide(_pageLeftLabel); _pageRightLabel->GetContainingSizer()->Hide(_pageRightLabel); } _textViewRightTitle->GetContainingSizer()->Layout(); } void ReadableEditorDialog::showPage(std::size_t pageIndex) { //Gui Def before: std::string guiBefore = _guiEntry->GetValue().ToStdString(); // Update CurrentPage Label _currentPageIndex = pageIndex; _curPageDisplay->SetLabel(string::to_string(pageIndex+1)); if (_xData->getPageLayout() == XData::TwoSided) { // Update Gui statement entry from xData if (!_xData->getGuiPage(pageIndex).empty()) { _guiEntry->SetValue(_xData->getGuiPage(pageIndex)); } else { _guiEntry->SetValue(XData::DEFAULT_TWOSIDED_GUI); } setTextViewAndScroll(_textViewRightTitle, _xData->getPageContent(XData::Title, pageIndex, XData::Right)); setTextViewAndScroll(_textViewRightBody, _xData->getPageContent(XData::Body, pageIndex, XData::Right)); } else { // Update Gui statement entry from xData if (!_xData->getGuiPage(pageIndex).empty()) { _guiEntry->SetValue(_xData->getGuiPage(pageIndex)); } else { _guiEntry->SetValue(XData::DEFAULT_ONESIDED_GUI); } } // Update page statements textviews from xData setTextViewAndScroll(_textViewTitle, _xData->getPageContent(XData::Title, pageIndex, XData::Left)); setTextViewAndScroll(_textViewBody, _xData->getPageContent(XData::Body, pageIndex, XData::Left)); // Update the GUI View if the gui changed. For the page contents, updateGuiView is called automatically // by onTextChange. if (guiBefore != _guiEntry->GetValue().ToStdString()) { updateGuiView(); } } void ReadableEditorDialog::updateGuiView(wxWindow* parent, const std::string& guiPath, const std::string& xDataName, const std::string& xDataPath) { // If the name of an xData object is passed it will be rendered instead of the current // xData object, to enable previewing of XData definitions induced by the XDataSelector. if (!xDataName.empty()) { XData::XDataPtr xd; XData::XDataMap xdMap; if (_xdLoader->importDef(xDataName, xdMap, xDataPath)) { xd = xdMap.begin()->second; _guiView->setGui(GlobalGuiManager().getGui(xd->getGuiPage(0))); } else { std::string msg = fmt::format(_("Failed to import {0}."), xDataName); msg += "\n\n"; msg += _("Do you want to open the import summary?"); wxutil::Messagebox dialog(_("Import failed"), msg, ui::IDialog::MESSAGE_ASK, parent ? parent : this ); if (dialog.run() == ui::IDialog::RESULT_YES) { showXdImportSummary(); } return; } const gui::IGuiPtr& gui = _guiView->getGui(); if (gui == NULL) { std::string msg = fmt::format(_("Failed to load gui definition {0}."), xd->getGuiPage(0)); msg += "\n\n"; msg += _("Do you want to open the import summary?"); wxutil::Messagebox dialog(_("Import failed"), msg, ui::IDialog::MESSAGE_ASK, parent ? parent : this ); if (dialog.run() == ui::IDialog::RESULT_YES) { showGuiImportSummary(); } return; } // Load data from xdata into the GUI's state variables if (xd->getPageLayout() == XData::OneSided) { // One-sided has title and body gui->setStateString("title", xd->getPageContent(XData::Title, 0, XData::Left)); gui->setStateString("body", xd->getPageContent(XData::Body, 0, XData::Left)); } else { // Two-sided has four important state strings gui->setStateString("left_title", xd->getPageContent(XData::Title, 0, XData::Left)); gui->setStateString("left_body", xd->getPageContent(XData::Body, 0, XData::Left)); gui->setStateString("right_title", xd->getPageContent(XData::Title, 0, XData::Right)); gui->setStateString("right_body", xd->getPageContent(XData::Body, 0, XData::Right)); } initGuiState(gui); } else { if (guiPath.empty()) { _guiView->setGui(GlobalGuiManager().getGui(_guiEntry->GetValue().ToStdString())); } else { _guiView->setGui(GlobalGuiManager().getGui(guiPath)); } const gui::IGuiPtr& gui = _guiView->getGui(); if (gui == NULL) { std::string nameGui = guiPath.empty() ? _guiEntry->GetValue().ToStdString() : guiPath; std::string msg = fmt::format(_("Failed to load gui definition {0}."), nameGui); msg += "\n\n"; msg += _("Do you want to open the import summary?"); wxutil::Messagebox dialog(_("Import failed"), msg, ui::IDialog::MESSAGE_ASK, parent ? parent : this ); if (dialog.run() == ui::IDialog::RESULT_YES) { showGuiImportSummary(); } return; } // Load data from xdata into the GUI's state variables if (_xData->getPageLayout() == XData::OneSided) { // One-sided has title and body gui->setStateString("title", _textViewTitle->GetValue().ToStdString()); gui->setStateString("body", _textViewBody->GetValue().ToStdString()); } else { // Two-sided has four important state strings gui->setStateString("left_title", _textViewTitle->GetValue().ToStdString()); gui->setStateString("left_body", _textViewBody->GetValue().ToStdString()); gui->setStateString("right_title", _textViewRightTitle->GetValue().ToStdString()); gui->setStateString("right_body", _textViewRightBody->GetValue().ToStdString()); } // Set common GUI state for rendering initGuiState(gui); } _guiView->redraw(); } void ReadableEditorDialog::initGuiState(const gui::IGuiPtr& gui) { assert(gui); // Some fancy readables have custom backgrounds depending on the page number // This is usually done by game script at runtime, so let's fake something similar gui->setStateString("curPage", string::to_string(_currentPageIndex + 1)); gui->setStateString("numPages", string::to_string(_numPages->GetValue())); #if 0 // ContentsFadeIn is reponsible of setting custom backgrounds if (gui->findWindowDef("ContentsFadeIn")) { gui->findWindowDef("ContentsFadeIn")->notime = false; } #endif // Initialise the time of this GUI gui->initTime(0); // Run the first frame gui->update(16); } void ReadableEditorDialog::storeCurrentPage() { // Store the GUI-Page _xData->setGuiPage(_guiEntry->GetValue().ToStdString(), _currentPageIndex); // On OneSidedXData the Side-enum is discarded, so it's save to call this method _xData->setPageContent(XData::Title, _currentPageIndex, XData::Left, _textViewTitle->GetValue().ToStdString()); _xData->setPageContent(XData::Body, _currentPageIndex, XData::Left, _textViewBody->GetValue().ToStdString()); if (_xData->getPageLayout() == XData::TwoSided) { _xData->setPageContent(XData::Title, _currentPageIndex, XData::Right, _textViewRightTitle->GetValue().ToStdString()); _xData->setPageContent(XData::Body, _currentPageIndex, XData::Right, _textViewRightBody->GetValue().ToStdString()); } } void ReadableEditorDialog::populateControlsFromXData() { toggleTwoSidedEditingInterface(_xData->getPageLayout() == XData::TwoSided); showPage(0); _xDataNameEntry->SetValue(_xData->getName()); _numPages->SetValue(static_cast(_xData->getNumPages())); std::string sndString = _xData->getSndPageTurn(); _pageTurnEntry->SetValue( sndString.empty() ? XData::DEFAULT_SNDPAGETURN : sndString ); if (_xData->getPageLayout() == XData::TwoSided) { _twoSidedButton->SetValue(true); } else { _oneSidedButton->SetValue(true); } } void ReadableEditorDialog::checkXDataUniqueness() { _runningXDataUniquenessCheck = true; std::string xdn = _xDataNameEntry->GetValue().ToStdString(); if (_xData->getName() == xdn) { _runningXDataUniquenessCheck = false; return; } _xdLoader->retrieveXdInfo(); XData::StringVectorMap::const_iterator it = _xdLoader->getDefinitionList().find(xdn); if (it != _xdLoader->getDefinitionList().end()) { // The definition already exists. Ask the user whether it should be imported. If not make a different name suggestion. wxutil::Messagebox dialog( _("Import definition?"), fmt::format(_("The definition {0} already exists. Should it be imported?"), xdn), ui::IDialog::MESSAGE_ASK, this ); std::string message = ""; if (dialog.run() == ui::IDialog::RESULT_YES) { try { int result = XdFileChooserDialog::Import( xdn, _xData, _xdFilename, _xdLoader, this); if (result == wxID_OK) { _xdNameSpecified = true; _useDefaultFilename = false; populateControlsFromXData(); _runningXDataUniquenessCheck = false; refreshWindowTitle(); return; } } catch (XdFileChooserDialog::ImportFailedException&) { message = _("Import failed:"); message += "\n\t" + _xdLoader->getImportSummary()[_xdLoader->getImportSummary().size() - 1]; message += "\n\n"; message += _("Consult the import summary for further information."); message += "\n\n"; } } // Dialog RESULT_NO, XdFileChooserDialog::RESULT_CANCEL or import failed! Make a different name suggestion! std::string suggestion; for (int n = 1; n > 0; n++) { suggestion = xdn + string::to_string(n); if (_xdLoader->getDefinitionList().find(suggestion) == _xdLoader->getDefinitionList().end()) { // The suggested XData-name does not exist. break; } } // Update entry and XData object. Notify the user about the suggestion. _xDataNameEntry->SetValue(suggestion); _xData->setName(suggestion); message += fmt::format(_("To avoid duplicated XData definitions " "the current definition has been renamed to {0}."), suggestion); wxutil::Messagebox::Show( _("XData has been renamed."), message, ui::IDialog::MESSAGE_CONFIRM, this ); } else { _xData->setName(xdn); } _xdNameSpecified = true; _useDefaultFilename = true; _runningXDataUniquenessCheck = false; refreshWindowTitle(); } void ReadableEditorDialog::insertPage() { storeCurrentPage(); _xData->setNumPages(_xData->getNumPages()+1); _numPages->SetValue(static_cast(_xData->getNumPages())); handleNumberOfPagesChanged(); for (std::size_t n = _xData->getNumPages() - 1; n > _currentPageIndex; n--) { _xData->setGuiPage(_xData->getGuiPage(n - 1), n); _xData->setPageContent(XData::Title, n, XData::Left, _xData->getPageContent(XData::Title, n - 1, XData::Left) ); _xData->setPageContent(XData::Body, n, XData::Left, _xData->getPageContent(XData::Body, n - 1, XData::Left) ); } // New Page: _xData->setPageContent(XData::Title, _currentPageIndex, XData::Left, ""); _xData->setPageContent(XData::Body, _currentPageIndex, XData::Left, ""); _xData->setGuiPage(_xData->getGuiPage(_currentPageIndex + 1), _currentPageIndex); // Right side: if (_xData->getPageLayout() == XData::TwoSided) { for (std::size_t n = _xData->getNumPages() - 1; n > _currentPageIndex; n--) { _xData->setGuiPage(_xData->getGuiPage(n - 1), n); _xData->setPageContent(XData::Title, n, XData::Right, _xData->getPageContent(XData::Title, n - 1, XData::Right) ); _xData->setPageContent(XData::Body, n, XData::Right, _xData->getPageContent(XData::Body, n - 1, XData::Right) ); } // New Page: _xData->setPageContent(XData::Title, _currentPageIndex, XData::Right, ""); _xData->setPageContent(XData::Body, _currentPageIndex, XData::Right, ""); } showPage(_currentPageIndex); } void ReadableEditorDialog::deletePage() { if (_currentPageIndex == _xData->getNumPages() - 1) { if (_currentPageIndex == 0) { _xData->setNumPages(0); _xData->setNumPages(1); showPage(0); return; } _numPages->SetValue(static_cast(_currentPageIndex)); handleNumberOfPagesChanged(); } else { for (std::size_t n = _currentPageIndex; n < _xData->getNumPages()-1; n++) { _xData->setGuiPage(_xData->getGuiPage(n+1), n); _xData->setPageContent(XData::Title, n, XData::Left, _xData->getPageContent(XData::Title, n+1, XData::Left) ); _xData->setPageContent(XData::Body, n, XData::Left, _xData->getPageContent(XData::Body, n+1, XData::Left) ); } // Right Side if (_xData->getPageLayout() == XData::TwoSided) { for (std::size_t n = _currentPageIndex; n < _xData->getNumPages()-1; n++) { _xData->setGuiPage(_xData->getGuiPage(n+1), n); _xData->setPageContent(XData::Title, n, XData::Right, _xData->getPageContent(XData::Title, n+1, XData::Right) ); _xData->setPageContent(XData::Body, n, XData::Right, _xData->getPageContent(XData::Body, n+1, XData::Right) ); } } _xData->setNumPages(_xData->getNumPages() - 1); _numPages->SetValue(static_cast(_xData->getNumPages())); showPage(_currentPageIndex); } } void ReadableEditorDialog::deleteSide(bool rightSide) { storeCurrentPage(); if (!rightSide) { _xData->setPageContent(XData::Title, _currentPageIndex, XData::Left, _xData->getPageContent(XData::Title, _currentPageIndex, XData::Right) ); _xData->setPageContent(XData::Body, _currentPageIndex, XData::Left, _xData->getPageContent(XData::Body, _currentPageIndex, XData::Right) ); } if (_currentPageIndex < _xData->getNumPages() - 1) { _xData->setPageContent(XData::Title, _currentPageIndex, XData::Right, _xData->getPageContent(XData::Title, _currentPageIndex + 1, XData::Left) ); _xData->setPageContent(XData::Body, _currentPageIndex, XData::Right, _xData->getPageContent(XData::Body, _currentPageIndex + 1, XData::Left) ); for (std::size_t n = _currentPageIndex + 1; n < _xData->getNumPages() - 1; n++) { _xData->setPageContent(XData::Title, n, XData::Left, _xData->getPageContent(XData::Title, n, XData::Right)); _xData->setPageContent(XData::Title, n, XData::Right, _xData->getPageContent(XData::Title, n+1, XData::Left)); _xData->setPageContent(XData::Body, n, XData::Left, _xData->getPageContent(XData::Body, n, XData::Right)); _xData->setPageContent(XData::Body, n, XData::Right, _xData->getPageContent(XData::Body, n+1, XData::Left)); } _xData->setPageContent(XData::Title, _xData->getNumPages() - 1, XData::Left, _xData->getPageContent(XData::Title, _xData->getNumPages() - 1, XData::Right)); _xData->setPageContent(XData::Body, _xData->getNumPages() - 1, XData::Left, _xData->getPageContent(XData::Body, _xData->getNumPages() - 1, XData::Right)); } if (_xData->getPageContent(XData::Title, _xData->getNumPages() - 1, XData::Left).empty() && _xData->getPageContent(XData::Body, _xData->getNumPages() - 1, XData::Left).empty()) { // The last page has no content anymore, so delete it. _numPages->SetValue(static_cast(_xData->getNumPages() - 1)); handleNumberOfPagesChanged(); } else { _xData->setPageContent(XData::Title, _xData->getNumPages() - 1, XData::Right, ""); _xData->setPageContent(XData::Body, _xData->getNumPages() - 1, XData::Right, ""); } showPage(_currentPageIndex); } void ReadableEditorDialog::insertSide(bool rightSide) { storeCurrentPage(); if (!_xData->getPageContent(XData::Title, _xData->getNumPages()-1, XData::Right).empty() || !_xData->getPageContent(XData::Body, _xData->getNumPages()-1, XData::Right).empty()) { //Last side has content. Raise numPages before shifting _numPages->SetValue(static_cast(_xData->getNumPages() + 1)); handleNumberOfPagesChanged(); } for (std::size_t n = _xData->getNumPages() - 1; n>_currentPageIndex; n--) { _xData->setPageContent(XData::Title, n, XData::Right, _xData->getPageContent(XData::Title, n, XData::Left)); _xData->setPageContent(XData::Title, n, XData::Left, _xData->getPageContent(XData::Title, n-1, XData::Right)); _xData->setPageContent(XData::Body, n, XData::Right, _xData->getPageContent(XData::Body, n, XData::Left)); _xData->setPageContent(XData::Body, n, XData::Left, _xData->getPageContent(XData::Body, n-1, XData::Right)); } if (!rightSide) { _xData->setPageContent(XData::Title, _currentPageIndex, XData::Right, _xData->getPageContent(XData::Title, _currentPageIndex, XData::Left)); _xData->setPageContent(XData::Body, _currentPageIndex, XData::Right, _xData->getPageContent(XData::Body, _currentPageIndex, XData::Left)); _xData->setPageContent(XData::Title, _currentPageIndex, XData::Left, ""); _xData->setPageContent(XData::Body, _currentPageIndex, XData::Left, ""); } else { _xData->setPageContent(XData::Title, _currentPageIndex, XData::Right, ""); _xData->setPageContent(XData::Body, _currentPageIndex, XData::Right, ""); } showPage(_currentPageIndex); } void ReadableEditorDialog::useOneSidedEditing() { if (_xData->getPageLayout() != XData::OneSided) { toggleLayout(); } } void ReadableEditorDialog::useTwoSidedEditing() { if (_xData->getPageLayout() != XData::TwoSided) { toggleLayout(); } } void ReadableEditorDialog::toggleLayout() { // Convert OneSided XData to TwoSided and vice versa and refresh controls storeXData(); _xData->togglePageLayout(_xData); populateControlsFromXData(); } void ReadableEditorDialog::checkGuiLayout() { _runningGuiLayoutCheck = true; std::string guiName = _guiEntry->GetValue().ToStdString(); std::string msg; gui::GuiType type = GlobalGuiManager().getGuiType(guiName); switch (type) { case gui::NO_READABLE: msg = _("The specified gui definition is not a readable."); break; case gui::ONE_SIDED_READABLE: if (_xData->getPageLayout() != XData::OneSided) { msg = _("The specified gui definition is not suitable for the currently chosen page-layout."); } else { _runningGuiLayoutCheck = false; updateGuiView(); return; } break; case gui::TWO_SIDED_READABLE: if (_xData->getPageLayout() != XData::TwoSided) { msg = _("The specified gui definition is not suitable for the currently chosen page-layout."); } else { _runningGuiLayoutCheck = false; updateGuiView(); return; } break; case gui::IMPORT_FAILURE: msg = _("Failure during import."); break; case gui::FILE_NOT_FOUND: msg = _("The specified Definition does not exist."); break; default: rWarning() << "Invalid GUI type encountered in switch: " << type << std::endl; break; } wxutil::Messagebox dialog( _("Not a suitable Gui Definition!"), msg + "\n\n" + std::string(_("Start the Gui Browser?")), IDialog::MESSAGE_ASK, this); if (dialog.run() == ui::IDialog::RESULT_YES) { XData::PageLayout layoutBefore = _xData->getPageLayout(); std::string selectedGuiName = GuiSelector::Run(_xData->getPageLayout() == XData::TwoSided, this); if (!selectedGuiName.empty()) { _guiEntry->SetValue(selectedGuiName); _runningGuiLayoutCheck = false; updateGuiView(); return; } else { if (_xData->getPageLayout() != layoutBefore) { toggleLayout(); } // User clicked cancel. Use default layout: if (_xData->getPageLayout() == XData::TwoSided) { _guiEntry->SetValue(XData::DEFAULT_TWOSIDED_GUI); } else { _guiEntry->SetValue(XData::DEFAULT_ONESIDED_GUI); } updateGuiView(); wxutil::Messagebox::Show(_("Switching to default Gui..."), _("You didn't choose a Gui. Using the default Gui now."), ui::IDialog::MESSAGE_CONFIRM, this); _runningGuiLayoutCheck = false; return; } } // Entry should hold focus. _guiEntry->SetFocus(); _runningGuiLayoutCheck = false; } void ReadableEditorDialog::showXdImportSummary() { XData::StringList summary = _xdLoader->getImportSummary(); if (summary.empty()) { wxutil::Messagebox::ShowError(_("No import summary available. An XData definition has to be imported first..."), this); return; } std::string sum; for (std::size_t n = 0; n < summary.size(); n++) { sum += summary[n]; } TextViewInfoDialog::Show(_("XData import summary"), sum, this); } void ReadableEditorDialog::showGuiImportSummary() { XData::StringList errors = GlobalGuiManager().getErrorList(); if (errors.empty()) { wxutil::Messagebox::ShowError(_("No import summary available. Browse Gui Definitions first."), this); return; } std::string summary; for (std::size_t n = 0; n < errors.size(); n++) { summary += errors[n]; } TextViewInfoDialog::Show(_("Gui import summary"), summary, this); } ////////////////////////////////////////////////////////////////////////////// // Callback Methods for Signals: void ReadableEditorDialog::onMenuItemClick(wxCommandEvent& ev) { switch (static_cast(ev.GetId())) { case InsertWholePage: insertPage(); break; case InsertLeft: insertSide(false); break; case InsertRight: insertSide(true); break; case DeleteWholePage: deletePage(); break; case DeleteLeft: deleteSide(false); break; case DeleteRight: deleteSide(true); break; case AppendPage: _numPages->SetValue(static_cast(_xData->getNumPages() + 1)); handleNumberOfPagesChanged(); storeCurrentPage(); showPage(_currentPageIndex + 1); break; case PrependPage: insertPage(); break; case ShowXDataSummary: showXdImportSummary(); break; case ShowGuiImportSummary: showGuiImportSummary(); break; case ShowDuplicatedDefs: showDuplicateDefinitions(); break; }; } void ReadableEditorDialog::onCancel(wxCommandEvent& ev) { EndModal(wxID_CANCEL); } void ReadableEditorDialog::onSave(wxCommandEvent& ev) { if (_xdNameSpecified) { save(); } else { wxutil::Messagebox::ShowError(_("Please specify an XData name first!"), this); } } void ReadableEditorDialog::onSaveClose(wxCommandEvent& ev) { if (!_saveInProgress) { if (_xdNameSpecified) { if (save()) { // Done, just destroy the window EndModal(wxID_OK); } } else { wxutil::Messagebox::ShowError(_("Please specify an XData name first!"), this); } } } void ReadableEditorDialog::onBrowseXd(wxCommandEvent& ev) { // FileChooser for xd-files _xdLoader->retrieveXdInfo(); std::string res = XDataSelector::run(_xdLoader->getDefinitionList(), this); if (res.empty()) { updateGuiView(); return; } // Import the file: try { if (XdFileChooserDialog::Import(res, _xData, _xdFilename, _xdLoader, this) == wxID_OK) { _xdNameSpecified = true; _useDefaultFilename = false; populateControlsFromXData(); refreshWindowTitle(); } else { updateGuiView(); } } catch (XdFileChooserDialog::ImportFailedException&) { std::string msg = fmt::format(_("Failed to import {0}."), res); msg += "\n\n"; msg += _("Do you want to open the import summary?"); wxutil::Messagebox dialog(_("Import failed"), msg, ui::IDialog::MESSAGE_ASK, this); if (dialog.run() == ui::IDialog::RESULT_YES) { showXdImportSummary(); } updateGuiView(); return; } } void ReadableEditorDialog::onNextPage(wxCommandEvent& ev) { if (_currentPageIndex+1 < _xData->getNumPages()) { storeCurrentPage(); showPage(_currentPageIndex+1); } else { static_cast(ev.GetEventObject())->PopupMenu(_appendMenu.get()); } } void ReadableEditorDialog::onPrevPage(wxCommandEvent& ev) { if (_currentPageIndex > 0) { storeCurrentPage(); showPage(_currentPageIndex-1); } else { static_cast(ev.GetEventObject())->PopupMenu(_prependMenu.get()); } } void ReadableEditorDialog::onFirstPage(wxCommandEvent& ev) { if (_currentPageIndex != 0) { storeCurrentPage(); showPage(0); } else { static_cast(ev.GetEventObject())->PopupMenu(_prependMenu.get()); } } void ReadableEditorDialog::onLastPage(wxCommandEvent& ev) { if (_currentPageIndex != _xData->getNumPages() - 1) { storeCurrentPage(); showPage(_xData->getNumPages()-1); } else { static_cast(ev.GetEventObject())->PopupMenu(_appendMenu.get()); } } void ReadableEditorDialog::onBrowseGui(wxCommandEvent& ev) { XData::PageLayout layoutBefore = _xData->getPageLayout(); std::string guiDefBefore = _guiEntry->GetValue().ToStdString(); std::string guiName = GuiSelector::Run(_xData->getPageLayout() == XData::TwoSided, this); if (!guiName.empty()) { _guiEntry->SetValue(guiName); } else { if (_xData->getPageLayout() != layoutBefore) { toggleLayout(); } if (_guiEntry->GetValue() != guiDefBefore) { _guiEntry->SetValue(guiDefBefore); } updateGuiView(); } } void ReadableEditorDialog::onInsert(wxCommandEvent& ev) { if (_xData->getPageLayout() == XData::TwoSided) { static_cast(ev.GetEventObject())->PopupMenu(_insertMenu.get()); } else { insertPage(); } } void ReadableEditorDialog::onDelete(wxCommandEvent& ev) { if (_xData->getPageLayout() == XData::TwoSided) { static_cast(ev.GetEventObject())->PopupMenu(_deleteMenu.get()); } else { deletePage(); } } void ReadableEditorDialog::handleNumberOfPagesChanged() { std::size_t nNP = static_cast(_numPages->GetValue()); _xData->setNumPages(nNP); if (_currentPageIndex >= nNP) { showPage(nNP - 1); } } void ReadableEditorDialog::onNumPagesChanged(wxSpinEvent& ev) { handleNumberOfPagesChanged(); } void ReadableEditorDialog::onToolsClicked(wxCommandEvent& ev) { static_cast(ev.GetEventObject())->PopupMenu(_toolsMenu.get()); } void ReadableEditorDialog::showDuplicateDefinitions() { _xdLoader->retrieveXdInfo(); XData::StringVectorMap dupDefs; try { dupDefs = _xdLoader->getDuplicateDefinitions(); } catch (std::runtime_error&) { wxutil::Messagebox::Show( _("Duplicated XData definitions"), _("There are no duplicated definitions!"), ui::IDialog::MESSAGE_CONFIRM, this ); return; } std::string out; for (XData::StringVectorMap::iterator it = dupDefs.begin(); it != dupDefs.end(); ++it) { std::string occ; for (std::size_t n = 0; n < it->second.size()-1; n++) { occ += it->second[n] + ", "; } occ += it->second[it->second.size() - 1]; out += fmt::format(_("{0} has been defined in:"), it->first); out +="\n\t"; out += occ; out += ".\n\n"; } TextViewInfoDialog::Show(_("Duplicated XData definitions"), out, this); } void ReadableEditorDialog::onOneSided(wxCommandEvent& ev) { if ((_oneSidedButton->GetValue())) useOneSidedEditing(); } void ReadableEditorDialog::onTwoSided(wxCommandEvent& ev) { if ((_twoSidedButton->GetValue())) useTwoSidedEditing(); } ////////////////////////////////////////////////////////////////////////////// // Callback Methods for Events: void ReadableEditorDialog::onFocusOut(wxFocusEvent& ev) { if (ev.GetEventObject() == _xDataNameEntry) { // Only call checkXDataUniqueness if the method is not running yet. if (!_runningXDataUniquenessCheck) { checkXDataUniqueness(); } } else // WIDGET_GUI_ENTRY { // Only call checkGuiLayout() if the method is not yet running if (!_runningGuiLayoutCheck) { checkGuiLayout(); } } ev.Skip(); } void ReadableEditorDialog::onTextChanged(wxCommandEvent& ev) { // Update the preview updateGuiView(); } void ReadableEditorDialog::onChar(wxKeyEvent& ev) { if (ev.GetEventObject() == _xDataNameEntry) { switch (ev.GetKeyCode()) { // Some forbidden keys case WXK_SPACE: case '!': case '*': case '+': case WXK_NUMPAD_MULTIPLY: case WXK_NUMPAD_SUBTRACT: case WXK_NUMPAD_ADD: case WXK_NUMPAD_SEPARATOR: case ',': case '.': case ':': case ';': case '?': case '-': return; // Since TAB would be added to the value, inercept it here and // emulate TAB-traversal to the next control instead case WXK_TAB: if (ev.ShiftDown()) { _nameEntry->SetFocus(); } else { _numPages->SetFocus(); } return; // Check Uniqueness of the XData-Name. case WXK_RETURN: case WXK_NUMPAD_ENTER: checkXDataUniqueness(); ev.Skip(); break; default: ev.Skip(); break; } } else if (ev.GetEventObject() == _nameEntry) { switch (ev.GetKeyCode()) { // Forbidden key check case WXK_TAB: _xDataNameEntry->SetFocus(); return; // forbid the tab default: ev.Skip(); return; } } else if (ev.GetEventObject() == _numPages) { if (ev.GetKeyCode() != WXK_ESCAPE) { ev.Skip(); return; } // Restore the old value. _numPages->SetValue(static_cast(_xData->getNumPages())); return; } else if (ev.GetEventObject() == _guiEntry) { if (ev.GetKeyCode() != WXK_RETURN && ev.GetKeyCode() != WXK_NUMPAD_ENTER) { ev.Skip(); return; } checkGuiLayout(); return; } ev.Skip(); } // Sets the text of a TextView identified by its widget enumerator and scrolls it to the end. void ReadableEditorDialog::setTextViewAndScroll(wxTextCtrl* view, const std::string& text) { view->SetValue(text); view->ShowPosition(view->GetLastPosition()); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.gui/ReadableEditorDialog.h000066400000000000000000000142051321750546400227160ustar00rootroot00000000000000#pragma once #include "icommandsystem.h" #include "wxutil/dialog/DialogBase.h" #include "ReadableGuiView.h" #include #include #include "XDataLoader.h" #include "string/string.h" #include "wxutil/XmlResourceBasedWidget.h" class Entity; class wxTextCtrl; class wxSpinCtrl; class wxRadioButton; class wxStaticText; namespace ui { namespace { const std::string RKEY_READABLES_STORAGE_FOLDER = "user/ui/gui/storageFolder"; const std::string RKEY_READABLES_CUSTOM_FOLDER = "user/ui/gui/customFolder"; } ///////////////////////////// ReadableEditorDialog: // The main dialog of the Readable Editor, which implements most editing features. class ReadableEditorDialog : public wxutil::DialogBase, private wxutil::XmlResourceBasedWidget { private: gui::ReadableGuiView* _guiView; // The entity we're working with Entity* _entity; // The XData-definition of the entity XData::XDataPtr _xData; // The filename of the XData definition std::string _xdFilename; // The XData filename derived from the map-name. std::string _mapBasedFilename; // The xData loader XData::XDataLoaderPtr _xdLoader; // The index of the current page. std::size_t _currentPageIndex; // Has the XData name been specified? bool _xdNameSpecified; // Tells the program whether checkGuiLayout() is already running. bool _runningGuiLayoutCheck; // Tells the program whether checkXDataUniqueness is already running. bool _runningXDataUniquenessCheck; // Tells the exporter whether to use the _mapBasedFilename (true) or not. bool _useDefaultFilename; // Prevents saving races. bool _saveInProgress; wxTextCtrl* _nameEntry; wxTextCtrl* _xDataNameEntry; wxSpinCtrl* _numPages; wxRadioButton* _oneSidedButton; wxRadioButton* _twoSidedButton; wxTextCtrl* _pageTurnEntry; wxStaticText* _curPageDisplay; wxTextCtrl* _guiEntry; wxStaticText* _pageLeftLabel; wxStaticText* _pageRightLabel; wxTextCtrl* _textViewTitle; wxTextCtrl* _textViewRightTitle; wxTextCtrl* _textViewBody; wxTextCtrl* _textViewRightBody; std::unique_ptr _insertMenu; std::unique_ptr _deleteMenu; std::unique_ptr _appendMenu; std::unique_ptr _prependMenu; std::unique_ptr _toolsMenu; public: // Pass the working entity to the constructor ReadableEditorDialog(Entity* entity); static void RunDialog(const cmd::ArgumentList& args); // override DialogBase int ShowModal(); // Switch between the editing modes void useOneSidedEditing(); void useTwoSidedEditing(); // Updates the GUI preview (text updates). Public so that XDataSelector and GuiSelector can access it. // Uses the current data in the readable editor for updating or imports XData/guis by the passed strings. // This Method can create error-messages. For that reason a parent window can be specified. If Null the Readable // Editor Dialog is parent. void updateGuiView(wxWindow* parent = NULL, const std::string& guiPath = "", const std::string& xDataName = "", const std::string& xDataPath = ""); // Sets some GUI state variables needed for rendering and sets the time for script execution void initGuiState(const gui::IGuiPtr& gui); // shows the XData import Summary void showXdImportSummary(); private: // Save all settings on the entity and exports xdata. bool save(); // Constructs the storage path for the current XData definition. Used by save-method and for the window-title. std::string constructStoragePath(); // Refreshes the window title based on the storagepath. void refreshWindowTitle(); // Retrieves information from the entity and imports xdata. If the user cancels, the window is destroyed in _postshow bool initControlsFromEntity(); // _show_ TwoSided editing-interface. void toggleTwoSidedEditingInterface(bool show); // shows the gui import Summary void showGuiImportSummary(); // toggles the layout of the xdata object and updates the interface. void toggleLayout(); // Stores the contents of the current page inside the xData-object. void storeCurrentPage(); // Stores the contents of all controls in the xData-object. void storeXData(); // Updates the page related inputs and the preview renderer. Also adds default guis. // Warning: Contents are overwritten. storeCurrentPage() should be called beforehand. void showPage(std::size_t pageIndex); // Populates the controls with the information in the xdata object. Adds a default snd_page_turn, if not defined. void populateControlsFromXData(); // Checks whether the chosen XData name already exists. If it does a popup will ask the user whether it should be imported. void checkXDataUniqueness(); // Checks whether the specified gui definition matches the pagelayout. Returns FALSE on success. void checkGuiLayout(); void handleNumberOfPagesChanged(); // Deleting and inserting of pages. void insertPage(); void deletePage(); // Inserts a page on the right or left side of the current page. void insertSide(bool rightSide); // Deletes the left or right side of the current page. void deleteSide(bool rightSide); // Ui Creation: void setupGeneralPropertiesInterface(); void setupPageRelatedInterface(); void setupButtonPanel(); void createMenus(); // Callback methods for Signals: void onCancel(wxCommandEvent& ev); void onSave(wxCommandEvent& ev); void onSaveClose(wxCommandEvent& ev); void onBrowseXd(wxCommandEvent& ev); void onBrowseGui(wxCommandEvent& ev); void onFirstPage(wxCommandEvent& ev); void onPrevPage(wxCommandEvent& ev); void onNextPage(wxCommandEvent& ev); void onLastPage(wxCommandEvent& ev); void onInsert(wxCommandEvent& ev); void onDelete(wxCommandEvent& ev); void onNumPagesChanged(wxSpinEvent& ev); void onToolsClicked(wxCommandEvent& ev); void onMenuItemClick(wxCommandEvent& ev); void onOneSided(wxCommandEvent& ev); void onTwoSided(wxCommandEvent& ev); // Callback methods for Events: void onFocusOut(wxFocusEvent& ev); void onChar(wxKeyEvent& ev); void onTextChanged(wxCommandEvent& ev); // Helper Methods: void showDuplicateDefinitions(); // Sets the text of a TextView identified by its widget enumerator and scrolls it to the end. void setTextViewAndScroll(wxTextCtrl* view, const std::string& text); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.gui/ReadableGuiView.cpp000066400000000000000000000024151321750546400222620ustar00rootroot00000000000000#include "ReadableGuiView.h" #include "math/Vector4.h" #include "gui/GuiWindowDef.h" namespace gui { ReadableGuiView::ReadableGuiView(wxWindow* parent) : GuiView(parent) {} void ReadableGuiView::setGLViewPort() { double width = _windowDims[0]; double height = _windowDims[1]; double aspectRatio = _bgDims[0] / _bgDims[1]; if (width / height > aspectRatio) { width = height * aspectRatio; } else { height = width / aspectRatio; } SetSize(static_cast(width), -1); glViewport(0, 0, static_cast(width), static_cast(height)); } void ReadableGuiView::setGui(const IGuiPtr& gui) { // Call the base class first GuiView::setGui(gui); Vector2 topLeft(0, 0); Vector2 bottomRight(640, 480); if (_gui != NULL) { IGuiWindowDefPtr bgWindowDef = _gui->findWindowDef("backgroundImage"); if (!bgWindowDef) { bgWindowDef = _gui->findWindowDef("backgroundmulti"); if (!bgWindowDef) { bgWindowDef = _gui->findWindowDef("backgroundsingle"); } } if (bgWindowDef != NULL) { const Vector4& rect = bgWindowDef->rect; topLeft = Vector2(rect[0], rect[1]); bottomRight = Vector2(rect[0] + rect[2], rect[1] + rect[3]); } } _bgDims = bottomRight - topLeft; _renderer.setVisibleArea(topLeft, bottomRight); } } // namespace DarkRadiant-2.5.0/plugins/dm.gui/ReadableGuiView.h000066400000000000000000000007761321750546400217370ustar00rootroot00000000000000#pragma once #include "wxutil/preview/GuiView.h" namespace gui { /** * greebo: Specialisation of the generic GuiView with regard to readables. * The viewport is cropped to the size of the "backgroundImage" windowDef. */ class ReadableGuiView : public wxutil::GuiView { protected: Vector2 _bgDims; std::vector backgroundDefList; public: ReadableGuiView(wxWindow* parent); virtual void setGui(const IGuiPtr& gui) override; protected: virtual void setGLViewPort(); }; } // namespace DarkRadiant-2.5.0/plugins/dm.gui/ReadablePopulator.h000066400000000000000000000030751321750546400223400ustar00rootroot00000000000000#pragma once #include "gui/GuiManager.h" #include "wxutil/VFSTreePopulator.h" #include "wxutil/ModalProgressDialog.h" #include "EventRateLimiter.h" #include "i18n.h" namespace ui { /** * greebo: A helper class sorting GUIs into two given TreePopulators. */ class ReadablePopulator : public gui::GuiManager::Visitor { private: wxutil::VFSTreePopulator& _popOne; wxutil::VFSTreePopulator& _popTwo; // Progress dialog and model count wxutil::ModalProgressDialog _progress; std::size_t _count; std::size_t _numGuis; // Event rate limiter for progress dialog EventRateLimiter _evLimiter; public: ReadablePopulator(wxutil::VFSTreePopulator& popOne, wxutil::VFSTreePopulator& popTwo) : _popOne(popOne), _popTwo(popTwo), _progress(_("Analysing Guis")), _count(0), _numGuis(GlobalGuiManager().getNumGuis()), _evLimiter(50) {} void visit(const std::string& guiPath, const gui::GuiType& guiType) { _count++; if (_evLimiter.readyForEvent()) { float fraction = static_cast(_count) / _numGuis; _progress.setTextAndFraction(guiPath.substr(guiPath.rfind('/') + 1), fraction); } gui::GuiType type; if (guiType == gui::NOT_LOADED_YET || guiType == gui::UNDETERMINED) { type = GlobalGuiManager().getGuiType(guiPath); } else { type = guiType; } if (type == gui::ONE_SIDED_READABLE) { _popOne.addPath(guiPath.substr(guiPath.find('/') + 1)); // omit the guis-folder } else if (type == gui::TWO_SIDED_READABLE) { _popTwo.addPath(guiPath.substr(guiPath.find('/') + 1)); // omit the guis-folder } } }; } // namespace DarkRadiant-2.5.0/plugins/dm.gui/ReadableReloader.h000066400000000000000000000024421321750546400221050ustar00rootroot00000000000000#pragma once #include "gui/GuiManager.h" #include "wxutil/VFSTreePopulator.h" #include "wxutil/ModalProgressDialog.h" #include "EventRateLimiter.h" #include "imainframe.h" #include "i18n.h" namespace ui { /** * greebo: A helper class reloading all GUI definitions. */ class ReadableReloader : public gui::GuiManager::Visitor { private: // Progress dialog and model count wxutil::ModalProgressDialog _progress; std::size_t _count; std::size_t _numGuis; // Event rate limiter for progress dialog EventRateLimiter _evLimiter; public: ReadableReloader() : _progress(_("Reloading Guis")), _count(0), _evLimiter(50) { _numGuis = GlobalGuiManager().getNumGuis(); } void visit(const std::string& guiPath, const gui::GuiType& guiType) { _count++; if (_evLimiter.readyForEvent()) { float fraction = static_cast(_count) / _numGuis; _progress.setTextAndFraction(guiPath.substr(guiPath.rfind('/') + 1), fraction); } if (guiType != gui::NOT_LOADED_YET) { GlobalGuiManager().reloadGui(guiPath); } } static void run(const cmd::ArgumentList& args) { try { GlobalGuiManager().reloadGuis(); ReadableReloader reloader; GlobalGuiManager().foreachGui(reloader); } catch (wxutil::ModalProgressDialog::OperationAbortedException&) {} } }; } // namespace DarkRadiant-2.5.0/plugins/dm.gui/TextViewInfoDialog.h000066400000000000000000000024641321750546400224470ustar00rootroot00000000000000#pragma once #include "wxutil/dialog/DialogBase.h" #include #include namespace ui { ///////////////////////////// TextViewInfoDialog: // Small Info-Dialog showing text in a scrolled, non-editable textview and an ok button. class TextViewInfoDialog : public wxutil::DialogBase { private: wxTextCtrl* _text; public: TextViewInfoDialog(const std::string& title, const std::string& text, wxWindow* parent = NULL, int win_width = 650, int win_height = 500) : DialogBase(title), _text(new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_LEFT | wxTE_MULTILINE | wxTE_READONLY | wxTE_WORDWRAP)) { SetSize(win_width, win_height); // Add a vbox for the dialog elements SetSizer(new wxBoxSizer(wxVERTICAL)); // Add a vbox for the dialog elements wxBoxSizer* vbox = new wxBoxSizer(wxVERTICAL); GetSizer()->Add(vbox, 1, wxEXPAND | wxALL, 12); vbox->Add(_text, 1, wxEXPAND | wxBOTTOM, 6); vbox->Add(CreateStdDialogButtonSizer(wxOK), 0, wxALIGN_RIGHT); _text->SetValue(text); CenterOnParent(); } static void Show(const std::string& title, const std::string& text, wxWindow* parent = NULL) { TextViewInfoDialog* dialog = new TextViewInfoDialog(title, text, parent); dialog->ShowModal(); dialog->Destroy(); } }; } // namespace DarkRadiant-2.5.0/plugins/dm.gui/XData.cpp000066400000000000000000000302101321750546400202560ustar00rootroot00000000000000#include "XData.h" #include "i18n.h" #include "itextstream.h" #include #include "os/fs.h" #include "os/file.h" namespace XData { //XData implementations: //->export: FileStatus XData::xport( const std::string& filename, ExporterCommand cmd ) { fs::path Path(filename); fs::path parent = Path.parent_path(); // Ensure the parent path exists fs::create_directories(parent); if (os::fileOrDirExists(Path.string())) { switch (cmd) { case Merge: { //Check if definition already exists and return DefinitionExists. If it does not, append the definition to the file. std::fstream file(Path.string(), std::ios_base::in | std::ios_base::out | std::ios_base::app); if (!file.is_open()) return OpenFailed; std::stringstream ss; ss << file.rdbuf(); std::string String = ss.str(); std::size_t DefPos = String.find(_name); while (DefPos != std::string::npos) //A name of a XData could be contained in another XData's name. Check that... { char before = String[DefPos > 0 ? DefPos-1 : 0]; char after = String[DefPos+_name.length() < String.size() ? DefPos+_name.length() : 0]; if ((DefPos == 0 || before == ' ' || before == '\t' || before == '\n') && (DefPos+_name.length() == String.size() || after == ' ' || after == '\t' || after == '\n')) //other delimiters necessary? { _definitionStart = 0; //Definition found. But we want to have it clean, so let's see how many spaces are before the definition begin. while (DefPos > 0) { if (String[--DefPos] != ' ' && String[DefPos] != '\t' && String[DefPos] != '\n') { _definitionStart = DefPos+1; break; } } file.close(); return DefinitionExists; } DefPos = String.find(_name,DefPos+_name.length()); } file << "\n\n\n" << generateXDataDef(); file.close(); return AllOk; } case MergeOverwriteExisting: { //Find the old definition in the target file and delete it. Append the new definition. //_definitionStart has been set in the first iteration of this method. std::fstream file(Path.string(), std::ios_base::in); if (!file.is_open()) return OpenFailed; std::stringstream ss; ss << file.rdbuf(); std::string OutString = ss.str(); file.close(); std::size_t DefLength = getDefLength(OutString.substr(_definitionStart)); if (DefLength == 0) //If the definitionlength can't be obtained, the merge fails. return MergeFailed; OutString.erase(_definitionStart, DefLength); std::string insString = std::string( (_definitionStart != 0) ? "\n\n\n" : "" ) //No new lines in the first line + generateXDataDef() + std::string( (_definitionStart == OutString.size()) ? "" : "\n\n\n" ); //No new lines at the end of file OutString.insert(_definitionStart, insString); file.open(Path.string(), std::ios_base::out | std::ios_base::trunc); if (!file.is_open()) return OpenFailed; file << OutString; file.close(); return AllOk; } case Overwrite: { //Warn if the target file contains multiple definitions: return MultipleDefinitions. //Warn if the definition in the target file does not match the current definition: return DefinitionMisMatch //else overwrite existing file. std::string DefName; std::ifstream file(Path.string(), std::ios_base::in); if (!file.is_open()) return OpenFailed; try { DefName = getDefinitionNameFromXD(file); } catch (...) { rConsoleError() << "[XData::xport] Syntax error in file " << filename << ". Overwriting the file..." << std::endl; break; } if (DefName == _name) //Definition will be overwritten... break; else if (DefName == "") //MultipleDefinitions exist return MultipleDefinitions; return DefinitionMismatch; } case OverwriteMultDef: //Replace the file no matter what. break; default: return FileExists; } } //Write the definition into the file. std::ofstream file(Path.string(), std::ios_base::out | std::ios_base::trunc); if (!file.is_open()) return OpenFailed; file << generateXDataDef(); file.close(); return AllOk; } const std::string XData::generateXDataDef() const { std::stringstream xDataDef; xDataDef << _name << "\n" << "{" << "\n" << "\tprecache" << "\n" << "\t\"num_pages\"\t: \"" << _numPages << "\"\n"; std::stringstream ss; std::string TempString; xDataDef << getContentDef(); for (std::size_t n=0; n<_numPages; n++) { xDataDef << "\t\"gui_page" << n+1 << "\"\t: \"" << _guiPage[n] << "\"\n"; } xDataDef << "\t\"snd_page_turn\"\t: \"" << _sndPageTurn << "\"\n}";//*/ return xDataDef.str(); } const std::size_t XData::getDefLength(const std::string& def) const { std::size_t charIndex = 0; while (def[charIndex] != '\0') { if (def[++charIndex] == '{') { int BracketCount = 1; while (def[++charIndex] != '\0' && BracketCount > 0) { // Move out of brackets if (def[charIndex] == '{') ++BracketCount; else if (def[charIndex] == '}') --BracketCount; } if (BracketCount > 0) break; while (def[charIndex] != '\0') { // The definitionlength also expands over trailing spaces if ( (def[charIndex] != ' ') && (def[charIndex] != '\t') && (def[charIndex] != '\n') ) break; ++charIndex; } return charIndex; } } return 0; //no appropriate bracketstructure was found. } const std::string XData::getDefinitionNameFromXD(std::ifstream& file) const { std::string ReturnString; parser::BasicDefTokeniser tok(file); bool FirstDef = true; while (tok.hasMoreTokens()) { if (FirstDef) ReturnString = tok.nextToken(); else return ""; FirstDef = false; tok.assertNextToken("{"); jumpOutOfBrackets(tok, 1); } return ReturnString; } const std::string XData::generateTextDef(const std::string& rawString) const { std::stringstream ss; std::stringstream xDataDef; std::string TempString; xDataDef << "\t{\n"; if (rawString != "") { ss << rawString; while ( std::getline(ss, TempString) ) //replace "\n" and add an escape-character before quotes. { std::size_t QuotePos = TempString.find("\"",0); while ( QuotePos != std::string::npos ) { TempString.insert(QuotePos, "\\"); QuotePos = TempString.find("\"",QuotePos+2); } xDataDef << "\t\t\"" << TempString << "\"\n"; } xDataDef << "\t}\n"; } else xDataDef << "\t\t\"\"\n\t}\n"; return xDataDef.str(); } //->general: void XData::resizeVectors(std::size_t targetSize) { std::string fill = ""; if (_guiPage.size() > 0) fill = _guiPage[_guiPage.size()-1]; _guiPage.resize(targetSize, fill); } void XData::jumpOutOfBrackets(parser::DefTokeniser& tok, int currentDepth) const { while ( tok.hasMoreTokens() && currentDepth > 0) { std::string token = tok.nextToken(); if (token == "{") currentDepth += 1; else if (token == "}") currentDepth -= 1; } } //TwoSidedXData implementations: void TwoSidedXData::resizeVectors(std::size_t targetSize) { XData::resizeVectors(targetSize); _pageLeftBody.resize(targetSize, ""); _pageLeftTitle.resize(targetSize, ""); _pageRightBody.resize(targetSize, ""); _pageRightTitle.resize(targetSize, ""); } void TwoSidedXData::setPageContent(ContentType cc, std::size_t pageIndex, Side side, const std::string& content) { if (pageIndex >= _numPages) throw std::runtime_error(_("Page Index out of bounds.")); switch (cc) { case Title: switch (side) { case Left: _pageLeftTitle[pageIndex] = content; break; default: _pageRightTitle[pageIndex] = content; } break; default: switch (side) { case Left: _pageLeftBody[pageIndex] = content; break; default: _pageRightBody[pageIndex] = content; } } } const std::string& TwoSidedXData::getPageContent(ContentType cc, std::size_t pageIndex, Side side) const { if (pageIndex >= _numPages) throw std::runtime_error(_("Page Index out of bounds.")); switch (cc) { case Title: switch (side) { case Left: return _pageLeftTitle[pageIndex]; default: return _pageRightTitle[pageIndex]; } default: switch (side) { case Left: return _pageLeftBody[pageIndex]; default: return _pageRightBody[pageIndex]; } } } const std::string TwoSidedXData::getContentDef() const { std::stringstream xDataDef; for (std::size_t n = 0; n < _numPages; n++) { //Page Left Title xDataDef << "\t\"page" << n+1 << "_left_title\"\t:\n"; xDataDef << generateTextDef(_pageLeftTitle[n]); //Page Left Body xDataDef << "\t\"page" << n+1 << "_left_body\"\t:\n"; xDataDef << generateTextDef(_pageLeftBody[n]); //Page Right Title xDataDef << "\t\"page" << n+1 << "_right_title\"\t:\n"; xDataDef << generateTextDef(_pageRightTitle[n]); //Page Right Body xDataDef << "\t\"page" << n+1 << "_right_body\"\t:\n"; xDataDef << generateTextDef(_pageRightBody[n]); } return xDataDef.str(); } void TwoSidedXData::togglePageLayout(XDataPtr& target) const { XDataPtr newXData(new OneSidedXData(_name)); newXData->setNumPages(_numPages*2); //delete last page if no content later. newXData->setSndPageTurn(_sndPageTurn); // Add default guiPage to all guiPage-entries. newXData->setGuiPage( StringList( newXData->getNumPages(), DEFAULT_ONESIDED_GUI) ); // Reshuffle the TwoSided pages contents into the OneSided page contents. for (std::size_t n = 0; n < _numPages; n++) { newXData->setPageContent(Title, 2*n, Left, _pageLeftTitle[n] ); newXData->setPageContent(Body, 2*n, Left, _pageLeftBody[n] ); newXData->setPageContent(Title, 2*n+1, Left, _pageRightTitle[n] ); newXData->setPageContent(Body, 2*n+1, Left, _pageRightBody[n] ); } if ( (_pageRightTitle[_numPages-1] == "") && (_pageRightBody[_numPages-1] == "") ) // Last page is empty. Set new numpages accordingly. newXData->setNumPages(newXData->getNumPages()-1); target = newXData; } //OneSidedXData implementations: void OneSidedXData::resizeVectors(std::size_t targetSize) { XData::resizeVectors(targetSize); _pageBody.resize(targetSize, ""); _pageTitle.resize(targetSize, ""); } void OneSidedXData::setPageContent(ContentType cc, std::size_t pageIndex, Side side, const std::string& content) { if (pageIndex >= _numPages) throw std::runtime_error(_("Page Index out of bounds.")); switch (cc) { case Title: _pageTitle[pageIndex] = content; break; case Body: default: _pageBody[pageIndex] = content; } } const std::string& OneSidedXData::getPageContent(ContentType cc, std::size_t pageIndex, Side side) const { if (pageIndex >= _numPages) throw std::runtime_error(_("Page Index out of bounds.")); switch (cc) { case Title: return _pageTitle[pageIndex]; default: return _pageBody[pageIndex]; } } const std::string OneSidedXData::getContentDef() const { std::stringstream xDataDef; for (std::size_t n = 0; n < _numPages; n++) { //Title xDataDef << "\t\"page" << n+1 << "_title\"\t:\n"; xDataDef << generateTextDef(_pageTitle[n]); //Body xDataDef << "\t\"page" << n+1 << "_body\"\t:\n"; xDataDef << generateTextDef(_pageBody[n]); } return xDataDef.str(); } void OneSidedXData::togglePageLayout(XDataPtr& target) const { XDataPtr newXData(new TwoSidedXData(_name)); newXData->setNumPages( (_numPages+1)/2); newXData->setSndPageTurn(_sndPageTurn); // Add default guiPage to all guiPage-entries. newXData->setGuiPage( StringList( newXData->getNumPages(), DEFAULT_TWOSIDED_GUI) ); // Reshuffle the TwoSided pages contents into the OneSided page contents. for (std::size_t n = 0; n < newXData->getNumPages()-1; n++) { newXData->setPageContent(Title, n, Left, _pageTitle[2*n] ); newXData->setPageContent(Body, n, Left, _pageBody[2*n] ); newXData->setPageContent(Title, n, Right, _pageTitle[2*n+1] ); newXData->setPageContent(Body, n, Right, _pageBody[2*n+1] ); } newXData->setPageContent(Title, newXData->getNumPages()-1, Left, _pageTitle[2*(newXData->getNumPages()-1)] ); newXData->setPageContent(Body, newXData->getNumPages()-1, Left, _pageBody[2*(newXData->getNumPages()-1)] ); if ( (_numPages % 2) == 0) { // Prevent vector subscript exceeded. newXData->setPageContent(Title, newXData->getNumPages()-1, Right, _pageTitle[_numPages-1] ); newXData->setPageContent(Body, newXData->getNumPages()-1, Right, _pageBody[_numPages-1] ); } target = newXData; } } // namespace XData DarkRadiant-2.5.0/plugins/dm.gui/XData.h000066400000000000000000000170551321750546400177370ustar00rootroot00000000000000#pragma once #include #include #include #include "i18n.h" #include #include "parser/DefTokeniser.h" namespace XData { namespace { // All vectors of XData-objects are initialized with this size so that no sorting is necessary, which // would otherwise be necessary when e.g. page2_body was defined before page1_body and a simple // vector.push_back(..) was used to store the data instead of a direct access using an Index. const std::size_t MAX_PAGE_COUNT = 20; const char* DEFAULT_TWOSIDED_GUI = "guis/readables/books/book_calig_mac_humaine.gui"; const char* DEFAULT_ONESIDED_GUI = "guis/readables/sheets/sheet_paper_hand_nancy.gui"; const char* DEFAULT_SNDPAGETURN = "readable_page_turn"; } typedef std::vector StringList; enum FileStatus { FileExists, DefinitionExists, MultipleDefinitions, DefinitionMismatch, MergeFailed, OpenFailed, AllOk }; enum ExporterCommand { Normal, Merge, MergeOverwriteExisting, Overwrite, OverwriteMultDef }; enum Side { Left, Right }; enum ContentType { Title, Body }; enum PageLayout { TwoSided, OneSided }; class XData; typedef std::shared_ptr XDataPtr; ///////////////////////////// XData: // XData base-class for exporting, storing and managing XData. class XData { public: //Methods: // Returns the PageLayout of the object: TwoSided or OneSided virtual const PageLayout getPageLayout() const = 0; // Exports the XData class formated properly into the File specified in // Filename (absolute Filepath). If the file already exists this function can overwrite the // file or merge. If the file cannot be opened, OpenFailed is returned. // -Merge: If the definition already exists, DefinitionExists is returned (can also return OpenFailed). // This definition can be overwritten using the command MergeOverWriteExisting AFTER using Merge-cmd. // Otherwise the definition is appended. MergeOverwriteExisting can fail and returns MergeFailed or OpenFailed. // -Overwrite: Returns DefinitionMismatch if the definition in the target-file does not match // the name of the current definition or returns MultipleDefinitions. If a DefinitionMatch // is the case, the file is overwritten. Use the command OverwriteMultDef to overwrite the // file no matter what. With both commands OpenFailed can be returned. // If the target-file has Syntax errors, it is overwritten... FileStatus xport(const std::string& filename, ExporterCommand cmd); // Creates a OneSided XData object of an TwoSided XData object and vice versa without discarding any actual page // contents. numPages is adjusted accordingly and default guiPage-statements are applied. The result is stored // in target. virtual void togglePageLayout(XDataPtr& target) const = 0; virtual ~XData() {}; //Getters and Setters for Attributes: // The page-contents of the Two- and OneSided XData-objects. cc defines whether Title or Body shall be accessed. // The Side-parameter is discarded on OneSidedXData-objects. Throws std::runtime_error when index out of bounds. virtual const std::string& getPageContent(ContentType cc, std::size_t pageIndex, Side side) const = 0; virtual void setPageContent(ContentType cc, std::size_t pageIndex, Side side, const std::string& content) = 0; // The name of the XData-Definition const std::string& getName() const { return _name; } void setName(const std::string& name) { _name = name; } // numPages-statement. Resizes vectors accordingly. Attention: If numPages is lowered, data will obviously be discarded. const std::size_t getNumPages() const { return _numPages; } void setNumPages(std::size_t numPages) { _numPages = numPages; resizeVectors(numPages); } // guiPage-statement. Methods for accessing a whole StringList or single VectorElements. const StringList& getGuiPage() const { return _guiPage; } const std::string& getGuiPage(std::size_t index) const { if (index >= _numPages) throw std::runtime_error(_("GUI Page Index out of bounds.")); return _guiPage[index]; } void setGuiPage(const StringList& guiPage) { _guiPage = guiPage; } void setGuiPage(const std::string& guiPage, std::size_t index) { if (index >= _numPages) throw std::runtime_error(_("GUI Page Index out of bounds.")); _guiPage[index] = guiPage; } // sndPageTurn-statement. const std::string& getSndPageTurn() const{ return _sndPageTurn; } void setSndPageTurn(const std::string& sndPageTurn) { _sndPageTurn = sndPageTurn; } private: //Methods for export: // Generates the XData-Definition for the XData-object. const std::string generateXDataDef() const; // Returns the length of the current definition. The get-pointer has to be at the beginning of that definition. Returns 0 on Syntax errors. const std::size_t getDefLength(const std::string& def) const; // Returns the definition-name found in the file or "" if multiple definitions where found. // Used by the xport()-method in the overwrite-command for checking for a DefinitionMatch or MultipleDefinitions. const std::string getDefinitionNameFromXD(std::ifstream& file) const; // Used to jump out of a definition. Can lead to undefined behavior on Syntax-errors. void jumpOutOfBrackets(parser::DefTokeniser& tok, int currentDepth) const; protected: // Resizes all vectors to TargetSize. virtual void resizeVectors(std::size_t targetSize); // Returns the OneSided or TwoSided Page-content-definition of an XData object. virtual const std::string getContentDef() const = 0; // Generates the XData-compatible definition for Multi-line strings and adds escape-characters before quotes. const std::string generateTextDef(const std::string& rawString) const; //Attributes: std::string _name; //name of the XData-Definition std::size_t _numPages; //numPages-statement StringList _guiPage; //guiPage-statements std::string _sndPageTurn; //sndPageTurn-statement std::size_t _definitionStart; //Marks the start of a definition in an .xd-File. Used by the xport()-method. }; class OneSidedXData : public XData { private: //page contents StringList _pageTitle; StringList _pageBody; //end of page contents const std::string getContentDef() const; void resizeVectors(std::size_t targetSize); public: void setPageContent(ContentType cc, std::size_t pageIndex, Side side, const std::string& content); const std::string& getPageContent(ContentType cc, std::size_t pageIndex, Side side) const; const PageLayout getPageLayout() const { return OneSided; } void togglePageLayout(XDataPtr& target) const; OneSidedXData(const std::string& name) { _name = name; setNumPages(MAX_PAGE_COUNT); } ~OneSidedXData() { _guiPage.clear(); _pageTitle.clear(); _pageBody.clear(); } }; typedef std::shared_ptr OneSidedXDataPtr; class TwoSidedXData : public XData { private: //page contents: StringList _pageLeftTitle; StringList _pageRightTitle; StringList _pageLeftBody; StringList _pageRightBody; //end of page contents const std::string getContentDef() const; void resizeVectors(std::size_t targetSize); public: void setPageContent(ContentType cc, std::size_t pageIndex, Side side, const std::string& content); const std::string& getPageContent(ContentType cc, std::size_t pageIndex, Side side) const; const PageLayout getPageLayout() const { return TwoSided; } void togglePageLayout(XDataPtr& target) const; TwoSidedXData(const std::string& name) { _name=name; setNumPages(MAX_PAGE_COUNT); } ~TwoSidedXData() { _pageLeftTitle.clear(); _pageLeftBody.clear(); _pageRightBody.clear(); _pageRightTitle.clear(); _guiPage.clear(); } }; typedef std::shared_ptr TwoSidedXDataPtr; } //namespace XData DarkRadiant-2.5.0/plugins/dm.gui/XDataLoader.cpp000066400000000000000000000604141321750546400214160ustar00rootroot00000000000000#include "XDataLoader.h" #include "string/convert.h" #include "iarchive.h" namespace XData { const bool XDataLoader::importDef( const std::string& definitionName, XDataMap& target, const std::string& filename ) { // Initialization: _errorList.clear(); _newXData.reset(); target.clear(); StringList files; if (filename != "") { //Check fileextension: if (filename.substr( filename.rfind(".")+1 ) != "xd") return reportError("[XDataLoader::import] Error: File-extension is not .xd: " + filename + "\n"); files.push_back(filename); } else { //Find the files in which the definition can be found. retrieveXdInfo(); StringVectorMap::iterator it = _defMap.find(definitionName); if (it == _defMap.end()) return reportError("[XDataLoader::importDef] Error: Specified definition " + definitionName + " not found.\n"); files = it->second; if (files.size() > 1) //Definition contained in multiple files. reportError("[XData::import] Warning: The requested definition " + definitionName + " exists in multiple files.\n"); } // Parse the requested definition from all files: for (std::size_t n = 0; n < files.size(); n++) { // Attempt to open the file in text mode and retrieve DefTokeniser ArchiveTextFilePtr file = GlobalFileSystem().openTextFile(files[n]); if (file == NULL) return reportError("[XDataLoader::importDef] Error: Failed to open file " + files[n] + "\n"); std::istream is(&(file->getInputStream())); parser::BasicDefTokeniser tok(is); // Parse the desired definition: while (tok.hasMoreTokens() && !parseXDataDef(tok,definitionName)) {} if (_newXData) target.insert(XDataMap::value_type(file->getModName() + "/" + file->getName(),_newXData)); else reportError("[XDataLoader::importDef] Error: Failed to load " + definitionName + " from file " + files[n] + ".\n"); } //Summarizing report: if (target.size() == 0) { if (filename == "") reportError("[XDataLoader::importDef] Error: Failed to load " + definitionName + ".\n"); return false; } else { _errorList.push_back("[XDataLoader::importDef] " + definitionName + " loaded successfully with " + string::to_string(_errorList.size()) + " error(s)/warning(s).\n"); //Summary output: if (_errorList.size() > 1) rConsoleError() << _errorList[_errorList.size() - 1]; else rMessage() << _errorList[0]; } return true; } const bool XDataLoader::import( const std::string& filename, XDataMap& target ) { //initialization: _errorList.clear(); target.clear(); unsigned int ErrorCount = 0; //Check fileextension: if (filename.substr( filename.rfind(".")+1 ) != "xd") return reportError("[XDataLoader::import] Fileextension is not .xd: " + filename + "\n"); // Attempt to open the file in text mode and retrieve DefTokeniser ArchiveTextFilePtr file = GlobalFileSystem().openTextFile(filename); if (file == NULL) return reportError("[XDataLoader::import] Failed to open file: " + filename + "\n"); std::istream is(&(file->getInputStream())); parser::BasicDefTokeniser tok(is); //Parse Loop: while (tok.hasMoreTokens()) { if (parseXDataDef(tok)) target.insert(XDataMap::value_type(_newXData->getName(),_newXData)); else ErrorCount += 1; } //Write summary _errorList.push_back( "[XDataLoader::import] Import finished with " + string::to_string(_errorList.size()) + " error(s)/warning(s). " + string::to_string(target.size()) + " XData-definition(s) successfully imported, but failed to import at least " + string::to_string(ErrorCount) + " definitions.\n" ); //Summary output: if (_errorList.size() == 1) //No errors. rMessage() << _errorList[0]; else rConsoleError() << _errorList[_errorList.size() - 1]; if (target.size() == 0) return false; return true; } // XDataLoader::import const bool XDataLoader::parseXDataDef(parser::DefTokeniser& tok, const std::string& definitionName) { _name = tok.nextToken(); _newXData.reset(); //Check Syntax: try { tok.assertNextToken("{"); } catch (...) { while (tok.hasMoreTokens() && (tok.nextToken() != "{")) {} jumpOutOfBrackets(tok); return reportError(tok, "[XDataLoader::import] Syntaxerror in definition: " + _name + ". '{' expected. Undefined behavior!\n\tTrying to Jump to next XData definition. Might lead to furthers errors.\n"); } //Check if every definition shall be parsed or only a specific one: if (!definitionName.empty() && _name != definitionName) { jumpOutOfBrackets(tok); return false; } //Initialization: _guiPageError.clear(); _maxPageCount = 0; _maxGuiNumber = 0; _guiPageDef = ""; _numPages = 0; _sndPageTurn = ""; _guiPage.clear(); _guiPage.resize(MAX_PAGE_COUNT,""); //see MAX_PAGE_COUNT declaration in XData.h for explanation. //Parse-Loop: while (tok.hasMoreTokens()) { std::string token = tok.nextToken(); if (token == "}") break; if (!storeContent(token, &tok, _name)) return false; } //cleaning up: if ( _maxGuiNumber+1 > _numPages) //Append missing GUI-errormessages... Until now, it wasn't clear how many guipages are actually discarded. { int diff = _maxGuiNumber + 1 - _maxPageCount; for (unsigned int n = _guiPageError.size()-diff; n<_guiPageError.size(); n++) { reportError(_guiPageError[n]); } } // Check if _guiPage-statements for all pages are available. if (_guiPageDef.empty()) { reportError("[XDataLoader::import] Warning for definition: " + _name + ". _guiPage-statement(s) missing. Setting default value...\n"); if (_newXData->getPageLayout() == TwoSided) _guiPageDef = DEFAULT_TWOSIDED_GUI; else _guiPageDef = DEFAULT_ONESIDED_GUI; } for (std::size_t n=0; n<_numPages; n++ ) { if (_guiPage[n].empty()) _guiPage[n] = _guiPageDef; } //Store values: _newXData->setGuiPage(_guiPage); _newXData->setNumPages(_numPages); //Use default sndPageTurn if the statement was missing: if (_sndPageTurn.empty()) { _newXData->setSndPageTurn(DEFAULT_SNDPAGETURN); reportError("[XDataLoader::import] Warning for definition: " + _name + ". _sndPageTurn-statement missing. Setting default value...\n"); } else _newXData->setSndPageTurn(_sndPageTurn); return true; } const bool XDataLoader::storeContent(const std::string& statement, parser::DefTokeniser* tok, const std::string& defName, const std::string& content) { //page-statement: if (statement.substr(0,4) == "page") { //Now that it is known, whether we are dealing with a Two- or OneSidedXDataObject, create the XData object. if (!_newXData) { if (statement.find("left",6) != std::string::npos || statement.find("right",6) != std::string::npos) //TwoSided _newXData = XDataPtr(new TwoSidedXData(_name)); else _newXData = XDataPtr(new OneSidedXData(_name)); } //Acquire PageIndex std::size_t PageIndex; std::size_t numEnd = 4; while (statement[numEnd++] != '_') {} std::string number = statement.substr(4,numEnd-5); try { PageIndex = std::stoi(number)-1; } catch (...) { if (tok != NULL) { _newXData.reset(); return reportError(*tok, "[XDataLoader::import] Error in definition: " + defName + ", " + statement + " statement. '" + number + "' is not a number.\n\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } _newXData.reset(); return reportError("[XDataLoader::import] Error in definition: " + defName + ", " + statement + " statement. '" + number + "' is not a number.\n" ); } //Read content std::string readContent; if (tok != NULL) { if (!readLines(*tok, readContent)) { _newXData.reset(); return reportError(*tok, "[XDataLoader::import] Error in definition: " + defName + ". Failed to read content of " + statement + " statement.\n\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } } else readContent = content; // Check whether readContent actually has content. (There might be space, linebreaks and tabs that can be discarded) bool hasContent = false; for (std::size_t n = 0; n < readContent.size(); n++) { if ( (readContent[n] != ' ') && (readContent[n] != '\t') && (readContent[n] != '\n') ) { hasContent = true; break; } } //Check PageIndex-Range if (PageIndex >= _numPages ) { if (hasContent) //_numPages is raised automatically, if a page with content is detected... { _numPages = PageIndex+1; reportError("[XDataLoader::import] Warning for definition: " + defName + ", " + statement + " statement.\n\tnumPages not (yet) specified or too low. Raising _numPages to " + number +"...\n" ); } else { reportError("[XDataLoader::import] Warning for definition: " + defName + ", " + statement + " statement.\n\tnumPages not (yet) specified or too low. However, this page does not contain any text and is discarded...\n" ); return true; } } //Refresh the _maxPageCount variable if this page has content. if (hasContent) { if (_maxPageCount < PageIndex+1) _maxPageCount = PageIndex+1; //Write the content into the XData object Side side; if (statement.find("left",6) != std::string::npos) //Side is discarded on OneSidedXData... side = Left; else side = Right; if (statement.find("body",6) != std::string::npos) _newXData->setPageContent(Body, PageIndex, side, readContent); //could throw if PageIndex >= MAX_PAGE_COUNT. Unlikely! else _newXData->setPageContent(Title, PageIndex, side, readContent); } return true; } //gui_page statement else if (statement.substr(0,8) == "gui_page") { //Acquire gui_page number: std::string number = statement.substr(8); std::size_t guiNumber; try { guiNumber = std::stoi(number)-1; } catch (...) { if (tok != NULL) { _newXData.reset(); return reportError(*tok, "[XDataLoader::import] Error in definition: " + defName + ", gui_page statement. '" + number + "' is not a number.\n\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } _newXData.reset(); return reportError("[XDataLoader::import] Error in definition: " + defName + ", gui_page statement. '" + number + "' is not a number.\n"); } if (_maxGuiNumber < guiNumber) _maxGuiNumber = guiNumber; //Get the GuiPageDef: if (tok != NULL) { if (!readLines(*tok, _guiPageDef)) { _newXData.reset(); return reportError(*tok, "[XDataLoader::import] Error in definition: " + defName + ". Failed to read content of " + statement + " statement.\n\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } } else _guiPageDef = content; _guiPage[ guiNumber ] = _guiPageDef; //could throw if guiNumber >= MAX_PAGE_COUNT //Append error-message if _numPages is exceeded. if (guiNumber >= _numPages) _guiPageError.push_back("[XDataLoader::import] Warning for definition: " + defName + ". More _guiPage statements, than pages. Discarding statement for Page " + number + ".\n" ); return true; } //num_pages statement else if (statement == "num_pages") { //Get num_pages: if (tok != NULL) { std::string number; if (!readLines(*tok, number)) { _newXData.reset(); return reportError(*tok, "[XDataLoader::import] Error in definition: " + defName + ". Failed to read content of " + statement + " statement.\n\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } try { _numPages = std::stoi(number); } catch(...) { _newXData.reset(); return reportError(*tok, "[XDataLoader::import] Error in definition: " + defName + ", num_pages statement. '" + number + "' is not a number.\n\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } } else { try { _numPages = std::stoi(content); } catch (...) { return reportError("[XDataLoader::import] Error in definition: " + defName + ", num_pages statement. '" + content + "' is not a number.\n" ); } } //Correct a faulty _numPages value if (_maxPageCount > _numPages) { _numPages = _maxPageCount; reportError("[XDataLoader::import] Warning for definition: " + defName + ". The specified _numPages statement did not match the amount of pages with content.\n\tnumPages is set to " + string::to_string(_numPages) + ".\n"); } return true; } //snd_page_turn statement else if (statement == "snd_page_turn") { if (tok != NULL) { if (!readLines(*tok, _sndPageTurn)) { _newXData.reset(); return reportError(*tok, "[XDataLoader::import] Error in definition: " + defName + ". Failed to read content of " + statement + " statement.\n\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } } else _sndPageTurn = content; return true; } //import statement else if (statement == "import") { StringPairList importedData; std::string SourceDef; StringMap statements; if (tok == NULL) //Only works with def tokeniser. { _newXData.reset(); return false; } if (!getImportParameters(*tok, statements, SourceDef, defName)) { _newXData.reset(); return false; } retrieveXdInfo(); //refresh defmap. Prevents faulty imports. if (!recursiveImport(SourceDef, statements, defName, importedData)) { _errorList[_errorList.size()-1] += "\tTrying to Jump to next XData definition. Might lead to furthers errors.\n"; jumpOutOfBrackets(*tok); _newXData.reset(); return false; } for (std::size_t n=0; n < importedData.size(); n++) { if (!storeContent(importedData[n].first, NULL, defName, importedData[n].second)) { _newXData.reset(); return reportError(*tok, "[XDataLoader::import] Error in definition: " + defName + ". Import-statement failed.\n\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } } return true; } else if (statement == "precache") { return true; } _newXData.reset(); jumpOutOfBrackets(*tok); return false; } const bool XDataLoader::recursiveImport( const std::string& sourceDef, const StringMap& statements, const std::string& defName, StringPairList& importContent) { //Check where the Definition is stored in the _defMap StringVectorMap::iterator it = _defMap.find(sourceDef); if (it == _defMap.end()) //Definition not found!! return reportError("[XData::import] Error in definition: " + defName + ". Found an import-statement, but not the corresponding definition.\n"); std::size_t FileCount = it->second.size(); if (FileCount > 1) //Definition contained in multiple files. reportError( "[XData::import] Warning for definition: " + defName + ". Found an import-statement and the referenced definition exists multiple times.\n" ); //Try every file the definition is stored in. If import of none succeeded, the import failed. for (std::size_t k = 0; k < FileCount; k++) { //Init StringMap StmtsCpy = statements; StringMap ImpStmts; std::string ImpSrcDef = ""; int BracketDepth = 1; //Open the file ArchiveTextFilePtr file = GlobalFileSystem().openTextFile(it->second[k]); if (file == NULL) return reportError( "[XData::import] Error in definition: " + defName + ". Found an import-statement, but failed to open the corresponding file: " + it->second[k] + ".\n" ); //Find the Source-Definition in the File: std::istream is(&(file->getInputStream())); parser::BasicDefTokeniser ImpTok(is); while (true) { while ( ImpTok.nextToken() != sourceDef) {} if ( ImpTok.nextToken() == "{") break; } //Import-Loop: Find the SourceStatements in the definition and parse the contents. //Further Import-directives possibly have to be handled recursively... while (ImpTok.hasMoreTokens()) { //Find SourceStatements or further import-statements. Count entering and leaving brackets to identify definition end. std::string token = ImpTok.nextToken(); StringMap::iterator StIt = StmtsCpy.find(token); if (StIt != StmtsCpy.end()) { //Token found that matches the Source statement. Parse it and delete statement from StmtsCpy afterwards. std::string StmtCtnt; if (readLines(ImpTok, StmtCtnt)) { importContent.push_back( StringPairList::value_type( StIt->second, StmtCtnt )); StmtsCpy.erase(StIt); } else if (!ImpTok.hasMoreTokens()) { //Syntaxerror! std::string errMSG = "[XData::import] Error in definition: " + defName + ". Found an import-statement, but an sytax-error occured in the definition " + sourceDef + ", in sourcefile " + it->second[k] + ".\n"; if ( (FileCount > 1) && (k+1 < FileCount) ) errMSG += "\tThe referenced definition has been found in multiple files. Trying next file...\n"; reportError(errMSG); break; } else //The found token was not an actual statement. continue; } else if (token == "{") BracketDepth += 1; else if (token == "}") BracketDepth -= 1; else if (token == "import") { //getImportParameters fails on syntax errors or EOF (which is an syntax-error as well), so go on with next file. if (!getImportParameters(ImpTok, ImpStmts, ImpSrcDef, defName)) { std::string errMSG = "[XData::import] Error in definition: " + defName + ". Found an import-statement, but an sytax-error occured in the definition " + sourceDef + ", in sourcefile " + it->second[k] + ".\n"; if ( (FileCount > 1) && (k < FileCount-1) ) errMSG += "\tThe referenced definition has been found in multiple files. Trying next file...\n"; reportError(errMSG); break; } } if (StmtsCpy.size() == 0) //Success: Return! return true; if (BracketDepth == 0) { //Not all Source-statements have been found in the definition. If an import statement has been found //check if all requested statements would be imported by it. if (ImpSrcDef != "") { StringMap ChkStmts = StmtsCpy; StringMap RecoverStatements; for (StringMap::iterator impCheck = ImpStmts.begin(); impCheck != ImpStmts.end(); impCheck++) { StringMap::iterator temp = ChkStmts.find(impCheck->second); if (temp != ChkStmts.end()) { ChkStmts.erase(temp); RecoverStatements.insert(*impCheck); } } if (ChkStmts.size() == 0) { //All necessary statements found in import-directive. StringPairList imported; if (recursiveImport(ImpSrcDef,RecoverStatements,sourceDef,imported)) { //Success! Store imported contents and return! for (std::size_t u = 0; u < imported.size(); u++) { importContent.push_back( StringPairList::value_type( StmtsCpy.find(imported[u].first)->second, imported[u].second )); } return true; } else { std::string errMSG = "[XData::import] Error in definition: " + defName + ". Found an import-statement, but failed to import the requested statements of definition " + sourceDef + ", in sourcefile " + it->second[k] + ".\n"; if ( (FileCount > 1) && (k < FileCount-1) ) errMSG += "\tThe referenced definition has been found in multiple files. Trying next file...\n"; reportError(errMSG); break; } } } //No import-statement found or not all requested definitions in import statement. Move on with next file. std::string errMSG = "[XData::import] Error in definition: " + defName + ". Found an import-statement, but couldn't find all referenced statements in the definition " + sourceDef + ", in sourcefile " + it->second[k] + ".\n"; if ( (FileCount > 1) && (k < FileCount-1) ) errMSG += "\tThe referenced definition has been found in multiple files. Trying next file...\n"; reportError(errMSG); break; } //End of definition was reached! } //End of Import-Loop } //End of File-duplicate-Loop return reportError("[XData::import] Error in definition: " + defName + ". Import-statement failed.\n"); } const bool XDataLoader::getImportParameters(parser::DefTokeniser& tok, StringMap& statements, std::string& sourceDef, const std::string& defName) { std::string token; //Enter content brackets try { tok.assertNextToken("{"); } catch(...) { return reportError(tok, "[XDataLoader::import] Syntax error in definition: " + defName + ", import-statement. '{' expected. Undefined behavior!\n" ); } //Get Source- and DestStatements try { token = tok.nextToken(); while (token != "}") { tok.skipTokens(1); //Skip "->" statements.insert( StringMap::value_type(token,tok.nextToken()) ); token = tok.nextToken(); } } catch (...) { return reportError(tok, "[XDataLoader::import] Error in definition: " + defName + ". Failed to read content of import-statement. Probably Syntax-error.\n" + "\tTrying to Jump to next XData definition. Might lead to furthers errors.\n", 2 ); } //Get Name of Source-Definition try { tok.assertNextToken("from"); } catch (...) { return reportError(tok, "[XDataLoader::import] Syntax error in definition: " + defName + ", import-statement. 'from' expected. Undefined behavior!\n" + "\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } try { sourceDef = tok.nextToken(); } catch (...) { return reportError(tok, "[XDataLoader::import] Error in definition: " + defName + ". Failed to read content of import-statement.\n" + "\tTrying to Jump to next XData definition. Might lead to furthers errors.\n" ); } return true; } inline const bool XDataLoader::readLines(parser::DefTokeniser& tok, std::string& what) const { std::stringstream out; std::string token; //Check Syntax try { tok.assertNextToken(":"); what = tok.nextToken(); if (what != "{" ) //Parsing SingleLine Content return true; //Parse MultiLine Content. token = tok.nextToken(); while (token != "}") { if (token == "/") //newlines can also be signalized by slashed. Ignore them { token = tok.nextToken(); continue; } else if (token.c_str()[token.length()-1] == '\\') //support for escaped quotes in texts... token = token.substr(0,token.length()-1) + "\""; else token = token + "\n"; out << token; token = tok.nextToken(); } } catch (...) { return false; } what = out.str(); return true; } void XDataLoader::jumpOutOfBrackets(parser::DefTokeniser& tok, std::size_t currentDepth) const { while ( tok.hasMoreTokens() && currentDepth > 0) { std::string token = tok.nextToken(); if (token == "{") currentDepth += 1; else if (token == "}") currentDepth -= 1; } } void XDataLoader::retrieveXdInfo() { _defMap.clear(); _fileSet.clear(); _duplicatedDefs.clear(); //ScopedDebugTimer timer("XData definitions parsed: "); GlobalFileSystem().forEachFile(XDATA_DIR, XDATA_EXT, [&](const std::string& filename) { loadFromFile(filename); }, 99); } void XDataLoader::loadFromFile(const std::string& filename) { // Attempt to open the file in text mode ArchiveTextFilePtr file = GlobalFileSystem().openTextFile(XDATA_DIR + filename); if (file != NULL) { // File is open, so add the file to the _fileSet-set and parse the tokens _fileSet.insert(file->getModName() + "/" + file->getName()); try { std::istream is(&(file->getInputStream())); parser::BasicDefTokeniser tok(is); //grab all names from stream: while (tok.hasMoreTokens()) { std::string tempstring = tok.nextToken(); tok.assertNextToken("{"); std::pair ret = _defMap.insert( StringVectorMap::value_type(tempstring, StringList(1, XDATA_DIR + filename) ) ); if (!ret.second) //Definition already exists. { ret.first->second.push_back(XDATA_DIR + filename); rError() << "[XDataLoader] The definition " << tempstring << " of the file " << filename << " already exists. It was defined at least once. First in " << ret.first->second[0] << ".\n"; //Create an entry in the _duplicatedDefs map with the original file. If entry already exists, insert will fail. std::pair duplRet = _duplicatedDefs.insert( StringVectorMap::value_type(tempstring, StringList(1,ret.first->second[0]) ) ); //The new file is appended to the vector. duplRet.first->second.push_back(XDATA_DIR + filename); } jumpOutOfBrackets(tok); } } catch (parser::ParseException& e) { rError() << "[XDataLoader] Failed to parse " << filename << ": " << e.what() << std::endl; } } else { rError() << "[XDataLoader] Unable to open " << filename << std::endl; } } } // namespace XData DarkRadiant-2.5.0/plugins/dm.gui/XDataLoader.h000066400000000000000000000150531321750546400210620ustar00rootroot00000000000000#pragma once #include "itextstream.h" #include "XData.h" #include "parser/DefTokeniser.h" #include #include "ifilesystem.h" namespace XData { namespace { const std::string XDATA_DIR = "xdata/"; const std::string XDATA_EXT = "xd"; } typedef std::set StringSet; typedef std::map XDataMap; typedef std::map> StringVectorMap; typedef std::map StringMap; typedef std::vector> StringPairList; ///////////////////////////// XDataSelector: // Class for importing XData from files and retrieving fileinfos: duplicated definitions, // List of all definitions with their corresponding filename. class XDataLoader { // Notes: // -Importer cannot cope with multiple definitions in a single file currently. public: // Imports a MultiMap of XData-Pointers sorted by name from the specified File (just the name, not the path). // Returns false if import failed. The import-breaking error-message is the last element of _errorList, which can // be retrieved by calling getImportSummary() and also stores other errors and warnings. // -target: Key Value = definitionName, Mapped Value = XData object. const bool import(const std::string& filename, XDataMap& target); // Imports a single Definition from the specified file (filename) if defined or all duplicated definitions if only // the definitionName is specified. Returns false if import failed. The import-breaking error-message is the last // element of _errorList, which can be retrieved by calling getImportSummary() and also stores other errors and warnings. // -target: Key Value = filepath INCLUDING ModPath(!!), Mapped Value = XData object. const bool importDef(const std::string& definitionName, XDataMap& target, const std::string& filename = ""); virtual ~XDataLoader() { _defMap.clear(); _duplicatedDefs.clear(); _fileSet.clear(); _errorList.clear(); _guiPageError.clear(); _guiPage.clear(); } //Getters: // Returns StringVector with errors and warnings of the last import process // as well as a brief summary in the last element of the vector. const StringList& getImportSummary() const { return _errorList; } // Returns Map of duplicated definitions. (Data might be outdated, maybe use retrieveXdInfo() before) // Key Value = DefinitionNames, Mapped Value = StringVector of corresponding filenames. const StringVectorMap& getDuplicateDefinitions() const { if (_duplicatedDefs.empty()) { throw std::runtime_error("No Data available. Call retrieveXdInfo() before."); } return _duplicatedDefs; } // Returns StringSet of all .xd-files in the VFS. (Data might be outdated, maybe use retrieveXdInfo() before) const StringSet& getXdFilenameList() const { if (_fileSet.empty()) throw std::runtime_error("No Data available. Call retrieveXdInfo() before."); return _fileSet; } // Returns a map of all Definitions and their corresponding filenames found in the VFS. The filenames are stored // in a vector in case a definition exists multiple times. (Data might be outdated, maybe use retrieveXdInfo() before) const StringVectorMap& getDefinitionList() const { if (_defMap.empty()) throw std::runtime_error("No Data available. Call retrieveXdInfo() before."); return _defMap; } // Retrieves all XData-related information found in the VFS. void retrieveXdInfo(); // Functor operator: Adds all definitions found in the target file to the _defMap. void loadFromFile(const std::string& filename); private: // Issues the ErrorMessage to the cerr console and appends it to the _errorList. Returns always false, so that it can be used after a return statement. const bool reportError(const std::string& ErrorMessage) { rConsoleError() << ErrorMessage; _errorList.push_back(ErrorMessage); return false; } // Additionally to what the upper version of reportError(..) does, this method also tries to jump to the next definition by calling jumpOutOfBrackets. const bool reportError(parser::DefTokeniser& tok, const std::string& ErrorMessage, std::size_t currentDepth = 1) { reportError(ErrorMessage); jumpOutOfBrackets(tok, currentDepth); return false; } // Opens the file in which sourceDef is contained, while handling duplicate definitions. // -statements: Key value = Statements in sourceDef, Mapped value = Statement in the calling definition to whom the imported content shall be parsed. // -defName: Name of the definition that has induced the recursiveImport. This is just used for error reporting. // -importContent: Vector of stringpairs. First = Name of the destination Statement in the calling definition, Second = Imported Content. const bool recursiveImport(const std::string& sourceDef, const StringMap& statements, const std::string& defName, StringPairList& importContent); // Parses the contents of the import-statement. const bool getImportParameters(parser::DefTokeniser& tok, StringMap& statements, std::string& sourceDef, const std::string& defName); // Checks where the content following in the tokenizer has to be stored. DefName is the name of the // definition for whom content is parsed and is just used for error-messages. If tok is NULL, the string content is stored. This // is only induced by the import-directive, which on the contrary must have tok!= NULL. const bool storeContent(const std::string& statement, parser::DefTokeniser* tok, const std::string& defName, const std::string& content = ""); // Parses a single definition from a stream into an XData object and generates warning and error messages. If definitionName // is defined, only matching definitions will be parsed and return false otherwise. const bool parseXDataDef(parser::DefTokeniser& tok, const std::string& definitionName = ""); // Skips the ":" and parses the following SingleLine or MultiLine content into What. const bool readLines(parser::DefTokeniser& tok, std::string& what) const; // Used to jump out of a definition. Can lead to undefined behavior on Syntax-errors. void jumpOutOfBrackets(parser::DefTokeniser& tok, std::size_t currentDepth = 1) const; //General Member variables: StringList _errorList; StringVectorMap _defMap; StringSet _fileSet; StringVectorMap _duplicatedDefs; //Helper-variables for import: XDataPtr _newXData; std::string _name; StringList _guiPageError; std::size_t _maxPageCount; std::size_t _maxGuiNumber; std::string _guiPageDef; std::size_t _numPages; std::string _sndPageTurn; StringList _guiPage; }; // XDataLoader class typedef std::shared_ptr XDataLoaderPtr; } // namespace XData DarkRadiant-2.5.0/plugins/dm.gui/XDataSelector.cpp000066400000000000000000000063271321750546400217730ustar00rootroot00000000000000#include "XDataSelector.h" #include "i18n.h" #include "imainframe.h" #include "iuimanager.h" #include "ReadableEditorDialog.h" #include #include namespace ui { namespace { const char* const WINDOW_TITLE = N_("Choose an XData Definition..."); const int WINDOW_WIDTH = 500; const int WINDOW_HEIGHT = 600; const char* const XDATA_ICON = "sr_icon_readable.png"; const char* const FOLDER_ICON = "folder16.png"; } XDataSelector::XDataSelector(const XData::StringVectorMap& files, ReadableEditorDialog* editorDialog) : DialogBase(_(WINDOW_TITLE), editorDialog), _store(new wxutil::TreeModel(_columns)), _files(files), _editorDialog(editorDialog) { _xdataIcon.CopyFromBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + XDATA_ICON)); _folderIcon.CopyFromBitmap(wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + FOLDER_ICON)); fillTree(); SetSize(WINDOW_WIDTH, WINDOW_HEIGHT); // Add a vbox for the dialog elements SetSizer(new wxBoxSizer(wxVERTICAL)); // Add a vbox for the dialog elements wxBoxSizer* vbox = new wxBoxSizer(wxVERTICAL); GetSizer()->Add(vbox, 1, wxEXPAND | wxALL, 12); // Create the treeview _view = wxutil::TreeView::CreateWithModel(this, _store, wxDV_NO_HEADER); _view->AppendIconTextColumn(_("Xdata Path"), _columns.name.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); _view->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(XDataSelector::onSelectionChanged), NULL, this); // Use the TreeModel's full string search function _view->AddSearchColumn(_columns.name); vbox->Add(_view, 1, wxEXPAND | wxBOTTOM, 6); vbox->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT); FindWindowById(wxID_OK, this)->Enable(false); CenterOnParent(); } std::string XDataSelector::run(const XData::StringVectorMap& files, ReadableEditorDialog* editorDialog) { XDataSelector* dialog = new XDataSelector(files, editorDialog); std::string rv = ""; if (dialog->ShowModal() == wxID_OK) { rv = dialog->_selection; } dialog->Destroy(); return rv; } void XDataSelector::visit(wxutil::TreeModel& /* store */, wxutil::TreeModel::Row& row, const std::string& path, bool isExplicit) { row[_columns.name] = wxVariant(wxDataViewIconText(path.substr(path.rfind("/") + 1), isExplicit ? _xdataIcon : _folderIcon)); row[_columns.fullName] = path; row[_columns.isFolder] = !isExplicit; row.SendItemAdded(); } void XDataSelector::fillTree() { // Start adding to tree. wxutil::VFSTreePopulator populator(_store); for (XData::StringVectorMap::const_iterator it = _files.begin(); it != _files.end(); ++it) { populator.addPath(it->first); } populator.forEachNode(*this); _store->SortModelFoldersFirst(_columns.name, _columns.isFolder); } void XDataSelector::onSelectionChanged(wxDataViewEvent& ev) { wxDataViewItem item = _view->GetSelection(); if (item.IsOk()) { wxutil::TreeModel::Row row(item, *_store); if (!row[_columns.isFolder].getBool()) { _selection = row[_columns.fullName]; _editorDialog->updateGuiView(this, "", _selection); FindWindowById(wxID_OK, this)->Enable(true); return; } } FindWindowById(wxID_OK, this)->Enable(false); } } // namespace ui DarkRadiant-2.5.0/plugins/dm.gui/XDataSelector.h000066400000000000000000000034541321750546400214360ustar00rootroot00000000000000#pragma once #include "XDataLoader.h" #include "wxutil/dialog/DialogBase.h" #include "wxutil/VFSTreePopulator.h" #include "wxutil/TreeView.h" namespace ui { class ReadableEditorDialog; ///////////////////////////// XDataSelector: // Runs a dialog for choosing XData definitions, which updates the guiView of the calling // ReadableEditorDialog for previewing. class XDataSelector : public wxutil::DialogBase, public wxutil::VFSTreePopulator::Visitor { private: // Treestore enum struct XdataTreeModelColumns : public wxutil::TreeModel::ColumnRecord { XdataTreeModelColumns() : name(add(wxutil::TreeModel::Column::IconText)), fullName(add(wxutil::TreeModel::Column::String)), isFolder(add(wxutil::TreeModel::Column::Boolean)) {} wxutil::TreeModel::Column name; wxutil::TreeModel::Column fullName; wxutil::TreeModel::Column isFolder; }; // The tree XdataTreeModelColumns _columns; wxutil::TreeModel::Ptr _store; wxutil::TreeView* _view; // A Map of XData files. Basically just the keyvalues are needed. XData::StringVectorMap _files; // The name of the chosen definition std::string _selection; // Pointer to the ReadableEditorDialog for updating the guiView. ReadableEditorDialog* _editorDialog; wxIcon _xdataIcon; wxIcon _folderIcon; public: // Runs the dialog and returns the name of the chosen definition. static std::string run(const XData::StringVectorMap& files, ReadableEditorDialog* editorDialog); void visit(wxutil::TreeModel& store, wxutil::TreeModel::Row& row, const std::string& path, bool isExplicit); private: // private contructor called by the run method. XDataSelector(const XData::StringVectorMap& files, ReadableEditorDialog* editorDialog); void fillTree(); void onSelectionChanged(wxDataViewEvent& ev); // view is manually bound }; } //namespace ui DarkRadiant-2.5.0/plugins/dm.gui/XdFileChooserDialog.cpp000066400000000000000000000072541321750546400231070ustar00rootroot00000000000000#include "XdFileChooserDialog.h" #include "wxutil/dialog/MessageBox.h" #include "i18n.h" #include "imainframe.h" #include "ReadableEditorDialog.h" #include #include #include "idialogmanager.h" namespace ui { namespace { const char* const WINDOW_TITLE = N_("Choose a file..."); } int XdFileChooserDialog::Import(const std::string& defName, XData::XDataPtr& newXData, std::string& filename, XData::XDataLoaderPtr& loader, ReadableEditorDialog* editorDialog) { // Import the file: XData::XDataMap xdMap; if (loader->importDef(defName,xdMap)) { if (xdMap.size() > 1) { // The requested definition has been defined in multiple files. Use the XdFileChooserDialog to pick a file. // Optimally, the preview renderer would already show the selected definition. XdFileChooserDialog* fcDialog = new XdFileChooserDialog(defName, xdMap, editorDialog); int result = fcDialog->ShowModal(); if (result == wxID_OK) { XData::XDataMap::iterator ChosenIt = xdMap.find(fcDialog->_chosenFile); filename = ChosenIt->first; newXData = ChosenIt->second; } fcDialog->Destroy(); return result; } else { filename = xdMap.begin()->first; newXData = xdMap.begin()->second; if (loader->getImportSummary().size() > 1) { std::string msg = fmt::format(_("{0} successfully imported."), defName); msg += "\n\nHowever, there were some problems.\n\n"; msg += _("Do you want to open the import summary?"); wxutil::Messagebox dialog(_("Problems during import"), msg, ui::IDialog::MESSAGE_ASK, editorDialog ); if (dialog.run() == ui::IDialog::RESULT_YES) { editorDialog->showXdImportSummary(); } } } return wxID_OK; } throw ImportFailedException(_("Import failed")); } XdFileChooserDialog::XdFileChooserDialog(const std::string& defName, const XData::XDataMap& xdMap, ReadableEditorDialog* editorDialog) : DialogBase(_(WINDOW_TITLE), editorDialog), _listStore(new wxutil::TreeModel(_columns, true)), _treeview(NULL), _editorDialog(editorDialog), _defName(defName) { SetSizer(new wxBoxSizer(wxVERTICAL)); // Add a vbox for the dialog elements wxBoxSizer* vbox = new wxBoxSizer(wxVERTICAL); GetSizer()->Add(vbox, 1, wxEXPAND | wxALL, 12); // Create topLabel wxStaticText* topLabel = new wxStaticText(this, wxID_ANY, _("The requested definition has been found in multiple Files. Choose the file:")); // Create the list of files: _treeview = wxutil::TreeView::CreateWithModel(this, _listStore, wxDV_NO_HEADER); _treeview->AppendTextColumn(_("File"), _columns.name.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); // Append all xdMap-entries to the list. for (XData::XDataMap::const_iterator it = xdMap.begin(); it != xdMap.end(); ++it) { wxutil::TreeModel::Row row = _listStore->AddItem(); row[_columns.name] = it->first; row.SendItemAdded(); } // Connect the selection change signal _treeview->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(XdFileChooserDialog::onSelectionChanged), NULL, this); //Add everything to the vbox and to the window. vbox->Add(topLabel, 0, wxBOTTOM, 6); vbox->Add(_treeview, 1, wxEXPAND | wxBOTTOM, 6); vbox->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT); } void XdFileChooserDialog::onSelectionChanged(wxDataViewEvent& ev) { wxDataViewItem item = _treeview->GetSelection(); if (item.IsOk()) { wxutil::TreeModel::Row row(item, *_listStore); _chosenFile = row[_columns.name]; _editorDialog->updateGuiView(this, "", _defName, _chosenFile.substr(_chosenFile.find("/")+1)); } } } // namespace ui DarkRadiant-2.5.0/plugins/dm.gui/XdFileChooserDialog.h000066400000000000000000000034411321750546400225460ustar00rootroot00000000000000#pragma once #include #include "XDataLoader.h" #include "wxutil/dialog/DialogBase.h" #include "wxutil/TreeView.h" namespace ui { class ReadableEditorDialog; ///////////////////////////// XdFileChooserDialog: // Imports a given definition. If the definition has been found in multiple files, // the dialog shows up asking which file to use. class XdFileChooserDialog : public wxutil::DialogBase { public: struct ImportFailedException : public std::runtime_error { ImportFailedException(const std::string& what) : std::runtime_error(what) {} }; private: // Treestore enum struct ListStoreColumns : public wxutil::TreeModel::ColumnRecord { ListStoreColumns() : name(add(wxutil::TreeModel::Column::String)) {} wxutil::TreeModel::Column name; }; // A container for storing enumerated widgets ListStoreColumns _columns; wxutil::TreeModel::Ptr _listStore; wxutil::TreeView* _treeview; // The chosen filename. std::string _chosenFile; // Pointer to the ReadableEditorDialog for updating the guiView. ReadableEditorDialog* _editorDialog; // Definition name std::string _defName; public: // Imports the definition given by defName and stores the result in newXData and the corresponding filename. // If the definition is found in mutliple files a dialog is shown asking the user which file to use. // @throws: ImportFailedException static int Import(const std::string& defName, XData::XDataPtr& newXData, std::string& filename, XData::XDataLoaderPtr& loader, ReadableEditorDialog* editorDialog); private: // Private constructor called by run. XdFileChooserDialog(const std::string& defName, const XData::XDataMap& xdMap, ReadableEditorDialog* editorDialog); void onOk(); void onCancel(); void onSelectionChanged(wxDataViewEvent& ev); }; } // namespace ui DarkRadiant-2.5.0/plugins/dm.gui/gui/000077500000000000000000000000001321750546400173415ustar00rootroot00000000000000DarkRadiant-2.5.0/plugins/dm.gui/gui/Gui.cpp000066400000000000000000000042031321750546400205700ustar00rootroot00000000000000#include "Gui.h" #include "itextstream.h" namespace gui { const IGuiWindowDefPtr& Gui::getDesktop() const { return _desktop; } void Gui::setDesktop(const IGuiWindowDefPtr& newDesktop) { _desktop = newDesktop; } GuiPtr Gui::createFromTokens(parser::DefTokeniser& tokeniser) { GuiPtr gui(new Gui); while (tokeniser.hasMoreTokens()) { std::string token = tokeniser.nextToken(); if (token == "windowDef") { if (gui->getDesktop() == NULL) { GuiWindowDefPtr desktop(new GuiWindowDef(*gui)); desktop->constructFromTokens(tokeniser); gui->setDesktop(desktop); } else { rError() << "Cannot define multiple top-level windowDefs" << std::endl; } } } return gui; } void Gui::setStateString(const std::string& key, const std::string& value) { _state[key] = value; // Handle state variable links to windowDef registers GuiStateChangedSignals::iterator i = _stateSignals.find(key); if (i != _stateSignals.end()) { i->second.emit(); } } sigc::signal& Gui::getChangedSignalForState(const std::string& key) { GuiStateChangedSignals::iterator i = _stateSignals.find(key); if (i != _stateSignals.end()) { return i->second; } // Insert a new signal std::pair result = _stateSignals.insert(std::make_pair(key, sigc::signal())); return result.first->second; } std::string Gui::getStateString(const std::string& key) { GuiState::const_iterator i = _state.find(key); return (i != _state.end()) ? i->second : ""; } void Gui::initTime(const std::size_t time) { if (_desktop != NULL) { _desktop->initTime(time, true); } } void Gui::update(const std::size_t timestep) { if (_desktop != NULL) { // Recursively update child windowDefs _desktop->update(timestep, true); } } IGuiWindowDefPtr Gui::findWindowDef(const std::string& name) { // Handle "Desktop" right here if (name == "Desktop") { return _desktop; } return (_desktop != NULL) ? _desktop->findWindowDef(name) : IGuiWindowDefPtr(); } void Gui::pepareRendering() { if (_desktop != NULL) { // Recursively prepare child windowDefs _desktop->pepareRendering(true); } } } // namespace DarkRadiant-2.5.0/plugins/dm.gui/gui/Gui.h000066400000000000000000000036431321750546400202440ustar00rootroot00000000000000#pragma once #include "igui.h" #include #include #include #include "parser/DefTokeniser.h" #include "GuiWindowDef.h" namespace gui { class Gui; typedef std::shared_ptr GuiPtr; class GuiWindowDef; typedef std::shared_ptr GuiWindowDefPtr; /** * greebo: This class represents a single D3 GUI. It holds all * the windowDefs and the source code behind. */ class Gui : public IGui { private: // The desktop window IGuiWindowDefPtr _desktop; // The global GUI state variables typedef std::unordered_map GuiState; GuiState _state; typedef std::unordered_map> GuiStateChangedSignals; GuiStateChangedSignals _stateSignals; public: const IGuiWindowDefPtr& getDesktop() const override; void setDesktop(const IGuiWindowDefPtr& newDesktop) override; // Sets the given state variable (gui:: = ) void setStateString(const std::string& key, const std::string& value) override; // Allocate and/or return the changed signal for the given key sigc::signal& getChangedSignalForState(const std::string& key) override; // Returns the state string "gui::" or an empty string if non-existent std::string getStateString(const std::string& key) override; // Sets up the time of the entire GUI (all windowDefs) void initTime(const std::size_t time) override; // "Think" routine, advances all active windowDefs (where notime == false) void update(const std::size_t timestep) override; // Returns a reference to the named windowDef, returns NULL if not found IGuiWindowDefPtr findWindowDef(const std::string& name) override; // Called by the GuiRenderer to re-compile text VBOs, etc. void pepareRendering() override; // Takes the given token stream and attempts to construct a GUI object from it // Returns NULL on failure static GuiPtr createFromTokens(parser::DefTokeniser& tokeniser); }; } // namespace DarkRadiant-2.5.0/plugins/dm.gui/gui/GuiExpression.cpp000066400000000000000000000430441321750546400226560ustar00rootroot00000000000000#include "GuiExpression.h" #include #include #include #include "itextstream.h" #include "string/trim.h" #include "string/convert.h" #include "string/predicate.h" namespace gui { // An expression referring to a GUI state variable GuiStateVariableExpression::GuiStateVariableExpression(IGui& gui, const std::string& variableName) : _gui(gui), _variableName(variableName) { if (!_variableName.empty()) { // Register to the GUI state change signal _gui.getChangedSignalForState(_variableName).connect([this]() { signal_valueChanged().emit(); }); } } float GuiStateVariableExpression::getFloatValue() { return string::convert(_gui.getStateString(_variableName)); } std::string GuiStateVariableExpression::getStringValue() { return _gui.getStateString(_variableName); } namespace detail { // Abstract base class for an expression taking two sub-expression as arguments // Additions, Multiplications, Divisions, Modulo, comparisons, etc. class BinaryExpression : public GuiExpression { public: // The operator precedence, smaller values mean higher priority enum Precedence { LOGICAL_NOT = 0, // ! MULTIPLICATION = 0, // * DIVISION = 0, // / MODULO = 0, // % ADDITION = 1, // + SUBTRACTION = 1, // - RELATIONAL_COMPARISON = 2, // > >= < <= EQUALITY_COMPARISON = 3, // == != LOGICAL_AND = 4, // && LOGICAL_OR = 5, // || }; protected: GuiExpressionPtr _a; GuiExpressionPtr _b; Precedence _precedence; sigc::connection _aChanged; sigc::connection _bChanged; public: BinaryExpression(Precedence precedence, const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : GuiExpression(), _a(a), _b(b), _precedence(precedence) { if (_a) { _aChanged = _a->signal_valueChanged().connect([this]() { signal_valueChanged().emit(); }); } if (_b) { _bChanged = _b->signal_valueChanged().connect([this]() { signal_valueChanged().emit(); }); } } Precedence getPrecedence() const { return _precedence; } void setA(const GuiExpressionPtr& a) { // Disconnect from any previous signal _aChanged.disconnect(); _a = a; if (_a) { _aChanged = _a->signal_valueChanged().connect([this]() { signal_valueChanged().emit(); }); } } void setB(const GuiExpressionPtr& b) { // Disconnect from any previous signal _bChanged.disconnect(); _b = b; if (_b) { _bChanged = _b->signal_valueChanged().connect([this]() { signal_valueChanged().emit(); }); } } virtual std::string getStringValue() override { return string::to_string(getFloatValue()); } }; typedef std::shared_ptr BinaryExpressionPtr; // An expression adding the value of two expressions class AddExpression : public BinaryExpression { public: AddExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(ADDITION, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() + _b->getFloatValue(); } }; // An expression subtracting the value of two expressions class SubtractExpression : public BinaryExpression { public: SubtractExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(SUBTRACTION, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() - _b->getFloatValue(); } }; // An expression multiplying the value of two expressions class MultiplyExpression : public BinaryExpression { public: MultiplyExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(MULTIPLICATION, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() * _b->getFloatValue(); } }; // An expression dividing the value of two expressions class DivideExpression : public BinaryExpression { public: DivideExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(DIVISION, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() / _b->getFloatValue(); } }; // An expression returning modulo of A % B class ModuloExpression : public BinaryExpression { public: ModuloExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(MODULO, a, b) {} virtual float getFloatValue() override { return fmod(_a->getFloatValue(), _b->getFloatValue()); } }; // An expression returning 1 if A < B, otherwise 0 class LesserThanExpression : public BinaryExpression { public: LesserThanExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(RELATIONAL_COMPARISON, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() < _b->getFloatValue() ? 1.0f : 0; } }; // An expression returning 1 if A <= B, otherwise 0 class LesserThanOrEqualExpression : public BinaryExpression { public: LesserThanOrEqualExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(RELATIONAL_COMPARISON, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() <= _b->getFloatValue() ? 1.0f : 0; } }; // An expression returning 1 if A > B, otherwise 0 class GreaterThanExpression : public BinaryExpression { public: GreaterThanExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(RELATIONAL_COMPARISON, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() > _b->getFloatValue() ? 1.0f : 0; } }; // An expression returning 1 if A >= B, otherwise 0 class GreaterThanOrEqualExpression : public BinaryExpression { public: GreaterThanOrEqualExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(RELATIONAL_COMPARISON, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() >= _b->getFloatValue() ? 1.0f : 0; } }; // An expression returning 1 if A == B, otherwise 0 class EqualityExpression : public BinaryExpression { public: EqualityExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(EQUALITY_COMPARISON, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() == _b->getFloatValue() ? 1.0f : 0; } }; // An expression returning 1 if A != B, otherwise 0 class InequalityExpression : public BinaryExpression { public: InequalityExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(EQUALITY_COMPARISON, a, b) {} virtual float getFloatValue() override { return _a->getFloatValue() != _b->getFloatValue() ? 1.0f : 0; } }; // An expression returning the opposite of the contained expression class LogicalNotExpression : public BinaryExpression { private: GuiExpressionPtr _contained; public: LogicalNotExpression(const GuiExpressionPtr& contained = GuiExpressionPtr()) : BinaryExpression(LOGICAL_NOT, contained) {} virtual float getFloatValue() override { return _a->getFloatValue() != 0.0f ? 1.0f : 0.0f; } virtual std::string getStringValue() override { return ""; // not implemented for string values } }; // An expression returning 1 if both A and B are true (non-zero), otherwise 0 class LogicalAndExpression : public BinaryExpression { public: LogicalAndExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(LOGICAL_AND, a, b) {} virtual float getFloatValue() override { return (_a->getFloatValue() != 0 && _b->getFloatValue() != 0) ? 1.0f : 0; } }; // An expression returning 1 if either A or B are true (non-zero), otherwise 0 class LogicalOrExpression : public BinaryExpression { public: LogicalOrExpression(const GuiExpressionPtr& a = GuiExpressionPtr(), const GuiExpressionPtr& b = GuiExpressionPtr()) : BinaryExpression(LOGICAL_OR, a, b) {} virtual float getFloatValue() override { return (_a->getFloatValue() != 0 || _b->getFloatValue() != 0) ? 1.0f : 0; } }; // Fixme: Maybe merge this with ShaderExpressionTokeniser class GuiExpressionTokeniser : public parser::DefTokeniser { private: parser::DefTokeniser& _tokeniser; // buffer containing tokens pulled from the wrapped tokeniser std::list _buffer; const char* _delims; public: GuiExpressionTokeniser(parser::DefTokeniser& tokeniser) : _tokeniser(tokeniser), _delims("+-%*/=!<>&|") {} bool hasMoreTokens() const override { return !_buffer.empty() || _tokeniser.hasMoreTokens(); } std::string nextToken() override { if (_buffer.empty()) { // Pull a new token from the underlying stream and split it up fillBuffer(_tokeniser.nextToken()); } std::string result = _buffer.front(); _buffer.pop_front(); return result; } std::string peek() const override { if (!_buffer.empty()) { // We have items in the buffer, return that one return _buffer.front(); } // No items in the buffer, take a peek in the underlying stream std::string rawToken = _tokeniser.peek(); if (rawToken.empty()) { return rawToken; // don't tokenise empty strings, just return it as it is } // Split up the token by using a sub-tokeniser, keeping all delimiters parser::BasicDefTokeniser subtokeniser(rawToken, "", _delims); // Return the first of these tokens, then exit return subtokeniser.nextToken(); } private: void fillBuffer(const std::string& token) { if (token.empty()) { // It's possible to retrieve an empty token if the DefTokeniser encounters an empty string "" // This is valid, so push the empty token _buffer.push_back(token); return; } // Use a separate tokeniser and keep all delimiters parser::BasicDefTokeniser subtokeniser(token, "", _delims); while (subtokeniser.hasMoreTokens()) { _buffer.push_back(subtokeniser.nextToken()); } } }; class GuiExpressionParser { private: IGui& _gui; GuiExpressionTokeniser& _tokeniser; typedef std::stack OperandStack; typedef std::stack OperatorStack; public: GuiExpressionParser(IGui& gui, GuiExpressionTokeniser& tokeniser) : _gui(gui), _tokeniser(tokeniser) {} GuiExpressionPtr getExpression() { // The local variable and operator stack OperandStack operands; OperatorStack operators; enum { SearchingForOperand, SearchingForOperator, } searchState; searchState = SearchingForOperand; while (_tokeniser.hasMoreTokens()) { if (searchState == SearchingForOperand) { // Don't actually pull the token from the tokeniser, we might want to // return the tokeniser to the caller if the token is not part of the expression // The token will be exhausted from the stream once it is recognised as keyword std::string token = _tokeniser.peek(); // The parsed operand GuiExpressionPtr term; if (token == "(") { _tokeniser.nextToken(); // valid token, exhaust // New scope, treat this as new expression term = getExpression(); } else if (token == ")") { _tokeniser.nextToken(); // valid token, exhaust // End of scope reached, break the loop and roll up the expression break; } else if (string::starts_with(token, "gui::")) { // This is a GUI state variable, extract the variable name _tokeniser.nextToken(); term = std::make_shared(_gui, token.substr(5)); } // If this is a + or -, take it as a sign operator else if (token == "+") { // A leading +, just ignore it _tokeniser.nextToken(); continue; } else if (token == "-") { // A leading -, interpret it as -1 * operands.push(std::make_shared(-1.0f)); operators.push(std::make_shared()); // Discard the - token _tokeniser.nextToken(); continue; } else { // This might either be a float or a string constant term = std::make_shared(_tokeniser.nextToken()); } if (!term) { break; // We've run out of terms } // The token has already been pulled from the tokeniser operands.push(term); // Look for an operator next searchState = SearchingForOperator; continue; } else if (searchState == SearchingForOperator) { // Get an operator BinaryExpressionPtr op = getOperator(); if (op) { // Check precedence if we have previous operators while (!operators.empty()) { if (operators.top()->getPrecedence() <= op->getPrecedence()) { finaliseOperator(operands, operators); } else { break; } } // Push this one on the operator stack operators.push(op); searchState = SearchingForOperand; // back to operand search mode continue; } // We might also hit a closing parenthesis after an operand if (_tokeniser.peek() == ")") { // Exhaust and break the loop _tokeniser.nextToken(); } // Not an operator, break the loop break; } } // Roll up the operations while (!operators.empty()) { finaliseOperator(operands, operators); } if (operands.empty()) { throw parser::ParseException("Missing expression"); } GuiExpressionPtr rv = operands.top(); operands.pop(); assert(operands.empty()); // there should be nothing left on the stack return rv; } private: void finaliseOperator(OperandStack& operands, OperatorStack& operators) { const BinaryExpressionPtr& op = operators.top(); // Set operand B first, we're dealing with a LIFO stack // Do not push a second operand in the case of a logical NOT if (!std::dynamic_pointer_cast(op)) { // Need two operands for an operator if (operands.size() < 2) { throw parser::ParseException("Too few operands for operator."); } op->setB(operands.top()); operands.pop(); } else { // Need at least one operand for a NOT operator if (operands.size() < 1) { throw parser::ParseException("Too few operands for ! operator."); } } op->setA(operands.top()); operands.pop(); // Push the result back on the stack, as operand operands.push(op); // Remove the operator, after we've copied it to the operand stack operators.pop(); } // Helper routines BinaryExpressionPtr getOperator() { if (!_tokeniser.hasMoreTokens()) { return BinaryExpressionPtr(); } std::string token = _tokeniser.peek(); if (token == "+") { _tokeniser.nextToken(); return std::make_shared(); } else if (token == "-") { _tokeniser.nextToken(); return std::make_shared(); } else if (token == "*") { _tokeniser.nextToken(); return std::make_shared(); } else if (token == "/") { _tokeniser.nextToken(); return std::make_shared(); } else if (token == "%") { _tokeniser.nextToken(); return std::make_shared(); } else if (token == "<") { _tokeniser.nextToken(); if (_tokeniser.peek() == "=") { _tokeniser.nextToken(); return std::make_shared(); } return std::make_shared(); } else if (token == ">") { _tokeniser.nextToken(); if (_tokeniser.peek() == "=") { _tokeniser.nextToken(); return std::make_shared(); } return std::make_shared(); } else if (token == "=") { _tokeniser.nextToken(); if (_tokeniser.peek() == "=") { _tokeniser.nextToken(); return std::make_shared(); } rError() << "Assignment operator '=' found in expression, did you mean '=='?" << std::endl; } else if (token == "!") { _tokeniser.nextToken(); if (_tokeniser.peek() == "=") { _tokeniser.nextToken(); return std::make_shared(); } return std::make_shared(); } else if (token == "&") { _tokeniser.nextToken(); if (_tokeniser.peek() == "&") { _tokeniser.nextToken(); return std::make_shared(); } rError() << "Bit-wise operator '&' found in expression, did you mean '&&'?" << std::endl; } else if (token == "|") { _tokeniser.nextToken(); if (_tokeniser.peek() == "|") { _tokeniser.nextToken(); return std::make_shared(); } rError() << "Bit-wise operator '|' found in expression, did you mean '||'?" << std::endl; } return BinaryExpressionPtr(); } }; } GuiExpression::GuiExpression() {} GuiExpressionPtr GuiExpression::CreateFromString(IGui& gui, const std::string& exprStr) { parser::BasicDefTokeniser tokeniser(exprStr, parser::WHITESPACE, "{}(),"); return CreateFromTokens(gui, tokeniser); } GuiExpressionPtr GuiExpression::CreateFromTokens(IGui& gui, parser::DefTokeniser& tokeniser) { // Create an adapter which takes care of splitting the tokens into finer grains // The incoming DefTokeniser is not splitting up expressions like "3*4" without any whitespace in them detail::GuiExpressionTokeniser adapter(tokeniser); try { detail::GuiExpressionParser parser(gui, adapter); return parser.getExpression(); } catch (parser::ParseException& ex) { rWarning() << "[GuiExpressionParser] " << ex.what() << std::endl; return GuiExpressionPtr(); } } } DarkRadiant-2.5.0/plugins/dm.gui/gui/GuiExpression.h000066400000000000000000000110561321750546400223210ustar00rootroot00000000000000#pragma once #include "igui.h" #include #include "string/convert.h" #include namespace gui { class GuiExpression; typedef std::shared_ptr GuiExpressionPtr; // Represents a right-hand expression found in the GUI code, which can be // a simple float, a string, a gui variable or a complex formula. class GuiExpression { protected: sigc::signal _sigValueChanged; public: GuiExpression(); virtual float getFloatValue() = 0; virtual std::string getStringValue() = 0; // Sub-expressions or clients can subscribe to get notified about changes sigc::signal& signal_valueChanged() { return _sigValueChanged; } static GuiExpressionPtr CreateFromString(IGui& gui, const std::string& exprStr); static GuiExpressionPtr CreateFromTokens(IGui& gui, parser::DefTokeniser& tokeniser); }; // An expression representing a constant floating point value class FloatExpression : public GuiExpression, public ConstantExpression { public: FloatExpression(float value) : ConstantExpression(value) {} virtual float getFloatValue() override { return this->evaluate(); } virtual std::string getStringValue() override { return string::to_string(getFloatValue()); } }; // An expression representing a constant string value class StringExpression : public GuiExpression, public ConstantExpression { public: StringExpression(const std::string& value) : ConstantExpression(value) {} virtual float getFloatValue() override { return string::convert(getStringValue()); } virtual std::string getStringValue() override { return this->evaluate(); } }; // Adapter class converting a generic GuiExpression to a IGuiExpression of a given type // making use of the string conversion templates in the string/conv.h header. template class TypedExpression : public IGuiExpression { private: GuiExpressionPtr _contained; sigc::signal _sigValueChanged; public: TypedExpression(const GuiExpressionPtr& contained) : _contained(contained) { if (_contained) { // Connect the changed signal of the contained expression // to fire our own signal _contained->signal_valueChanged().connect([this]() { signal_valueChanged().emit(); }); } } // The generic evaluate() implementation will try to cast the string // value of the GuiExpression to the desired ValueType virtual ValueType evaluate() override { return string::convert(_contained->getStringValue()); } sigc::signal& signal_valueChanged() override { return _sigValueChanged; } }; // Boolean specialisation, making use of the contained getFloatValue() // casting it to a bool (which is true if the float value is not 0.0f). template<> class TypedExpression : public IGuiExpression { private: GuiExpressionPtr _contained; sigc::signal _sigValueChanged; public: TypedExpression(const GuiExpressionPtr& contained) : _contained(contained) { if (_contained) { // Connect the changed signal of the contained expression // to fire our own signal _contained->signal_valueChanged().connect([this]() { signal_valueChanged().emit(); }); } } virtual bool evaluate() override { return _contained->getFloatValue() != 0.0f; } sigc::signal& signal_valueChanged() override { return _sigValueChanged; } }; // An expression representing a Vector4 value // Translating between a quadruple of script expressions and IGuiExpression class Vector4Expression : public IGuiExpression { private: std::vector _vec; sigc::signal _sigValueChanged; public: Vector4Expression(const GuiExpressionPtr& x, const GuiExpressionPtr& y, const GuiExpressionPtr& z, const GuiExpressionPtr& w) : _vec(4) // initialise to size 4 { _vec[0] = x; _vec[1] = y; _vec[2] = z; _vec[3] = w; for (const GuiExpressionPtr& comp : _vec) { if (!comp) continue; comp->signal_valueChanged().connect([this]() { signal_valueChanged().emit(); }); } } virtual Vector4 evaluate() override { return Vector4(_vec[0]->getFloatValue(), _vec[1]->getFloatValue(), _vec[2]->getFloatValue(), _vec[3]->getFloatValue()); } sigc::signal& signal_valueChanged() override { return _sigValueChanged; } }; class GuiStateVariableExpression : public GuiExpression { private: IGui& _gui; std::string _variableName; public: GuiStateVariableExpression(IGui& gui, const std::string& variableName); virtual float getFloatValue() override; virtual std::string getStringValue() override; }; } DarkRadiant-2.5.0/plugins/dm.gui/gui/GuiManager.cpp000066400000000000000000000103001321750546400220560ustar00rootroot00000000000000#include "GuiManager.h" #include "iarchive.h" #include "ifilesystem.h" #include "itextstream.h" #include "parser/CodeTokeniser.h" #include "Gui.h" namespace gui { GuiManager::GuiManager() : _guiLoader(std::bind(&GuiManager::findGuis, this)) {} void GuiManager::registerGui(const std::string& guiPath) { // Just store the path in the map, for later reference _guis.insert(GuiInfoMap::value_type(GUI_DIR + guiPath, GuiInfo())); } std::size_t GuiManager::getNumGuis() { ensureGuisLoaded(); return _guis.size(); } void GuiManager::foreachGui(Visitor& visitor) { ensureGuisLoaded(); for (GuiInfoMap::iterator i = _guis.begin(); i != _guis.end(); ++i) { visitor.visit(i->first, i->second.type); } } void GuiManager::reloadGui(const std::string& guiPath) { GuiPtr gui = loadGui(guiPath); determineGuiType(gui); } GuiType GuiManager::getGuiType(const std::string& guiPath) { // Get the GUI (will load the file if necessary) GuiPtr gui = std::static_pointer_cast(getGui(guiPath)); GuiInfoMap::iterator found = _guis.find(guiPath); if (found == _guis.end()) { return FILE_NOT_FOUND; } // Gui Info found, determine readable type if necessary if (found->second.type == UNDETERMINED) { found->second.type = determineGuiType(found->second.gui); } return found->second.type; } GuiType GuiManager::determineGuiType(const GuiPtr& gui) { if (gui) { // TODO: Find a better way of distinguishing GUIs if (gui->findWindowDef("body") != NULL) { return ONE_SIDED_READABLE; } else if (gui->findWindowDef("leftBody") != NULL) { return TWO_SIDED_READABLE; } } else { return IMPORT_FAILURE; } return NO_READABLE; } void GuiManager::init() { _guiLoader.start(); } void GuiManager::reloadGuis() { clear(); init(); } void GuiManager::findGuis() { _errorList.clear(); _guis.clear(); // Traverse the file system, using this class as callback GlobalFileSystem().forEachFile(GUI_DIR, GUI_EXT, [&](const std::string& filename) { registerGui(filename); }, 99); rMessage() << "[GuiManager]: Found " << _guis.size() << " guis." << std::endl; } void GuiManager::clear() { _guiLoader.reset(); _guis.clear(); _errorList.clear(); } IGuiPtr GuiManager::getGui(const std::string& guiPath) { ensureGuisLoaded(); GuiInfoMap::iterator i = _guis.find(guiPath); // Path existent? if (i != _guis.end()) { // Found in the map, load if not yet attempted if (i->second.type == NOT_LOADED_YET) { loadGui(guiPath); } return i->second.gui; } // GUI not buffered, try to load afresh return loadGui(guiPath); } void GuiManager::ensureGuisLoaded() { _guiLoader.ensureFinished(); } GuiPtr GuiManager::loadGui(const std::string& guiPath) { ensureGuisLoaded(); // Insert a new entry in the map, if necessary std::pair result = _guis.insert( GuiInfoMap::value_type(guiPath, GuiInfo()) ); GuiInfo& info = result.first->second; ArchiveTextFilePtr file = GlobalFileSystem().openTextFile(guiPath); if (file == NULL) { std::string errMSG = "Could not open file: " + guiPath + "\n"; _errorList.push_back(errMSG); rError() << errMSG; info.type = FILE_NOT_FOUND; return GuiPtr(); } // Construct a Code Tokeniser, which is able to handle #includes try { parser::CodeTokeniser tokeniser(file, parser::WHITESPACE, "{}(),;"); info.gui = Gui::createFromTokens(tokeniser); info.type = UNDETERMINED; return info.gui; } catch (parser::ParseException& p) { std::string errMSG = "Error while parsing " + guiPath + ": " + p.what() + "\n"; _errorList.push_back(errMSG); rError() << errMSG; info.type = IMPORT_FAILURE; return GuiPtr(); } } const std::string& GuiManager::getName() const { static std::string _name(MODULE_GUIMANAGER); return _name; } const StringSet& GuiManager::getDependencies() const { static StringSet _dependencies; if (_dependencies.empty()) { _dependencies.insert(MODULE_VIRTUALFILESYSTEM); } return _dependencies; } void GuiManager::initialiseModule(const ApplicationContext& ctx) { rMessage() << getName() << "::initialiseModule called." << std::endl; // Search the VFS for GUIs init(); } void GuiManager::shutdownModule() { clear(); } } // namespace gui DarkRadiant-2.5.0/plugins/dm.gui/gui/GuiManager.h000066400000000000000000000045031321750546400215330ustar00rootroot00000000000000#pragma once #include "igui.h" #include "util/Noncopyable.h" #include #include "ifilesystem.h" #include "string/string.h" #include "ThreadedDefLoader.h" namespace gui { namespace { const std::string GUI_DIR("guis/readables/"); const std::string GUI_EXT("gui"); } class Gui; typedef std::shared_ptr GuiPtr; /** * greebo: This manager keeps track of all the loaded GUIs, * including parsing the .gui files on demand. */ class GuiManager : public IGuiManager, public util::Noncopyable { private: struct GuiInfo { // The type of this Gui (NOT_LOADED_YET by default) GuiType type; // the cached GUI pointer, can be NULL if load failed GuiPtr gui; GuiInfo() : type(NOT_LOADED_YET) {} GuiInfo(const GuiPtr& gui_, GuiType type_) : type(type_), gui(gui_) {} }; // The table of all loaded Gui, sorted by VFS path typedef std::map GuiInfoMap; GuiInfoMap _guis; util::ThreadedDefLoader _guiLoader; // A List of all the errors occuring lastly. StringList _errorList; public: GuiManager(); // Gets a GUI from the given VFS path, parsing it on demand // Returns NULL if the GUI couldn't be found or loaded. IGuiPtr getGui(const std::string& guiPath) override; // Returns the number of known GUIs (or GUI paths) std::size_t getNumGuis() override; // Traverse all known GUIs using the given Visitor void foreachGui(Visitor& visitor) override; // Returns the GUI appearance type for the given GUI path GuiType getGuiType(const std::string& guiPath) override; // Reload the gui void reloadGui(const std::string& guiPath) override; // Returns the _errorList for use in a GUI. const StringList& getErrorList() override { return _errorList; } // Clears out the GUIs and reloads them void reloadGuis() override; // RegisterableModule const std::string& getName() const override; const StringSet& getDependencies() const override; void initialiseModule(const ApplicationContext& ctx) override; void shutdownModule() override; private: void init(); void clear(); // Searches the VFS for all available GUI definitions void findGuis(); void ensureGuisLoaded(); GuiType determineGuiType(const GuiPtr& gui); GuiPtr loadGui(const std::string& guiPath); // Used by findGuis() void registerGui(const std::string& guiPath); }; } // namespace DarkRadiant-2.5.0/plugins/dm.gui/gui/GuiScript.cpp000066400000000000000000000264221321750546400217640ustar00rootroot00000000000000#include "GuiScript.h" #include "itextstream.h" #include "parser/DefTokeniser.h" #include "Gui.h" #include "GuiExpression.h" #include "Variable.h" #include "string/case_conv.h" #include "string/trim.h" #include "string/predicate.h" namespace gui { GuiScript::GuiScript(GuiWindowDef& owner) : _owner(owner), _ip(0), _curLevel(0) {} void GuiScript::parseIfStatement(parser::DefTokeniser& tokeniser) { // Prototype: if () [else ] // The initial "if" has already been parsed StatementPtr ifStatement(new Statement(Statement::ST_IF)); // Any opening and closing parentheses are handled by the expression parser ifStatement->_condition = getIfExpression(tokeniser); // condition // Add the statement at the current position pushStatement(ifStatement); // Parse the statement(s) to execute if the above condition is true parseStatement(tokeniser); // Check the next token to see where we need to jump to if the condition evaluates to false std::string nextToken = tokeniser.nextToken(); if (nextToken == "else") { // There is an "else" block, so we need to add a JMP statement before proceeding StatementPtr jmpStatement(new Statement(Statement::ST_JMP)); pushStatement(jmpStatement); // Set the original IF jump position to this else ifStatement->jmpDest = getCurPosition(); // As next step, parse the code in the else block parseStatement(tokeniser); // Finally, position the jump at the location right after the else block jmpStatement->jmpDest = getCurPosition(); } else { // No else, execution falls through, but we need to set the jump destination first ifStatement->jmpDest = getCurPosition(); switchOnToken(nextToken, tokeniser); } } void GuiScript::parseSetStatement(parser::DefTokeniser& tokeniser) { // Prototype: set [window::] StatementPtr st(new Statement(Statement::ST_SET)); st->args.push_back(_owner.parseString(tokeniser)); // variable // Add all tokens up to the semicolon as arguments while (true) { std::string token = tokeniser.peek(); // Sometimes the semicolon is missing if (token == ";" || token == "}") break; st->args.push_back(ConstantExpression::Create(tokeniser.nextToken())); // argument } pushStatement(st); } void GuiScript::parseTransitionStatement(parser::DefTokeniser& tokeniser) { // Prototype: transition [window::]