diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..c7365809 --- /dev/null +++ b/.clang-format @@ -0,0 +1,108 @@ +# Clang-format version v9.0.0 +--- +Language: Cpp +BasedOnStyle: Google + +ColumnLimit: 120 + +## +## Indent Style +## + +IndentWidth: 4 +AccessModifierOffset: -4 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +TabWidth: 4 +UseTab: Never +IndentCaseLabels: false +NamespaceIndentation: None + +## +## Align Style +## + +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: true +PointerAlignment: Left + +## +## SingleLine Style +## + +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BreakBeforeBraces: Allman +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeTernaryOperators: true +CommentPragmas: "^ IWYU pragma:" +ConstructorInitializerAllOnOneLineOrOnePerLine: false + +## +## Others +## + +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeComma +ReflowComments: true +SortIncludes: false +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [foreach, Q_FOREACH, BOOST_FOREACH] +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 3 + - Regex: ".*" + Priority: 1 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 diff --git a/.editorconfig b/.editorconfig index 433e5b04..552be4bf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,10 +14,10 @@ insert_final_newline = true charset = gb2312 # 4 space indentation -indent_style = tab +indent_style = space indent_size = 4 # Matches the exact files [*.{json,xml}] indent_style = space -indent_size = 2 \ No newline at end of file +indent_size = 2 diff --git a/README-zh.md b/README-zh.md new file mode 100644 index 00000000..86fe775d --- /dev/null +++ b/README-zh.md @@ -0,0 +1,66 @@ +![Kiwano Logo](https://github.com/Nomango/Kiwano/raw/master/logo/logo_text_h.png) + +# Kiwano 游戏引擎 + +[![Build status](https://ci.appveyor.com/api/projects/status/frqh09om9ldaklr9/branch/master?svg=true)](https://ci.appveyor.com/project/Nomango/kiwano/branch/master) +[![GitHub release](https://img.shields.io/github/release/nomango/kiwano)](https://github.com/Nomango/Kiwano/releases/latest) +[![GitHub license](https://img.shields.io/github/license/nomango/kiwano)](https://github.com/Nomango/Kiwano/blob/master/LICENSE) + +[English](./README.md) | 简体中文 + +## 介绍 + +Kiwano 是一个使用 C++ 开发的 2D 游戏引擎,目前仅支持 Windows 平台。 + +Kiwano-Core 是一个提供了一系列实用工具的游戏无关库,它的目的是简化 C++ 开发过程。 + +这个仓库仍处于开发过程中,我创建这个仓库用来学习游戏引擎知识和开发自己的小游戏。 + +你可以到 [Kiwano Demos](https://github.com/kiwanogame/KiwanoDemos) 仓库查看和学习如何使用 Kiwano 引擎实现小游戏。 + +欢迎您任何形式的贡献。 + +## 功能 + +* 舞台和角色管理 +* 舞台过渡动画 +* 动作行为 +* 按钮等简易UI元素 +* 音频支持 +* 网络通信支持 +* 数据持久化 +* 物理引擎 (基于 Box2D) +* GUI 引擎 (基于 ImGui) + +## 安装 + +### 开发环境 + +- Win8 或更高 (推荐 Win10) +- Visual Studio 2015 或更高 + +### 通过 NuGet 安装 + +1. 打开你的 Visual Studio 解决方案 +2. 在解决方案资源管理器, 右击 `引用` 并选择 `管理 NuGet 程序包` +3. 选择 `浏览` 选项卡, 搜索 `kiwano`, 选中列表中的包然后点击 `安装` +4. 开始使用 Kiwano 进行开发吧! + +### 通过源代码安装 + +1. 从 Github 仓库克隆或下载源代码 +2. 打开你的 Visual Studio 解决方案, 在解决方案资源管理器中右键你的解决方案, 选择 `添加` => `现有项` +3. 选中源代码目录下 /projects 文件夹中所有的 `.vcxproj` 文件,并确认添加 +4. 右键你的项目,打开 `属性`, 选中 C\C++ => 常规, 并将源代码文件夹下的 src 目录添加到 `附加包含目录` 中 +5. 右键你的项目 `引用` 并选择 `添加引用`, 选中 `kiwano` 项目和其他你需要的项目 +6. 开始使用 Kiwano 进行开发吧! + +## 开发计划 + +* 跨平台支持 +* 粒子系统 + +## 社交媒体 + +* 网站: [kiwanoengine.com](https://kiwanoengine.com) +* QQ群: 608406540 diff --git a/README.md b/README.md index 2c722015..e2905733 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ [![GitHub release](https://img.shields.io/github/release/nomango/kiwano)](https://github.com/Nomango/Kiwano/releases/latest) [![GitHub license](https://img.shields.io/github/license/nomango/kiwano)](https://github.com/Nomango/Kiwano/blob/master/LICENSE) +English | [简体中文](./README-zh.md) + ## Introduction Kiwano is a open-source 2D C++ game engine, only support win32 platform. @@ -20,12 +22,13 @@ More docs and examples will be added later. ## Features * Scene management * Transitions between scenes -* Actions behaviours +* Action behaviours * Buttons and menus * Texture atlas support * Audio support * Custom data storage -* Direct2D based +* Physical engine (based on Box2D) +* GUI system (based on ImGui) ## Install @@ -51,8 +54,7 @@ More docs and examples will be added later. 6. Now you can build your own applications based on Kiwano source code ! ## Next plan -* GUI system -* Physical engine +* Cross-platform * Particle system ## Contact diff --git a/appveyor.yml b/appveyor.yml index 2435c118..bbda4bc7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,6 @@ pull_requests: environment: global: time_out_mins: 5 - job_to_deploy: 6 # 3(images) * 1(platform) * 2(configuration) flag_to_deploy: false appveyor_api_token: secure: UJFCbRNHMOqQg3e3Kv/ZnaIqqwXAt+5HDldetaZsZ5E= @@ -46,13 +45,12 @@ for: only: - master environment: - matrix: + matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - VS_PLATFORM_TOOLSET: v142 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - VS_PLATFORM_TOOLSET: v141 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - VS_PLATFORM_TOOLSET: v140 + global: + job_to_deploy: 6 # 3(images) * 1(platform) * 2(configuration) - branches: except: @@ -62,7 +60,8 @@ for: environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - VS_PLATFORM_TOOLSET: v142 + global: + job_to_deploy: 2 # 1(images) * 1(platform) * 2(configuration) configuration: - Debug @@ -90,9 +89,9 @@ after_build: artifacts: - path: projects/output/**/*.lib - name: $(appveyor_project_name)-v$(appveyor_build_version)-$(VS_PLATFORM_TOOLSET).$(platform).$(configuration) + name: PublishedLibraries - path: projects/output/**/*.pdb - name: $(appveyor_project_name)-v$(appveyor_build_version)-$(VS_PLATFORM_TOOLSET).$(platform).$(configuration) + name: PublishedSymbols before_deploy: - ps: .\scripts\appveyor\coapp_make.ps1 @@ -100,9 +99,9 @@ before_deploy: deploy: - provider: GitHub repository: KiwanoEngine/Kiwano - tag: v$(appveyor_build_version) - release: v$(appveyor_build_version) - description: Kiwano-v$(appveyor_build_version) releases. + tag: v$(APPVEYOR_BUILD_VERSION) + release: v$(APPVEYOR_BUILD_VERSION) + description: Kiwano-v$(APPVEYOR_BUILD_VERSION) releases. auth_token: secure: pDsK6i03d4qRjtrNXcbhLpAquso/muJWgDSWJHnxP7b6p54kXEvptB67J+1kJOhq artifact: /.*\.nupkg/ diff --git a/projects/3rd-party/curl/libcurl.vcxproj.filters b/projects/3rd-party/curl/libcurl.vcxproj.filters index 6c4e394b..097d9b72 100644 --- a/projects/3rd-party/curl/libcurl.vcxproj.filters +++ b/projects/3rd-party/curl/libcurl.vcxproj.filters @@ -33,7 +33,6 @@ include - diff --git a/projects/3rd-party/tinyxml2/libtinyxml2.vcxproj b/projects/3rd-party/tinyxml2/libtinyxml2.vcxproj deleted file mode 100644 index 8d2e1039..00000000 --- a/projects/3rd-party/tinyxml2/libtinyxml2.vcxproj +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - Debug - Win32 - - - Release - Win32 - - - - {AB47E875-85E5-4105-A71E-88930EAAB910} - libtinyxml2 - - - - StaticLibrary - true - Unicode - $(DefaultPlatformToolset) - - - StaticLibrary - false - false - Unicode - $(DefaultPlatformToolset) - - - - - - - - - - - - - - - $(SolutionDir)\output\$(PlatformToolset)\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(PlatformToolset)\$(Platform)\$(Configuration)\$(ProjectName)\ - true - - - $(SolutionDir)\output\$(PlatformToolset)\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(PlatformToolset)\$(Platform)\$(Configuration)\$(ProjectName)\ - true - - - - Level3 - Disabled - true - None - true - ../../../src/3rd-party; - false - - - Windows - true - - - - - Level3 - MaxSpeed - true - true - false - true - None - true - ../../../src/3rd-party; - false - - - Windows - false - true - true - - - - - - diff --git a/projects/3rd-party/tinyxml2/libtinyxml2.vcxproj.filters b/projects/3rd-party/tinyxml2/libtinyxml2.vcxproj.filters deleted file mode 100644 index 71b75fa1..00000000 --- a/projects/3rd-party/tinyxml2/libtinyxml2.vcxproj.filters +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/projects/Kiwano.sln b/projects/Kiwano.sln index 2899bd53..12303d43 100644 --- a/projects/Kiwano.sln +++ b/projects/Kiwano.sln @@ -14,8 +14,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kiwano-physics", "kiwano-ph EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3rd-party", "3rd-party", "{2D8919F2-8922-4B3F-8F68-D4127C6BCBB7}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtinyxml2", "3rd-party\tinyxml2\libtinyxml2.vcxproj", "{AB47E875-85E5-4105-A71E-88930EAAB910}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libimgui", "3rd-party\imgui\libimgui.vcxproj", "{7FA1E56D-62AC-47D1-97D1-40B302724198}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcurl", "3rd-party\curl\libcurl.vcxproj", "{A9ABACC7-75A1-46BA-8E48-4105346D9719}" @@ -49,10 +47,6 @@ Global {DF599AFB-744F-41E5-AF0C-2146F90575C8}.Debug|Win32.Build.0 = Debug|Win32 {DF599AFB-744F-41E5-AF0C-2146F90575C8}.Release|Win32.ActiveCfg = Release|Win32 {DF599AFB-744F-41E5-AF0C-2146F90575C8}.Release|Win32.Build.0 = Release|Win32 - {AB47E875-85E5-4105-A71E-88930EAAB910}.Debug|Win32.ActiveCfg = Debug|Win32 - {AB47E875-85E5-4105-A71E-88930EAAB910}.Debug|Win32.Build.0 = Debug|Win32 - {AB47E875-85E5-4105-A71E-88930EAAB910}.Release|Win32.ActiveCfg = Release|Win32 - {AB47E875-85E5-4105-A71E-88930EAAB910}.Release|Win32.Build.0 = Release|Win32 {7FA1E56D-62AC-47D1-97D1-40B302724198}.Debug|Win32.ActiveCfg = Debug|Win32 {7FA1E56D-62AC-47D1-97D1-40B302724198}.Debug|Win32.Build.0 = Debug|Win32 {7FA1E56D-62AC-47D1-97D1-40B302724198}.Release|Win32.ActiveCfg = Release|Win32 @@ -70,7 +64,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {AB47E875-85E5-4105-A71E-88930EAAB910} = {2D8919F2-8922-4B3F-8F68-D4127C6BCBB7} {7FA1E56D-62AC-47D1-97D1-40B302724198} = {2D8919F2-8922-4B3F-8F68-D4127C6BCBB7} {A9ABACC7-75A1-46BA-8E48-4105346D9719} = {2D8919F2-8922-4B3F-8F68-D4127C6BCBB7} {0CBA9295-F14D-4966-A7C4-1DD68158176C} = {2D8919F2-8922-4B3F-8F68-D4127C6BCBB7} diff --git a/projects/kiwano/kiwano.vcxproj b/projects/kiwano/kiwano.vcxproj index eab6029a..a9cdfe46 100644 --- a/projects/kiwano/kiwano.vcxproj +++ b/projects/kiwano/kiwano.vcxproj @@ -9,15 +9,20 @@ + - + + + + + @@ -36,7 +41,6 @@ - @@ -44,7 +48,6 @@ - @@ -54,36 +57,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -96,6 +96,7 @@ + @@ -111,6 +112,7 @@ + @@ -122,35 +124,34 @@ + - - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -165,11 +166,6 @@ Win32 - - - {ab47e875-85e5-4105-a71e-88930eaab910} - - {FF7F943D-A89C-4E6C-97CF-84F7D8FF8EDF} kiwano diff --git a/projects/kiwano/kiwano.vcxproj.filters b/projects/kiwano/kiwano.vcxproj.filters index dce7c87c..4281032d 100644 --- a/projects/kiwano/kiwano.vcxproj.filters +++ b/projects/kiwano/kiwano.vcxproj.filters @@ -7,9 +7,6 @@ {68eac919-ee87-4030-a033-c251731928f5} - - {07b6d541-4a1b-472a-aae0-daf9d082fe84} - {c2654ccc-59f6-4c17-bb6b-99b07fc78702} @@ -19,29 +16,23 @@ {2e18d99a-e906-499a-9e29-4e0783202644} - - {7897afce-24cb-42b4-9443-56508e4ec89c} - {9314f30d-5742-48b6-94e5-e3b4284106f6} - - {30333461-e9bc-4709-84bd-ce6e0e1a3079} - {e84dcf9a-e650-473e-8c9c-193804ab9e76} {c629aedd-ffb9-4bc1-82c3-f50e77c82e77} + + {adb44ca9-674a-4b77-993f-d65193d8ab06} + + + {fd281702-0006-46d2-8fd1-28c502464164} + - - ui - - - ui - 2d @@ -66,9 +57,6 @@ core - - core - math @@ -156,63 +144,9 @@ core - - renderer - - - renderer - - - renderer - - - renderer - - - renderer - - - renderer - - - renderer - - - renderer\win32 - - - renderer\win32 - - - renderer\win32 - - - renderer\win32 - - - renderer\win32 - - - renderer\win32 - - - renderer - 2d - - renderer - - - renderer - - - renderer - - - renderer - math @@ -234,36 +168,18 @@ platform - - core - platform platform - - platform - 2d - - core - core - - renderer - - - renderer\win32 - - - renderer - platform\win32 @@ -288,14 +204,89 @@ core\event + + 2d + + + core + + + core + + + core + + + core + + + core + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render\DirectX + + + render\DirectX + + + render\DirectX + + + render\DirectX + + + render\DirectX + + + render\DirectX + + + render\DirectX + - - ui - - - ui - 2d @@ -317,9 +308,6 @@ core - - core - platform @@ -383,57 +371,9 @@ core - - renderer - - - renderer - - - renderer - - - renderer - - - renderer - - - renderer - - - renderer - - - renderer\win32 - - - renderer\win32 - - - renderer\win32 - - - renderer\win32 - - - renderer\win32 - - - renderer - 2d - - renderer - - - renderer - - - renderer - core @@ -455,18 +395,12 @@ platform - - platform - 2d core - - renderer - platform\win32 @@ -485,5 +419,71 @@ core\event + + 2d + + + core + + + platform\win32 + + + core + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render + + + render\DirectX + + + render\DirectX + + + render\DirectX + + + render\DirectX + + + render\DirectX + \ No newline at end of file diff --git a/scripts/clang-format-all b/scripts/clang-format-all new file mode 100644 index 00000000..645c3ceb --- /dev/null +++ b/scripts/clang-format-all @@ -0,0 +1,84 @@ +#!/bin/bash +# +# clang-format-all: a tool to run clang-format on an entire project +# Copyright (C) 2016 Evan Klitzke +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +function usage { + echo "Usage: $0 DIR..." + exit 1 +} + +if [ $# -eq 0 ]; then + usage +fi + +# Variable that will hold the name of the clang-format command +FMT="" + +# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent +# that the version number be part of the command. We prefer clang-format if +# that's present, otherwise we work backwards from highest version to lowest +# version. +for clangfmt in clang-format{,-{4,3}.{9,8,7,6,5,4,3,2,1,0}}; do + if which "$clangfmt" &>/dev/null; then + FMT="$clangfmt" + break + fi +done + +# Check if we found a working clang-format +if [ -z "$FMT" ]; then + echo "failed to find clang-format" + exit 1 +fi + +# Check all of the arguments first to make sure they're all directories +for dir in "$@"; do + if [ ! -d "${dir}" ]; then + echo "${dir} is not a directory" + usage + fi +done + +# Find a dominating file, starting from a given directory and going up. +find-dominating-file() { + if [ -r "$1"/"$2" ]; then + return 0 + fi + if [ "$1" = "/" ]; then + return 1 + fi + find-dominating-file "$(realpath "$1"/..)" "$2" + return $? +} + +# Run clang-format -i on all of the things +for dir in "$@"; do + pushd "${dir}" &>/dev/null + if ! find-dominating-file . .clang-format; then + echo "Failed to find dominating .clang-format starting at $PWD" + continue + fi + find . \ + \( -name '*.c' \ + -o -name '*.cc' \ + -o -name '*.cpp' \ + -o -name '*.h' \ + -o -name '*.hh' \ + -o -name '*.hpp' \) \ + -exec "${FMT}" -i '{}' \; + popd &>/dev/null +done diff --git a/src/3rd-party/OuterC/oc/function/details.h b/src/3rd-party/OuterC/oc/function/details.h index 9abefaa1..66813ea6 100644 --- a/src/3rd-party/OuterC/oc/function/details.h +++ b/src/3rd-party/OuterC/oc/function/details.h @@ -115,23 +115,23 @@ public: virtual _Ret invoke(_Args... args) const override { - return (static_cast<_Ty*>(ptr_)->*func_)(::std::forward<_Args>(args)...); + return (ptr_->*func_)(::std::forward<_Args>(args)...); } - static inline callable<_Ret, _Args...>* make(void* ptr, _FuncType func) + static inline callable<_Ret, _Args...>* make(_Ty* ptr, _FuncType func) { return new (::std::nothrow) proxy_mem_callable<_Ty, _Ret, _Args...>(ptr, func); } protected: - proxy_mem_callable(void* ptr, _FuncType func) + proxy_mem_callable(_Ty* ptr, _FuncType func) : ptr_(ptr) , func_(func) { } protected: - void* ptr_; + _Ty* ptr_; _FuncType func_; }; @@ -144,23 +144,23 @@ public: virtual _Ret invoke(_Args... args) const override { - return (static_cast<_Ty*>(ptr_)->*func_)(::std::forward<_Args>(args)...); + return (ptr_->*func_)(::std::forward<_Args>(args)...); } - static inline callable<_Ret, _Args...>* make(void* ptr, _FuncType func) + static inline callable<_Ret, _Args...>* make(_Ty* ptr, _FuncType func) { return new (::std::nothrow) proxy_const_mem_callable<_Ty, _Ret, _Args...>(ptr, func); } protected: - proxy_const_mem_callable(void* ptr, _FuncType func) + proxy_const_mem_callable(_Ty* ptr, _FuncType func) : ptr_(ptr) , func_(func) { } protected: - void* ptr_; + _Ty* ptr_; _FuncType func_; }; diff --git a/src/3rd-party/OuterC/oc/intrusive_list.h b/src/3rd-party/OuterC/oc/intrusive_list.h index 7138a22c..81c3d8e1 100644 --- a/src/3rd-party/OuterC/oc/intrusive_list.h +++ b/src/3rd-party/OuterC/oc/intrusive_list.h @@ -16,20 +16,19 @@ template ::point class intrusive_list_item { public: - using pointer_type = _PTy; - using const_pointer_type = const _PTy; + using pointer = _PTy; - intrusive_list_item() : prev_(nullptr), next_(nullptr) {} - intrusive_list_item(pointer_type rhs) : prev_(nullptr), next_(nullptr) { if (rhs) { prev_ = rhs->prev_; next_ = rhs->next_; } } + intrusive_list_item() : prev_(nullptr), next_(nullptr) {} + intrusive_list_item(pointer rhs) : prev_(nullptr), next_(nullptr) { if (rhs) { prev_ = rhs->prev_; next_ = rhs->next_; } } - const_pointer_type prev_item() const { return prev_; } - pointer_type prev_item() { return prev_; } - const_pointer_type next_item() const { return next_; } - pointer_type next_item() { return next_; } + const pointer prev_item() const { return prev_; } + pointer prev_item() { return prev_; } + const pointer next_item() const { return next_; } + pointer next_item() { return next_; } private: - pointer_type prev_; - pointer_type next_; + pointer prev_; + pointer next_; friend class intrusive_list<_Ty, _PTy>; }; @@ -39,23 +38,24 @@ template class intrusive_list { public: - using pointer_type = _PTy; - using const_pointer_type = const _PTy; + using value_type = typename std::pointer_traits<_PTy>::element_type; + using pointer = _PTy; + using reference = value_type&; - intrusive_list() : first_(), last_() {} - ~intrusive_list() { clear(); } + intrusive_list() : first_(), last_() {} + ~intrusive_list() { clear(); } - const_pointer_type first_item() const { return first_; } - pointer_type first_item() { return first_; } - const_pointer_type last_item() const { return last_; } - pointer_type last_item() { return last_; } + const pointer first_item() const { return first_; } + pointer first_item() { return first_; } + const pointer last_item() const { return last_; } + pointer last_item() { return last_; } inline bool empty() const { return first_ == nullptr; } - void push_back(pointer_type child) + void push_back(pointer child) { if (child->prev_) child->prev_->next_ = child->next_; @@ -77,7 +77,7 @@ public: last_ = child; } - void push_front(pointer_type child) + void push_front(pointer child) { if (child->prev_) child->prev_->next_ = child->next_; @@ -99,7 +99,7 @@ public: first_ = child; } - void insert_before(pointer_type child, pointer_type before) + void insert_before(pointer child, pointer before) { if (child->prev_) child->prev_->next_ = child->next_; @@ -116,7 +116,7 @@ public: before->prev_ = child; } - void insert_after(pointer_type child, pointer_type after) + void insert_after(pointer child, pointer after) { if (child->prev_) child->prev_->next_ = child->next_; @@ -133,7 +133,7 @@ public: after->next_ = child; } - void remove(pointer_type child) + void remove(pointer child) { if (child->next_) { @@ -159,10 +159,10 @@ public: void clear() { - pointer_type p = first_; + pointer p = first_; while (p) { - pointer_type tmp = p; + pointer tmp = p; p = p->next_; if (tmp) { @@ -180,8 +180,8 @@ public: return; int pos = 0; - pointer_type p = first_; - pointer_type tmp = p; + pointer p = first_; + pointer tmp = p; do { tmp = p; @@ -205,15 +205,19 @@ public: struct iterator_impl { using iterator_category = std::bidirectional_iterator_tag; - using pointer_type = _PTy; - using const_pointer_type = const _PTy; + using value_type = typename std::pointer_traits<_PTy>::element_type; + using difference_type = ptrdiff_t; + using pointer = _PTy; + using reference = value_type&; - inline iterator_impl(pointer_type ptr = nullptr, bool is_end = false) : base_(ptr), is_end_(is_end) {} + inline iterator_impl(pointer ptr = nullptr, bool is_end = false) : base_(ptr), is_end_(is_end) {} - inline pointer_type operator*() const { OC_ASSERT(base_ && !is_end_); return base_; } - inline iterator_impl& operator++() { OC_ASSERT(base_ && !is_end_); pointer_type next = base_->next_item(); if (next) base_ = next; else is_end_ = true; return (*this); } + inline pointer base() const { OC_ASSERT(!is_end_); return const_cast(base_); } + inline reference operator*() const { OC_ASSERT(base_ && !is_end_); return const_cast(*base_); } + inline pointer operator->() const { OC_ASSERT(base_ && !is_end_); return const_cast(base_); } + inline iterator_impl& operator++() { OC_ASSERT(base_ && !is_end_); pointer next = base_->next_item(); if (next) base_ = next; else is_end_ = true; return (*this); } inline iterator_impl operator++(int) { iterator_impl old = (*this); ++(*this); return old; } - inline iterator_impl& operator--() { OC_ASSERT(base_); if (is_end_) is_end_ = false; else base_ = pointer_type(base_->prev_item()); return (*this); } + inline iterator_impl& operator--() { OC_ASSERT(base_); if (is_end_) is_end_ = false; else base_ = pointer(base_->prev_item()); return (*this); } inline iterator_impl operator--(int) { iterator_impl old = (*this); --(*this); return old; } inline bool operator==(iterator_impl const& other) const { return base_ == other.base_ && is_end_ == other.is_end_; } inline bool operator!=(iterator_impl const& other) const { return !(*this == other); } @@ -221,11 +225,11 @@ public: private: bool is_end_; - pointer_type base_; + pointer base_; }; - using iterator = iterator_impl; - using const_iterator = iterator_impl; + using iterator = iterator_impl; + using const_iterator = iterator_impl; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; @@ -241,14 +245,14 @@ public: inline reverse_iterator rend() { return reverse_iterator(begin()); } inline const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } inline const_reverse_iterator crend() const { return rend(); } - inline pointer_type front() { if (empty()) throw std::out_of_range("front() called on empty intrusive_list"); return first_item(); } - inline const_pointer_type front() const { if (empty()) throw std::out_of_range("front() called on empty intrusive_list"); return first_item(); } - inline pointer_type back() { if (empty()) throw std::out_of_range("back() called on empty intrusive_list"); return last_item(); } - inline const_pointer_type back() const { if (empty()) throw std::out_of_range("back() called on empty intrusive_list"); return last_item(); } + inline pointer front() { if (empty()) throw std::out_of_range("front() called on empty intrusive_list"); return first_item(); } + inline const pointer front() const { if (empty()) throw std::out_of_range("front() called on empty intrusive_list"); return first_item(); } + inline pointer back() { if (empty()) throw std::out_of_range("back() called on empty intrusive_list"); return last_item(); } + inline const pointer back() const { if (empty()) throw std::out_of_range("back() called on empty intrusive_list"); return last_item(); } private: - pointer_type first_; - pointer_type last_; + pointer first_; + pointer last_; }; } // namespace oc diff --git a/src/3rd-party/pugixml/pugiconfig.hpp b/src/3rd-party/pugixml/pugiconfig.hpp new file mode 100644 index 00000000..579db3e7 --- /dev/null +++ b/src/3rd-party/pugixml/pugiconfig.hpp @@ -0,0 +1,74 @@ +/** + * pugixml parser - version 1.10 + * -------------------------------------------------------- + * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +#ifndef HEADER_PUGICONFIG_HPP +#define HEADER_PUGICONFIG_HPP + +// Uncomment this to enable wchar_t mode +#define PUGIXML_WCHAR_MODE + +// Uncomment this to enable compact mode +// #define PUGIXML_COMPACT + +// Uncomment this to disable XPath +// #define PUGIXML_NO_XPATH + +// Uncomment this to disable STL +// #define PUGIXML_NO_STL + +// Uncomment this to disable exceptions +#define PUGIXML_NO_EXCEPTIONS + +// Set this to control attributes for public classes/functions, i.e.: +// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL +// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL +// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall +// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead + +// Tune these constants to adjust memory-related behavior +// #define PUGIXML_MEMORY_PAGE_SIZE 32768 +// #define PUGIXML_MEMORY_OUTPUT_STACK 10240 +// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 + +// Uncomment this to switch to header-only version +#define PUGIXML_HEADER_ONLY + +// Uncomment this to enable long long support +// #define PUGIXML_HAS_LONG_LONG + +#endif + +/** + * Copyright (c) 2006-2019 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/src/3rd-party/pugixml/pugixml.cpp b/src/3rd-party/pugixml/pugixml.cpp new file mode 100644 index 00000000..90c48b21 --- /dev/null +++ b/src/3rd-party/pugixml/pugixml.cpp @@ -0,0 +1,12865 @@ +/** + * pugixml parser - version 1.10 + * -------------------------------------------------------- + * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +#ifndef SOURCE_PUGIXML_CPP +#define SOURCE_PUGIXML_CPP + +#include "pugixml.hpp" + +#include +#include +#include +#include +#include + +#ifdef PUGIXML_WCHAR_MODE +# include +#endif + +#ifndef PUGIXML_NO_XPATH +# include +# include +#endif + +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// For placement new +#include + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +# pragma warning(disable: 4702) // unreachable code +# pragma warning(disable: 4996) // this function or variable may be unsafe +#endif + +#if defined(_MSC_VER) && defined(__c2__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" // this function or variable may be unsafe +#endif + +#ifdef __INTEL_COMPILER +# pragma warning(disable: 177) // function was declared but never referenced +# pragma warning(disable: 279) // controlling expression is constant +# pragma warning(disable: 1478 1786) // function was declared "deprecated" +# pragma warning(disable: 1684) // conversion from pointer to same-sized integral type +#endif + +#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) +# pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away +#endif + +#ifdef __BORLANDC__ +# pragma option push +# pragma warn -8008 // condition is always false +# pragma warn -8066 // unreachable code +#endif + +#ifdef __SNC__ +// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug +# pragma diag_suppress=178 // function was declared but never referenced +# pragma diag_suppress=237 // controlling expression is constant +#endif + +#ifdef __TI_COMPILER_VERSION__ +# pragma diag_suppress 179 // function was declared but never referenced +#endif + +// Inlining controls +#if defined(_MSC_VER) && _MSC_VER >= 1300 +# define PUGI__NO_INLINE __declspec(noinline) +#elif defined(__GNUC__) +# define PUGI__NO_INLINE __attribute__((noinline)) +#else +# define PUGI__NO_INLINE +#endif + +// Branch weight controls +#if defined(__GNUC__) && !defined(__c2__) +# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0) +#else +# define PUGI__UNLIKELY(cond) (cond) +#endif + +// Simple static assertion +#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } + +// Digital Mars C++ bug workaround for passing char loaded from memory via stack +#ifdef __DMC__ +# define PUGI__DMC_VOLATILE volatile +#else +# define PUGI__DMC_VOLATILE +#endif + +// Integer sanitizer workaround; we only apply this for clang since gcc8 has no_sanitize but not unsigned-integer-overflow and produces "attribute directive ignored" warnings +#if defined(__clang__) && defined(__has_attribute) +# if __has_attribute(no_sanitize) +# define PUGI__UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow"))) +# else +# define PUGI__UNSIGNED_OVERFLOW +# endif +#else +# define PUGI__UNSIGNED_OVERFLOW +#endif + +// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) +#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) +using std::memcpy; +using std::memmove; +using std::memset; +#endif + +// Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations +#if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX) +# define LLONG_MIN (-LLONG_MAX - 1LL) +# define LLONG_MAX __LONG_LONG_MAX__ +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +#endif + +// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features +#if defined(_MSC_VER) && !defined(__S3E__) +# define PUGI__MSVC_CRT_VERSION _MSC_VER +#endif + +// Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size. +#if __cplusplus >= 201103 +# define PUGI__SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) +#elif defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 +# define PUGI__SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__) +#else +# define PUGI__SNPRINTF sprintf +#endif + +// We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat. +#ifdef PUGIXML_HEADER_ONLY +# define PUGI__NS_BEGIN namespace pugi { namespace impl { +# define PUGI__NS_END } } +# define PUGI__FN inline +# define PUGI__FN_NO_INLINE inline +#else +# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces +# define PUGI__NS_BEGIN namespace pugi { namespace impl { +# define PUGI__NS_END } } +# else +# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace { +# define PUGI__NS_END } } } +# endif +# define PUGI__FN +# define PUGI__FN_NO_INLINE PUGI__NO_INLINE +#endif + +// uintptr_t +#if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561) +namespace pugi +{ +# ifndef _UINTPTR_T_DEFINED + typedef size_t uintptr_t; +# endif + + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +} +#else +# include +#endif + +// Memory allocation +PUGI__NS_BEGIN + PUGI__FN void* default_allocate(size_t size) + { + return malloc(size); + } + + PUGI__FN void default_deallocate(void* ptr) + { + free(ptr); + } + + template + struct xml_memory_management_function_storage + { + static allocation_function allocate; + static deallocation_function deallocate; + }; + + // Global allocation functions are stored in class statics so that in header mode linker deduplicates them + // Without a template<> we'll get multiple definitions of the same static + template allocation_function xml_memory_management_function_storage::allocate = default_allocate; + template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; + + typedef xml_memory_management_function_storage xml_memory; +PUGI__NS_END + +// String utilities +PUGI__NS_BEGIN + // Get string length + PUGI__FN size_t strlength(const char_t* s) + { + assert(s); + + #ifdef PUGIXML_WCHAR_MODE + return wcslen(s); + #else + return strlen(s); + #endif + } + + // Compare two strings + PUGI__FN bool strequal(const char_t* src, const char_t* dst) + { + assert(src && dst); + + #ifdef PUGIXML_WCHAR_MODE + return wcscmp(src, dst) == 0; + #else + return strcmp(src, dst) == 0; + #endif + } + + // Compare lhs with [rhs_begin, rhs_end) + PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) + { + for (size_t i = 0; i < count; ++i) + if (lhs[i] != rhs[i]) + return false; + + return lhs[count] == 0; + } + + // Get length of wide string, even if CRT lacks wide character support + PUGI__FN size_t strlength_wide(const wchar_t* s) + { + assert(s); + + #ifdef PUGIXML_WCHAR_MODE + return wcslen(s); + #else + const wchar_t* end = s; + while (*end) end++; + return static_cast(end - s); + #endif + } +PUGI__NS_END + +// auto_ptr-like object for exception recovery +PUGI__NS_BEGIN + template struct auto_deleter + { + typedef void (*D)(T*); + + T* data; + D deleter; + + auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_) + { + } + + ~auto_deleter() + { + if (data) deleter(data); + } + + T* release() + { + T* result = data; + data = 0; + return result; + } + }; +PUGI__NS_END + +#ifdef PUGIXML_COMPACT +PUGI__NS_BEGIN + class compact_hash_table + { + public: + compact_hash_table(): _items(0), _capacity(0), _count(0) + { + } + + void clear() + { + if (_items) + { + xml_memory::deallocate(_items); + _items = 0; + _capacity = 0; + _count = 0; + } + } + + void* find(const void* key) + { + if (_capacity == 0) return 0; + + item_t* item = get_item(key); + assert(item); + assert(item->key == key || (item->key == 0 && item->value == 0)); + + return item->value; + } + + void insert(const void* key, void* value) + { + assert(_capacity != 0 && _count < _capacity - _capacity / 4); + + item_t* item = get_item(key); + assert(item); + + if (item->key == 0) + { + _count++; + item->key = key; + } + + item->value = value; + } + + bool reserve(size_t extra = 16) + { + if (_count + extra >= _capacity - _capacity / 4) + return rehash(_count + extra); + + return true; + } + + private: + struct item_t + { + const void* key; + void* value; + }; + + item_t* _items; + size_t _capacity; + + size_t _count; + + bool rehash(size_t count); + + item_t* get_item(const void* key) + { + assert(key); + assert(_capacity > 0); + + size_t hashmod = _capacity - 1; + size_t bucket = hash(key) & hashmod; + + for (size_t probe = 0; probe <= hashmod; ++probe) + { + item_t& probe_item = _items[bucket]; + + if (probe_item.key == key || probe_item.key == 0) + return &probe_item; + + // hash collision, quadratic probing + bucket = (bucket + probe + 1) & hashmod; + } + + assert(false && "Hash table is full"); // unreachable + return 0; + } + + static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key) + { + unsigned int h = static_cast(reinterpret_cast(key)); + + // MurmurHash3 32-bit finalizer + h ^= h >> 16; + h *= 0x85ebca6bu; + h ^= h >> 13; + h *= 0xc2b2ae35u; + h ^= h >> 16; + + return h; + } + }; + + PUGI__FN_NO_INLINE bool compact_hash_table::rehash(size_t count) + { + size_t capacity = 32; + while (count >= capacity - capacity / 4) + capacity *= 2; + + compact_hash_table rt; + rt._capacity = capacity; + rt._items = static_cast(xml_memory::allocate(sizeof(item_t) * capacity)); + + if (!rt._items) + return false; + + memset(rt._items, 0, sizeof(item_t) * capacity); + + for (size_t i = 0; i < _capacity; ++i) + if (_items[i].key) + rt.insert(_items[i].key, _items[i].value); + + if (_items) + xml_memory::deallocate(_items); + + _capacity = capacity; + _items = rt._items; + + assert(_count == rt._count); + + return true; + } + +PUGI__NS_END +#endif + +PUGI__NS_BEGIN +#ifdef PUGIXML_COMPACT + static const uintptr_t xml_memory_block_alignment = 4; +#else + static const uintptr_t xml_memory_block_alignment = sizeof(void*); +#endif + + // extra metadata bits + static const uintptr_t xml_memory_page_contents_shared_mask = 64; + static const uintptr_t xml_memory_page_name_allocated_mask = 32; + static const uintptr_t xml_memory_page_value_allocated_mask = 16; + static const uintptr_t xml_memory_page_type_mask = 15; + + // combined masks for string uniqueness + static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask; + static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask; + +#ifdef PUGIXML_COMPACT + #define PUGI__GETHEADER_IMPL(object, page, flags) // unused + #define PUGI__GETPAGE_IMPL(header) (header).get_page() +#else + #define PUGI__GETHEADER_IMPL(object, page, flags) (((reinterpret_cast(object) - reinterpret_cast(page)) << 8) | (flags)) + // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings + #define PUGI__GETPAGE_IMPL(header) static_cast(const_cast(static_cast(reinterpret_cast(&header) - (header >> 8)))) +#endif + + #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header) + #define PUGI__NODETYPE(n) static_cast((n)->header & impl::xml_memory_page_type_mask) + + struct xml_allocator; + + struct xml_memory_page + { + static xml_memory_page* construct(void* memory) + { + xml_memory_page* result = static_cast(memory); + + result->allocator = 0; + result->prev = 0; + result->next = 0; + result->busy_size = 0; + result->freed_size = 0; + + #ifdef PUGIXML_COMPACT + result->compact_string_base = 0; + result->compact_shared_parent = 0; + result->compact_page_marker = 0; + #endif + + return result; + } + + xml_allocator* allocator; + + xml_memory_page* prev; + xml_memory_page* next; + + size_t busy_size; + size_t freed_size; + + #ifdef PUGIXML_COMPACT + char_t* compact_string_base; + void* compact_shared_parent; + uint32_t* compact_page_marker; + #endif + }; + + static const size_t xml_memory_page_size = + #ifdef PUGIXML_MEMORY_PAGE_SIZE + (PUGIXML_MEMORY_PAGE_SIZE) + #else + 32768 + #endif + - sizeof(xml_memory_page); + + struct xml_memory_string_header + { + uint16_t page_offset; // offset from page->data + uint16_t full_size; // 0 if string occupies whole page + }; + + struct xml_allocator + { + xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) + { + #ifdef PUGIXML_COMPACT + _hash = 0; + #endif + } + + xml_memory_page* allocate_page(size_t data_size) + { + size_t size = sizeof(xml_memory_page) + data_size; + + // allocate block with some alignment, leaving memory for worst-case padding + void* memory = xml_memory::allocate(size); + if (!memory) return 0; + + // prepare page structure + xml_memory_page* page = xml_memory_page::construct(memory); + assert(page); + + page->allocator = _root->allocator; + + return page; + } + + static void deallocate_page(xml_memory_page* page) + { + xml_memory::deallocate(page); + } + + void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); + + void* allocate_memory(size_t size, xml_memory_page*& out_page) + { + if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size)) + return allocate_memory_oob(size, out_page); + + void* buf = reinterpret_cast(_root) + sizeof(xml_memory_page) + _busy_size; + + _busy_size += size; + + out_page = _root; + + return buf; + } + + #ifdef PUGIXML_COMPACT + void* allocate_object(size_t size, xml_memory_page*& out_page) + { + void* result = allocate_memory(size + sizeof(uint32_t), out_page); + if (!result) return 0; + + // adjust for marker + ptrdiff_t offset = static_cast(result) - reinterpret_cast(out_page->compact_page_marker); + + if (PUGI__UNLIKELY(static_cast(offset) >= 256 * xml_memory_block_alignment)) + { + // insert new marker + uint32_t* marker = static_cast(result); + + *marker = static_cast(reinterpret_cast(marker) - reinterpret_cast(out_page)); + out_page->compact_page_marker = marker; + + // since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block + // this will make sure deallocate_memory correctly tracks the size + out_page->freed_size += sizeof(uint32_t); + + return marker + 1; + } + else + { + // roll back uint32_t part + _busy_size -= sizeof(uint32_t); + + return result; + } + } + #else + void* allocate_object(size_t size, xml_memory_page*& out_page) + { + return allocate_memory(size, out_page); + } + #endif + + void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) + { + if (page == _root) page->busy_size = _busy_size; + + assert(ptr >= reinterpret_cast(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast(page) + sizeof(xml_memory_page) + page->busy_size); + (void)!ptr; + + page->freed_size += size; + assert(page->freed_size <= page->busy_size); + + if (page->freed_size == page->busy_size) + { + if (page->next == 0) + { + assert(_root == page); + + // top page freed, just reset sizes + page->busy_size = 0; + page->freed_size = 0; + + #ifdef PUGIXML_COMPACT + // reset compact state to maximize efficiency + page->compact_string_base = 0; + page->compact_shared_parent = 0; + page->compact_page_marker = 0; + #endif + + _busy_size = 0; + } + else + { + assert(_root != page); + assert(page->prev); + + // remove from the list + page->prev->next = page->next; + page->next->prev = page->prev; + + // deallocate + deallocate_page(page); + } + } + } + + char_t* allocate_string(size_t length) + { + static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment; + + PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); + + // allocate memory for string and header block + size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); + + // round size up to block alignment boundary + size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1); + + xml_memory_page* page; + xml_memory_string_header* header = static_cast(allocate_memory(full_size, page)); + + if (!header) return 0; + + // setup header + ptrdiff_t page_offset = reinterpret_cast(header) - reinterpret_cast(page) - sizeof(xml_memory_page); + + assert(page_offset % xml_memory_block_alignment == 0); + assert(page_offset >= 0 && static_cast(page_offset) < max_encoded_offset); + header->page_offset = static_cast(static_cast(page_offset) / xml_memory_block_alignment); + + // full_size == 0 for large strings that occupy the whole page + assert(full_size % xml_memory_block_alignment == 0); + assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0)); + header->full_size = static_cast(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0); + + // round-trip through void* to avoid 'cast increases required alignment of target type' warning + // header is guaranteed a pointer-sized alignment, which should be enough for char_t + return static_cast(static_cast(header + 1)); + } + + void deallocate_string(char_t* string) + { + // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings + // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string + + // get header + xml_memory_string_header* header = static_cast(static_cast(string)) - 1; + assert(header); + + // deallocate + size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment; + xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); + + // if full_size == 0 then this string occupies the whole page + size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment; + + deallocate_memory(header, full_size, page); + } + + bool reserve() + { + #ifdef PUGIXML_COMPACT + return _hash->reserve(); + #else + return true; + #endif + } + + xml_memory_page* _root; + size_t _busy_size; + + #ifdef PUGIXML_COMPACT + compact_hash_table* _hash; + #endif + }; + + PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) + { + const size_t large_allocation_threshold = xml_memory_page_size / 4; + + xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); + out_page = page; + + if (!page) return 0; + + if (size <= large_allocation_threshold) + { + _root->busy_size = _busy_size; + + // insert page at the end of linked list + page->prev = _root; + _root->next = page; + _root = page; + + _busy_size = size; + } + else + { + // insert page before the end of linked list, so that it is deleted as soon as possible + // the last page is not deleted even if it's empty (see deallocate_memory) + assert(_root->prev); + + page->prev = _root->prev; + page->next = _root; + + _root->prev->next = page; + _root->prev = page; + + page->busy_size = size; + } + + return reinterpret_cast(page) + sizeof(xml_memory_page); + } +PUGI__NS_END + +#ifdef PUGIXML_COMPACT +PUGI__NS_BEGIN + static const uintptr_t compact_alignment_log2 = 2; + static const uintptr_t compact_alignment = 1 << compact_alignment_log2; + + class compact_header + { + public: + compact_header(xml_memory_page* page, unsigned int flags) + { + PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment); + + ptrdiff_t offset = (reinterpret_cast(this) - reinterpret_cast(page->compact_page_marker)); + assert(offset % compact_alignment == 0 && static_cast(offset) < 256 * compact_alignment); + + _page = static_cast(offset >> compact_alignment_log2); + _flags = static_cast(flags); + } + + void operator&=(uintptr_t mod) + { + _flags &= static_cast(mod); + } + + void operator|=(uintptr_t mod) + { + _flags |= static_cast(mod); + } + + uintptr_t operator&(uintptr_t mod) const + { + return _flags & mod; + } + + xml_memory_page* get_page() const + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + const char* page_marker = reinterpret_cast(this) - (_page << compact_alignment_log2); + const char* page = page_marker - *reinterpret_cast(static_cast(page_marker)); + + return const_cast(reinterpret_cast(static_cast(page))); + } + + private: + unsigned char _page; + unsigned char _flags; + }; + + PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset) + { + const compact_header* header = reinterpret_cast(static_cast(object) - header_offset); + + return header->get_page(); + } + + template PUGI__FN_NO_INLINE T* compact_get_value(const void* object) + { + return static_cast(compact_get_page(object, header_offset)->allocator->_hash->find(object)); + } + + template PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value) + { + compact_get_page(object, header_offset)->allocator->_hash->insert(object, value); + } + + template class compact_pointer + { + public: + compact_pointer(): _data(0) + { + } + + void operator=(const compact_pointer& rhs) + { + *this = rhs + 0; + } + + void operator=(T* value) + { + if (value) + { + // value is guaranteed to be compact-aligned; 'this' is not + // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) + // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to + // compensate for arithmetic shift rounding for negative values + ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); + ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start; + + if (static_cast(offset) <= 253) + _data = static_cast(offset + 1); + else + { + compact_set_value(this, value); + + _data = 255; + } + } + else + _data = 0; + } + + operator T*() const + { + if (_data) + { + if (_data < 255) + { + uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); + + return reinterpret_cast(base + (_data - 1 + start) * compact_alignment); + } + else + return compact_get_value(this); + } + else + return 0; + } + + T* operator->() const + { + return *this; + } + + private: + unsigned char _data; + }; + + template class compact_pointer_parent + { + public: + compact_pointer_parent(): _data(0) + { + } + + void operator=(const compact_pointer_parent& rhs) + { + *this = rhs + 0; + } + + void operator=(T* value) + { + if (value) + { + // value is guaranteed to be compact-aligned; 'this' is not + // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) + // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to + // compensate for arithmetic shift behavior for negative values + ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); + ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533; + + if (static_cast(offset) <= 65533) + { + _data = static_cast(offset + 1); + } + else + { + xml_memory_page* page = compact_get_page(this, header_offset); + + if (PUGI__UNLIKELY(page->compact_shared_parent == 0)) + page->compact_shared_parent = value; + + if (page->compact_shared_parent == value) + { + _data = 65534; + } + else + { + compact_set_value(this, value); + + _data = 65535; + } + } + } + else + { + _data = 0; + } + } + + operator T*() const + { + if (_data) + { + if (_data < 65534) + { + uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); + + return reinterpret_cast(base + (_data - 1 - 65533) * compact_alignment); + } + else if (_data == 65534) + return static_cast(compact_get_page(this, header_offset)->compact_shared_parent); + else + return compact_get_value(this); + } + else + return 0; + } + + T* operator->() const + { + return *this; + } + + private: + uint16_t _data; + }; + + template class compact_string + { + public: + compact_string(): _data(0) + { + } + + void operator=(const compact_string& rhs) + { + *this = rhs + 0; + } + + void operator=(char_t* value) + { + if (value) + { + xml_memory_page* page = compact_get_page(this, header_offset); + + if (PUGI__UNLIKELY(page->compact_string_base == 0)) + page->compact_string_base = value; + + ptrdiff_t offset = value - page->compact_string_base; + + if (static_cast(offset) < (65535 << 7)) + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); + + if (*base == 0) + { + *base = static_cast((offset >> 7) + 1); + _data = static_cast((offset & 127) + 1); + } + else + { + ptrdiff_t remainder = offset - ((*base - 1) << 7); + + if (static_cast(remainder) <= 253) + { + _data = static_cast(remainder + 1); + } + else + { + compact_set_value(this, value); + + _data = 255; + } + } + } + else + { + compact_set_value(this, value); + + _data = 255; + } + } + else + { + _data = 0; + } + } + + operator char_t*() const + { + if (_data) + { + if (_data < 255) + { + xml_memory_page* page = compact_get_page(this, header_offset); + + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + const uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); + assert(*base); + + ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1); + + return page->compact_string_base + offset; + } + else + { + return compact_get_value(this); + } + } + else + return 0; + } + + private: + unsigned char _data; + }; +PUGI__NS_END +#endif + +#ifdef PUGIXML_COMPACT +namespace pugi +{ + struct xml_attribute_struct + { + xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0) + { + PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8); + } + + impl::compact_header header; + + uint16_t namevalue_base; + + impl::compact_string<4, 2> name; + impl::compact_string<5, 3> value; + + impl::compact_pointer prev_attribute_c; + impl::compact_pointer next_attribute; + }; + + struct xml_node_struct + { + xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0) + { + PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12); + } + + impl::compact_header header; + + uint16_t namevalue_base; + + impl::compact_string<4, 2> name; + impl::compact_string<5, 3> value; + + impl::compact_pointer_parent parent; + + impl::compact_pointer first_child; + + impl::compact_pointer prev_sibling_c; + impl::compact_pointer next_sibling; + + impl::compact_pointer first_attribute; + }; +} +#else +namespace pugi +{ + struct xml_attribute_struct + { + xml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0) + { + header = PUGI__GETHEADER_IMPL(this, page, 0); + } + + uintptr_t header; + + char_t* name; + char_t* value; + + xml_attribute_struct* prev_attribute_c; + xml_attribute_struct* next_attribute; + }; + + struct xml_node_struct + { + xml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) + { + header = PUGI__GETHEADER_IMPL(this, page, type); + } + + uintptr_t header; + + char_t* name; + char_t* value; + + xml_node_struct* parent; + + xml_node_struct* first_child; + + xml_node_struct* prev_sibling_c; + xml_node_struct* next_sibling; + + xml_attribute_struct* first_attribute; + }; +} +#endif + +PUGI__NS_BEGIN + struct xml_extra_buffer + { + char_t* buffer; + xml_extra_buffer* next; + }; + + struct xml_document_struct: public xml_node_struct, public xml_allocator + { + xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) + { + } + + const char_t* buffer; + + xml_extra_buffer* extra_buffers; + + #ifdef PUGIXML_COMPACT + compact_hash_table hash; + #endif + }; + + template inline xml_allocator& get_allocator(const Object* object) + { + assert(object); + + return *PUGI__GETPAGE(object)->allocator; + } + + template inline xml_document_struct& get_document(const Object* object) + { + assert(object); + + return *static_cast(PUGI__GETPAGE(object)->allocator); + } +PUGI__NS_END + +// Low-level DOM operations +PUGI__NS_BEGIN + inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) + { + xml_memory_page* page; + void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page); + if (!memory) return 0; + + return new (memory) xml_attribute_struct(page); + } + + inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) + { + xml_memory_page* page; + void* memory = alloc.allocate_object(sizeof(xml_node_struct), page); + if (!memory) return 0; + + return new (memory) xml_node_struct(page, type); + } + + inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) + { + if (a->header & impl::xml_memory_page_name_allocated_mask) + alloc.deallocate_string(a->name); + + if (a->header & impl::xml_memory_page_value_allocated_mask) + alloc.deallocate_string(a->value); + + alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a)); + } + + inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) + { + if (n->header & impl::xml_memory_page_name_allocated_mask) + alloc.deallocate_string(n->name); + + if (n->header & impl::xml_memory_page_value_allocated_mask) + alloc.deallocate_string(n->value); + + for (xml_attribute_struct* attr = n->first_attribute; attr; ) + { + xml_attribute_struct* next = attr->next_attribute; + + destroy_attribute(attr, alloc); + + attr = next; + } + + for (xml_node_struct* child = n->first_child; child; ) + { + xml_node_struct* next = child->next_sibling; + + destroy_node(child, alloc); + + child = next; + } + + alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n)); + } + + inline void append_node(xml_node_struct* child, xml_node_struct* node) + { + child->parent = node; + + xml_node_struct* head = node->first_child; + + if (head) + { + xml_node_struct* tail = head->prev_sibling_c; + + tail->next_sibling = child; + child->prev_sibling_c = tail; + head->prev_sibling_c = child; + } + else + { + node->first_child = child; + child->prev_sibling_c = child; + } + } + + inline void prepend_node(xml_node_struct* child, xml_node_struct* node) + { + child->parent = node; + + xml_node_struct* head = node->first_child; + + if (head) + { + child->prev_sibling_c = head->prev_sibling_c; + head->prev_sibling_c = child; + } + else + child->prev_sibling_c = child; + + child->next_sibling = head; + node->first_child = child; + } + + inline void insert_node_after(xml_node_struct* child, xml_node_struct* node) + { + xml_node_struct* parent = node->parent; + + child->parent = parent; + + if (node->next_sibling) + node->next_sibling->prev_sibling_c = child; + else + parent->first_child->prev_sibling_c = child; + + child->next_sibling = node->next_sibling; + child->prev_sibling_c = node; + + node->next_sibling = child; + } + + inline void insert_node_before(xml_node_struct* child, xml_node_struct* node) + { + xml_node_struct* parent = node->parent; + + child->parent = parent; + + if (node->prev_sibling_c->next_sibling) + node->prev_sibling_c->next_sibling = child; + else + parent->first_child = child; + + child->prev_sibling_c = node->prev_sibling_c; + child->next_sibling = node; + + node->prev_sibling_c = child; + } + + inline void remove_node(xml_node_struct* node) + { + xml_node_struct* parent = node->parent; + + if (node->next_sibling) + node->next_sibling->prev_sibling_c = node->prev_sibling_c; + else + parent->first_child->prev_sibling_c = node->prev_sibling_c; + + if (node->prev_sibling_c->next_sibling) + node->prev_sibling_c->next_sibling = node->next_sibling; + else + parent->first_child = node->next_sibling; + + node->parent = 0; + node->prev_sibling_c = 0; + node->next_sibling = 0; + } + + inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node) + { + xml_attribute_struct* head = node->first_attribute; + + if (head) + { + xml_attribute_struct* tail = head->prev_attribute_c; + + tail->next_attribute = attr; + attr->prev_attribute_c = tail; + head->prev_attribute_c = attr; + } + else + { + node->first_attribute = attr; + attr->prev_attribute_c = attr; + } + } + + inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node) + { + xml_attribute_struct* head = node->first_attribute; + + if (head) + { + attr->prev_attribute_c = head->prev_attribute_c; + head->prev_attribute_c = attr; + } + else + attr->prev_attribute_c = attr; + + attr->next_attribute = head; + node->first_attribute = attr; + } + + inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) + { + if (place->next_attribute) + place->next_attribute->prev_attribute_c = attr; + else + node->first_attribute->prev_attribute_c = attr; + + attr->next_attribute = place->next_attribute; + attr->prev_attribute_c = place; + place->next_attribute = attr; + } + + inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) + { + if (place->prev_attribute_c->next_attribute) + place->prev_attribute_c->next_attribute = attr; + else + node->first_attribute = attr; + + attr->prev_attribute_c = place->prev_attribute_c; + attr->next_attribute = place; + place->prev_attribute_c = attr; + } + + inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) + { + if (attr->next_attribute) + attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; + else + node->first_attribute->prev_attribute_c = attr->prev_attribute_c; + + if (attr->prev_attribute_c->next_attribute) + attr->prev_attribute_c->next_attribute = attr->next_attribute; + else + node->first_attribute = attr->next_attribute; + + attr->prev_attribute_c = 0; + attr->next_attribute = 0; + } + + PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) + { + if (!alloc.reserve()) return 0; + + xml_node_struct* child = allocate_node(alloc, type); + if (!child) return 0; + + append_node(child, node); + + return child; + } + + PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) + { + if (!alloc.reserve()) return 0; + + xml_attribute_struct* attr = allocate_attribute(alloc); + if (!attr) return 0; + + append_attribute(attr, node); + + return attr; + } +PUGI__NS_END + +// Helper classes for code generation +PUGI__NS_BEGIN + struct opt_false + { + enum { value = 0 }; + }; + + struct opt_true + { + enum { value = 1 }; + }; +PUGI__NS_END + +// Unicode utilities +PUGI__NS_BEGIN + inline uint16_t endian_swap(uint16_t value) + { + return static_cast(((value & 0xff) << 8) | (value >> 8)); + } + + inline uint32_t endian_swap(uint32_t value) + { + return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); + } + + struct utf8_counter + { + typedef size_t value_type; + + static value_type low(value_type result, uint32_t ch) + { + // U+0000..U+007F + if (ch < 0x80) return result + 1; + // U+0080..U+07FF + else if (ch < 0x800) return result + 2; + // U+0800..U+FFFF + else return result + 3; + } + + static value_type high(value_type result, uint32_t) + { + // U+10000..U+10FFFF + return result + 4; + } + }; + + struct utf8_writer + { + typedef uint8_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + // U+0000..U+007F + if (ch < 0x80) + { + *result = static_cast(ch); + return result + 1; + } + // U+0080..U+07FF + else if (ch < 0x800) + { + result[0] = static_cast(0xC0 | (ch >> 6)); + result[1] = static_cast(0x80 | (ch & 0x3F)); + return result + 2; + } + // U+0800..U+FFFF + else + { + result[0] = static_cast(0xE0 | (ch >> 12)); + result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + result[2] = static_cast(0x80 | (ch & 0x3F)); + return result + 3; + } + } + + static value_type high(value_type result, uint32_t ch) + { + // U+10000..U+10FFFF + result[0] = static_cast(0xF0 | (ch >> 18)); + result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); + result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + result[3] = static_cast(0x80 | (ch & 0x3F)); + return result + 4; + } + + static value_type any(value_type result, uint32_t ch) + { + return (ch < 0x10000) ? low(result, ch) : high(result, ch); + } + }; + + struct utf16_counter + { + typedef size_t value_type; + + static value_type low(value_type result, uint32_t) + { + return result + 1; + } + + static value_type high(value_type result, uint32_t) + { + return result + 2; + } + }; + + struct utf16_writer + { + typedef uint16_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + *result = static_cast(ch); + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + uint32_t msh = static_cast(ch - 0x10000) >> 10; + uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; + + result[0] = static_cast(0xD800 + msh); + result[1] = static_cast(0xDC00 + lsh); + + return result + 2; + } + + static value_type any(value_type result, uint32_t ch) + { + return (ch < 0x10000) ? low(result, ch) : high(result, ch); + } + }; + + struct utf32_counter + { + typedef size_t value_type; + + static value_type low(value_type result, uint32_t) + { + return result + 1; + } + + static value_type high(value_type result, uint32_t) + { + return result + 1; + } + }; + + struct utf32_writer + { + typedef uint32_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + *result = ch; + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + *result = ch; + + return result + 1; + } + + static value_type any(value_type result, uint32_t ch) + { + *result = ch; + + return result + 1; + } + }; + + struct latin1_writer + { + typedef uint8_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + *result = static_cast(ch > 255 ? '?' : ch); + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + (void)ch; + + *result = '?'; + + return result + 1; + } + }; + + struct utf8_decoder + { + typedef uint8_t type; + + template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) + { + const uint8_t utf8_byte_mask = 0x3f; + + while (size) + { + uint8_t lead = *data; + + // 0xxxxxxx -> U+0000..U+007F + if (lead < 0x80) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + + // process aligned single-byte (ascii) blocks + if ((reinterpret_cast(data) & 3) == 0) + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0) + { + result = Traits::low(result, data[0]); + result = Traits::low(result, data[1]); + result = Traits::low(result, data[2]); + result = Traits::low(result, data[3]); + data += 4; + size -= 4; + } + } + } + // 110xxxxx -> U+0080..U+07FF + else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) + { + result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); + data += 2; + size -= 2; + } + // 1110xxxx -> U+0800-U+FFFF + else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) + { + result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); + data += 3; + size -= 3; + } + // 11110xxx -> U+10000..U+10FFFF + else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) + { + result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); + data += 4; + size -= 4; + } + // 10xxxxxx or 11111xxx -> invalid + else + { + data += 1; + size -= 1; + } + } + + return result; + } + }; + + template struct utf16_decoder + { + typedef uint16_t type; + + template static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) + { + uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+D7FF + if (lead < 0xD800) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + } + // U+E000..U+FFFF + else if (static_cast(lead - 0xE000) < 0x2000) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + } + // surrogate pair lead + else if (static_cast(lead - 0xD800) < 0x400 && size >= 2) + { + uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; + + if (static_cast(next - 0xDC00) < 0x400) + { + result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); + data += 2; + size -= 2; + } + else + { + data += 1; + size -= 1; + } + } + else + { + data += 1; + size -= 1; + } + } + + return result; + } + }; + + template struct utf32_decoder + { + typedef uint32_t type; + + template static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) + { + uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+FFFF + if (lead < 0x10000) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + } + // U+10000..U+10FFFF + else + { + result = Traits::high(result, lead); + data += 1; + size -= 1; + } + } + + return result; + } + }; + + struct latin1_decoder + { + typedef uint8_t type; + + template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) + { + result = Traits::low(result, *data); + data += 1; + size -= 1; + } + + return result; + } + }; + + template struct wchar_selector; + + template <> struct wchar_selector<2> + { + typedef uint16_t type; + typedef utf16_counter counter; + typedef utf16_writer writer; + typedef utf16_decoder decoder; + }; + + template <> struct wchar_selector<4> + { + typedef uint32_t type; + typedef utf32_counter counter; + typedef utf32_writer writer; + typedef utf32_decoder decoder; + }; + + typedef wchar_selector::counter wchar_counter; + typedef wchar_selector::writer wchar_writer; + + struct wchar_decoder + { + typedef wchar_t type; + + template static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits) + { + typedef wchar_selector::decoder decoder; + + return decoder::process(reinterpret_cast(data), size, result, traits); + } + }; + +#ifdef PUGIXML_WCHAR_MODE + PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) + { + for (size_t i = 0; i < length; ++i) + result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); + } +#endif +PUGI__NS_END + +PUGI__NS_BEGIN + enum chartype_t + { + ct_parse_pcdata = 1, // \0, &, \r, < + ct_parse_attr = 2, // \0, &, \r, ', " + ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab + ct_space = 8, // \r, \n, space, tab + ct_parse_cdata = 16, // \0, ], >, \r + ct_parse_comment = 32, // \0, -, >, \r + ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . + ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : + }; + + static const unsigned char chartype_table[256] = + { + 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 + 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 + 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 + 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 + + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 + }; + + enum chartypex_t + { + ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > + ctx_special_attr = 2, // Any symbol >= 0 and < 32, &, <, ", ' + ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ + ctx_digit = 8, // 0-9 + ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . + }; + + static const unsigned char chartypex_table[256] = + { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, // 0-15 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 + 0, 0, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 1, 0, // 48-63 + + 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 + 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 + + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 + }; + +#ifdef PUGIXML_WCHAR_MODE + #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) +#else + #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) +#endif + + #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) + #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) + + PUGI__FN bool is_little_endian() + { + unsigned int ui = 1; + + return *reinterpret_cast(&ui) == 1; + } + + PUGI__FN xml_encoding get_wchar_encoding() + { + PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); + + if (sizeof(wchar_t) == 2) + return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + else + return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + } + + PUGI__FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length) + { + #define PUGI__SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; } + #define PUGI__SCANCHARTYPE(ct) { while (offset < size && PUGI__IS_CHARTYPE(data[offset], ct)) offset++; } + + // check if we have a non-empty XML declaration + if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI__IS_CHARTYPE(data[5], ct_space))) + return false; + + // scan XML declaration until the encoding field + for (size_t i = 6; i + 1 < size; ++i) + { + // declaration can not contain ? in quoted values + if (data[i] == '?') + return false; + + if (data[i] == 'e' && data[i + 1] == 'n') + { + size_t offset = i; + + // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed + PUGI__SCANCHAR('e'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('c'); PUGI__SCANCHAR('o'); + PUGI__SCANCHAR('d'); PUGI__SCANCHAR('i'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('g'); + + // S? = S? + PUGI__SCANCHARTYPE(ct_space); + PUGI__SCANCHAR('='); + PUGI__SCANCHARTYPE(ct_space); + + // the only two valid delimiters are ' and " + uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\''; + + PUGI__SCANCHAR(delimiter); + + size_t start = offset; + + out_encoding = data + offset; + + PUGI__SCANCHARTYPE(ct_symbol); + + out_length = offset - start; + + PUGI__SCANCHAR(delimiter); + + return true; + } + } + + return false; + + #undef PUGI__SCANCHAR + #undef PUGI__SCANCHARTYPE + } + + PUGI__FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size) + { + // skip encoding autodetection if input buffer is too small + if (size < 4) return encoding_utf8; + + uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; + + // look for BOM in first few bytes + if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; + if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; + if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be; + if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le; + if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8; + + // look for <, (contents); + + return guess_buffer_encoding(data, size); + } + + PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + size_t length = size / sizeof(char_t); + + if (is_mutable) + { + out_buffer = static_cast(const_cast(contents)); + out_length = length; + } + else + { + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + if (contents) + memcpy(buffer, contents, length * sizeof(char_t)); + else + assert(length == 0); + + buffer[length] = 0; + + out_buffer = buffer; + out_length = length + 1; + } + + return true; + } + +#ifdef PUGIXML_WCHAR_MODE + PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) + { + return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || + (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); + } + + PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + const char_t* data = static_cast(contents); + size_t length = size / sizeof(char_t); + + if (is_mutable) + { + char_t* buffer = const_cast(data); + + convert_wchar_endian_swap(buffer, data, length); + + out_buffer = buffer; + out_length = length; + } + else + { + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + convert_wchar_endian_swap(buffer, data, length); + buffer[length] = 0; + + out_buffer = buffer; + out_length = length + 1; + } + + return true; + } + + template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) + { + const typename D::type* data = static_cast(contents); + size_t data_length = size / sizeof(typename D::type); + + // first pass: get length in wchar_t units + size_t length = D::process(data, data_length, 0, wchar_counter()); + + // allocate buffer of suitable length + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + // second pass: convert utf16 input to wchar_t + wchar_writer::value_type obegin = reinterpret_cast(buffer); + wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer()); + + assert(oend == obegin + length); + *oend = 0; + + out_buffer = buffer; + out_length = length + 1; + + return true; + } + + PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) + { + // get native encoding + xml_encoding wchar_encoding = get_wchar_encoding(); + + // fast path: no conversion required + if (encoding == wchar_encoding) + return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + + // only endian-swapping is required + if (need_endian_swap_utf(encoding, wchar_encoding)) + return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); + + // source encoding is utf8 + if (encoding == encoding_utf8) + return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder()); + + // source encoding is utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); + } + + // source encoding is utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); + } + + // source encoding is latin1 + if (encoding == encoding_latin1) + return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder()); + + assert(false && "Invalid encoding"); // unreachable + return false; + } +#else + template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) + { + const typename D::type* data = static_cast(contents); + size_t data_length = size / sizeof(typename D::type); + + // first pass: get length in utf8 units + size_t length = D::process(data, data_length, 0, utf8_counter()); + + // allocate buffer of suitable length + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + // second pass: convert utf16 input to utf8 + uint8_t* obegin = reinterpret_cast(buffer); + uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); + + assert(oend == obegin + length); + *oend = 0; + + out_buffer = buffer; + out_length = length + 1; + + return true; + } + + PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) + { + for (size_t i = 0; i < size; ++i) + if (data[i] > 127) + return i; + + return size; + } + + PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + const uint8_t* data = static_cast(contents); + size_t data_length = size; + + // get size of prefix that does not need utf8 conversion + size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); + assert(prefix_length <= data_length); + + const uint8_t* postfix = data + prefix_length; + size_t postfix_length = data_length - prefix_length; + + // if no conversion is needed, just return the original buffer + if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + + // first pass: get length in utf8 units + size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter()); + + // allocate buffer of suitable length + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + // second pass: convert latin1 input to utf8 + memcpy(buffer, data, prefix_length); + + uint8_t* obegin = reinterpret_cast(buffer); + uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer()); + + assert(oend == obegin + length); + *oend = 0; + + out_buffer = buffer; + out_length = length + 1; + + return true; + } + + PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) + { + // fast path: no conversion required + if (encoding == encoding_utf8) + return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + + // source encoding is utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); + } + + // source encoding is utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); + } + + // source encoding is latin1 + if (encoding == encoding_latin1) + return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); + + assert(false && "Invalid encoding"); // unreachable + return false; + } +#endif + + PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) + { + // get length in utf8 characters + return wchar_decoder::process(str, length, 0, utf8_counter()); + } + + PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) + { + // convert to utf8 + uint8_t* begin = reinterpret_cast(buffer); + uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer()); + + assert(begin + size == end); + (void)!end; + (void)!size; + } + +#ifndef PUGIXML_NO_STL + PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) + { + // first pass: get length in utf8 characters + size_t size = as_utf8_begin(str, length); + + // allocate resulting string + std::string result; + result.resize(size); + + // second pass: convert to utf8 + if (size > 0) as_utf8_end(&result[0], size, str, length); + + return result; + } + + PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size) + { + const uint8_t* data = reinterpret_cast(str); + + // first pass: get length in wchar_t units + size_t length = utf8_decoder::process(data, size, 0, wchar_counter()); + + // allocate resulting string + std::basic_string result; + result.resize(length); + + // second pass: convert to wchar_t + if (length > 0) + { + wchar_writer::value_type begin = reinterpret_cast(&result[0]); + wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer()); + + assert(begin + length == end); + (void)!end; + } + + return result; + } +#endif + + template + inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target) + { + // never reuse shared memory + if (header & xml_memory_page_contents_shared_mask) return false; + + size_t target_length = strlength(target); + + // always reuse document buffer memory if possible + if ((header & header_mask) == 0) return target_length >= length; + + // reuse heap memory if waste is not too great + const size_t reuse_threshold = 32; + + return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); + } + + template + PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) + { + if (source_length == 0) + { + // empty string and null pointer are equivalent, so just deallocate old memory + xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; + + if (header & header_mask) alloc->deallocate_string(dest); + + // mark the string as not allocated + dest = 0; + header &= ~header_mask; + + return true; + } + else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest)) + { + // we can reuse old buffer, so just copy the new data (including zero terminator) + memcpy(dest, source, source_length * sizeof(char_t)); + dest[source_length] = 0; + + return true; + } + else + { + xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; + + if (!alloc->reserve()) return false; + + // allocate new buffer + char_t* buf = alloc->allocate_string(source_length + 1); + if (!buf) return false; + + // copy the string (including zero terminator) + memcpy(buf, source, source_length * sizeof(char_t)); + buf[source_length] = 0; + + // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) + if (header & header_mask) alloc->deallocate_string(dest); + + // the string is now allocated, so set the flag + dest = buf; + header |= header_mask; + + return true; + } + } + + struct gap + { + char_t* end; + size_t size; + + gap(): end(0), size(0) + { + } + + // Push new gap, move s count bytes further (skipping the gap). + // Collapse previous gap. + void push(char_t*& s, size_t count) + { + if (end) // there was a gap already; collapse it + { + // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) + assert(s >= end); + memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); + } + + s += count; // end of current gap + + // "merge" two gaps + end = s; + size += count; + } + + // Collapse all gaps, return past-the-end pointer + char_t* flush(char_t* s) + { + if (end) + { + // Move [old_gap_end, current_pos) to [old_gap_start, ...) + assert(s >= end); + memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); + + return s - size; + } + else return s; + } + }; + + PUGI__FN char_t* strconv_escape(char_t* s, gap& g) + { + char_t* stre = s + 1; + + switch (*stre) + { + case '#': // &#... + { + unsigned int ucsc = 0; + + if (stre[1] == 'x') // &#x... (hex code) + { + stre += 2; + + char_t ch = *stre; + + if (ch == ';') return stre; + + for (;;) + { + if (static_cast(ch - '0') <= 9) + ucsc = 16 * ucsc + (ch - '0'); + else if (static_cast((ch | ' ') - 'a') <= 5) + ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); + else if (ch == ';') + break; + else // cancel + return stre; + + ch = *++stre; + } + + ++stre; + } + else // &#... (dec code) + { + char_t ch = *++stre; + + if (ch == ';') return stre; + + for (;;) + { + if (static_cast(ch - '0') <= 9) + ucsc = 10 * ucsc + (ch - '0'); + else if (ch == ';') + break; + else // cancel + return stre; + + ch = *++stre; + } + + ++stre; + } + + #ifdef PUGIXML_WCHAR_MODE + s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); + #else + s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); + #endif + + g.push(s, stre - s); + return stre; + } + + case 'a': // &a + { + ++stre; + + if (*stre == 'm') // &am + { + if (*++stre == 'p' && *++stre == ';') // & + { + *s++ = '&'; + ++stre; + + g.push(s, stre - s); + return stre; + } + } + else if (*stre == 'p') // &ap + { + if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' + { + *s++ = '\''; + ++stre; + + g.push(s, stre - s); + return stre; + } + } + break; + } + + case 'g': // &g + { + if (*++stre == 't' && *++stre == ';') // > + { + *s++ = '>'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + case 'l': // &l + { + if (*++stre == 't' && *++stre == ';') // < + { + *s++ = '<'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + case 'q': // &q + { + if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " + { + *s++ = '"'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + default: + break; + } + + return stre; + } + + // Parser utilities + #define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) + #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } + #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) + #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } + #define PUGI__POPNODE() { cursor = cursor->parent; } + #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } + #define PUGI__SCANWHILE(X) { while (X) ++s; } + #define PUGI__SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } } + #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } + #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) + #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } + + PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment)); + + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair + { + *s++ = '\n'; // replace first one with 0x0a + + if (*s == '\n') g.push(s, 1); + } + else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here + { + *g.flush(s) = 0; + + return s + (s[2] == '>' ? 3 : 2); + } + else if (*s == 0) + { + return 0; + } + else ++s; + } + } + + PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata)); + + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair + { + *s++ = '\n'; // replace first one with 0x0a + + if (*s == '\n') g.push(s, 1); + } + else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here + { + *g.flush(s) = 0; + + return s + 1; + } + else if (*s == 0) + { + return 0; + } + else ++s; + } + } + + typedef char_t* (*strconv_pcdata_t)(char_t*); + + template struct strconv_pcdata_impl + { + static char_t* parse(char_t* s) + { + gap g; + + char_t* begin = s; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata)); + + if (*s == '<') // PCDATA ends here + { + char_t* end = g.flush(s); + + if (opt_trim::value) + while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) + --end; + + *end = 0; + + return s + 1; + } + else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair + { + *s++ = '\n'; // replace first one with 0x0a + + if (*s == '\n') g.push(s, 1); + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (*s == 0) + { + char_t* end = g.flush(s); + + if (opt_trim::value) + while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) + --end; + + *end = 0; + + return s; + } + else ++s; + } + } + }; + + PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) + { + PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); + + switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above + { + case 0: return strconv_pcdata_impl::parse; + case 1: return strconv_pcdata_impl::parse; + case 2: return strconv_pcdata_impl::parse; + case 3: return strconv_pcdata_impl::parse; + case 4: return strconv_pcdata_impl::parse; + case 5: return strconv_pcdata_impl::parse; + case 6: return strconv_pcdata_impl::parse; + case 7: return strconv_pcdata_impl::parse; + default: assert(false); return 0; // unreachable + } + } + + typedef char_t* (*strconv_attribute_t)(char_t*, char_t); + + template struct strconv_attribute_impl + { + static char_t* parse_wnorm(char_t* s, char_t end_quote) + { + gap g; + + // trim leading whitespaces + if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s; + + do ++str; + while (PUGI__IS_CHARTYPE(*str, ct_space)); + + g.push(s, str - s); + } + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); + + if (*s == end_quote) + { + char_t* str = g.flush(s); + + do *str-- = 0; + while (PUGI__IS_CHARTYPE(*str, ct_space)); + + return s + 1; + } + else if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + *s++ = ' '; + + if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s + 1; + while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; + + g.push(s, str - s); + } + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + + static char_t* parse_wconv(char_t* s, char_t end_quote) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws)); + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + if (*s == '\r') + { + *s++ = ' '; + + if (*s == '\n') g.push(s, 1); + } + else *s++ = ' '; + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + + static char_t* parse_eol(char_t* s, char_t end_quote) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (*s == '\r') + { + *s++ = '\n'; + + if (*s == '\n') g.push(s, 1); + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + + static char_t* parse_simple(char_t* s, char_t end_quote) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + }; + + PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) + { + PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); + + switch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above + { + case 0: return strconv_attribute_impl::parse_simple; + case 1: return strconv_attribute_impl::parse_simple; + case 2: return strconv_attribute_impl::parse_eol; + case 3: return strconv_attribute_impl::parse_eol; + case 4: return strconv_attribute_impl::parse_wconv; + case 5: return strconv_attribute_impl::parse_wconv; + case 6: return strconv_attribute_impl::parse_wconv; + case 7: return strconv_attribute_impl::parse_wconv; + case 8: return strconv_attribute_impl::parse_wnorm; + case 9: return strconv_attribute_impl::parse_wnorm; + case 10: return strconv_attribute_impl::parse_wnorm; + case 11: return strconv_attribute_impl::parse_wnorm; + case 12: return strconv_attribute_impl::parse_wnorm; + case 13: return strconv_attribute_impl::parse_wnorm; + case 14: return strconv_attribute_impl::parse_wnorm; + case 15: return strconv_attribute_impl::parse_wnorm; + default: assert(false); return 0; // unreachable + } + } + + inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) + { + xml_parse_result result; + result.status = status; + result.offset = offset; + + return result; + } + + struct xml_parser + { + xml_allocator* alloc; + char_t* error_offset; + xml_parse_status error_status; + + xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) + { + } + + // DOCTYPE consists of nested sections of the following possible types: + // , , "...", '...' + // + // + // First group can not contain nested groups + // Second group can contain nested groups of the same type + // Third group can contain all other groups + char_t* parse_doctype_primitive(char_t* s) + { + if (*s == '"' || *s == '\'') + { + // quoted string + char_t ch = *s++; + PUGI__SCANFOR(*s == ch); + if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + + s++; + } + else if (s[0] == '<' && s[1] == '?') + { + // + s += 2; + PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype + if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + + s += 2; + } + else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') + { + s += 4; + PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype + if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + + s += 3; + } + else PUGI__THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_doctype_ignore(char_t* s) + { + size_t depth = 0; + + assert(s[0] == '<' && s[1] == '!' && s[2] == '['); + s += 3; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] == '[') + { + // nested ignore section + s += 3; + depth++; + } + else if (s[0] == ']' && s[1] == ']' && s[2] == '>') + { + // ignore section end + s += 3; + + if (depth == 0) + return s; + + depth--; + } + else s++; + } + + PUGI__THROW_ERROR(status_bad_doctype, s); + } + + char_t* parse_doctype_group(char_t* s, char_t endch) + { + size_t depth = 0; + + assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); + s += 2; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] != '-') + { + if (s[2] == '[') + { + // ignore + s = parse_doctype_ignore(s); + if (!s) return s; + } + else + { + // some control group + s += 2; + depth++; + } + } + else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') + { + // unknown tag (forbidden), or some primitive group + s = parse_doctype_primitive(s); + if (!s) return s; + } + else if (*s == '>') + { + if (depth == 0) + return s; + + depth--; + s++; + } + else s++; + } + + if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) + { + // parse node contents, starting with exclamation mark + ++s; + + if (*s == '-') // 'value = s; // Save the offset. + } + + if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) + { + s = strconv_comment(s, endch); + + if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); + } + else + { + // Scan for terminating '-->'. + PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')); + PUGI__CHECK_ERROR(status_bad_comment, s); + + if (PUGI__OPTSET(parse_comments)) + *s = 0; // Zero-terminate this segment at the first terminating '-'. + + s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. + } + } + else PUGI__THROW_ERROR(status_bad_comment, s); + } + else if (*s == '[') + { + // 'value = s; // Save the offset. + + if (PUGI__OPTSET(parse_eol)) + { + s = strconv_cdata(s, endch); + + if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); + } + else + { + // Scan for terminating ']]>'. + PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); + PUGI__CHECK_ERROR(status_bad_cdata, s); + + *s++ = 0; // Zero-terminate this segment. + } + } + else // Flagged for discard, but we still have to scan for the terminator. + { + // Scan for terminating ']]>'. + PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); + PUGI__CHECK_ERROR(status_bad_cdata, s); + + ++s; + } + + s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. + } + else PUGI__THROW_ERROR(status_bad_cdata, s); + } + else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E')) + { + s -= 2; + + if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s); + + char_t* mark = s + 9; + + s = parse_doctype_group(s, endch); + if (!s) return s; + + assert((*s == 0 && endch == '>') || *s == '>'); + if (*s) *s++ = 0; + + if (PUGI__OPTSET(parse_doctype)) + { + while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; + + PUGI__PUSHNODE(node_doctype); + + cursor->value = mark; + } + } + else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); + else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); + else PUGI__THROW_ERROR(status_unrecognized_tag, s); + + return s; + } + + char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) + { + // load into registers + xml_node_struct* cursor = ref_cursor; + char_t ch = 0; + + // parse node contents, starting with question mark + ++s; + + // read PI target + char_t* target = s; + + if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); + + PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); + PUGI__CHECK_ERROR(status_bad_pi, s); + + // determine node type; stricmp / strcasecmp is not portable + bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; + + if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) + { + if (declaration) + { + // disallow non top-level declarations + if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); + + PUGI__PUSHNODE(node_declaration); + } + else + { + PUGI__PUSHNODE(node_pi); + } + + cursor->name = target; + + PUGI__ENDSEG(); + + // parse value/attributes + if (ch == '?') + { + // empty node + if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); + s += (*s == '>'); + + PUGI__POPNODE(); + } + else if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + PUGI__SKIPWS(); + + // scan for tag end + char_t* value = s; + + PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); + PUGI__CHECK_ERROR(status_bad_pi, s); + + if (declaration) + { + // replace ending ? with / so that 'element' terminates properly + *s = '/'; + + // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES + s = value; + } + else + { + // store value and step over > + cursor->value = value; + + PUGI__POPNODE(); + + PUGI__ENDSEG(); + + s += (*s == '>'); + } + } + else PUGI__THROW_ERROR(status_bad_pi, s); + } + else + { + // scan for tag end + PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); + PUGI__CHECK_ERROR(status_bad_pi, s); + + s += (s[1] == '>' ? 2 : 1); + } + + // store from registers + ref_cursor = cursor; + + return s; + } + + char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch) + { + strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); + strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); + + char_t ch = 0; + xml_node_struct* cursor = root; + char_t* mark = s; + + while (*s != 0) + { + if (*s == '<') + { + ++s; + + LOC_TAG: + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' + { + PUGI__PUSHNODE(node_element); // Append a new node to the tree. + + cursor->name = s; + + PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. + PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. + + if (ch == '>') + { + // end of tag + } + else if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + LOC_ATTRIBUTES: + while (true) + { + PUGI__SKIPWS(); // Eat any whitespace. + + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... + { + xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute. + if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); + + a->name = s; // Save the offset. + + PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. + PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. + + if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + PUGI__SKIPWS(); // Eat any whitespace. + + ch = *s; + ++s; + } + + if (ch == '=') // '<... #=...' + { + PUGI__SKIPWS(); // Eat any whitespace. + + if (*s == '"' || *s == '\'') // '<... #="...' + { + ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. + ++s; // Step over the quote. + a->value = s; // Save the offset. + + s = strconv_attribute(s, ch); + + if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); + + // After this line the loop continues from the start; + // Whitespaces, / and > are ok, symbols and EOF are wrong, + // everything else will be detected + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); + } + else PUGI__THROW_ERROR(status_bad_attribute, s); + } + else PUGI__THROW_ERROR(status_bad_attribute, s); + } + else if (*s == '/') + { + ++s; + + if (*s == '>') + { + PUGI__POPNODE(); + s++; + break; + } + else if (*s == 0 && endch == '>') + { + PUGI__POPNODE(); + break; + } + else PUGI__THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '>') + { + ++s; + + break; + } + else if (*s == 0 && endch == '>') + { + break; + } + else PUGI__THROW_ERROR(status_bad_start_element, s); + } + + // !!! + } + else if (ch == '/') // '<#.../' + { + if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); + + PUGI__POPNODE(); // Pop. + + s += (*s == '>'); + } + else if (ch == 0) + { + // we stepped over null terminator, backtrack & handle closing tag + --s; + + if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); + } + else PUGI__THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '/') + { + ++s; + + mark = s; + + char_t* name = cursor->name; + if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark); + + while (PUGI__IS_CHARTYPE(*s, ct_symbol)) + { + if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark); + } + + if (*name) + { + if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); + else PUGI__THROW_ERROR(status_end_element_mismatch, mark); + } + + PUGI__POPNODE(); // Pop. + + PUGI__SKIPWS(); + + if (*s == 0) + { + if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); + } + else + { + if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); + ++s; + } + } + else if (*s == '?') // 'first_child) continue; + } + } + + if (!PUGI__OPTSET(parse_trim_pcdata)) + s = mark; + + if (cursor->parent || PUGI__OPTSET(parse_fragment)) + { + if (PUGI__OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value) + { + cursor->value = s; // Save the offset. + } + else + { + PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. + + cursor->value = s; // Save the offset. + + PUGI__POPNODE(); // Pop since this is a standalone. + } + + s = strconv_pcdata(s); + + if (!*s) break; + } + else + { + PUGI__SCANFOR(*s == '<'); // '...<' + if (!*s) break; + + ++s; + } + + // We're after '<' + goto LOC_TAG; + } + } + + // check that last tag is closed + if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s); + + return s; + } + + #ifdef PUGIXML_WCHAR_MODE + static char_t* parse_skip_bom(char_t* s) + { + unsigned int bom = 0xfeff; + return (s[0] == static_cast(bom)) ? s + 1 : s; + } + #else + static char_t* parse_skip_bom(char_t* s) + { + return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; + } + #endif + + static bool has_element_node_siblings(xml_node_struct* node) + { + while (node) + { + if (PUGI__NODETYPE(node) == node_element) return true; + + node = node->next_sibling; + } + + return false; + } + + static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) + { + // early-out for empty documents + if (length == 0) + return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element); + + // get last child of the root before parsing + xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0; + + // create parser on stack + xml_parser parser(static_cast(xmldoc)); + + // save last character and make buffer zero-terminated (speeds up parsing) + char_t endch = buffer[length - 1]; + buffer[length - 1] = 0; + + // skip BOM to make sure it does not end up as part of parse output + char_t* buffer_data = parse_skip_bom(buffer); + + // perform actual parsing + parser.parse_tree(buffer_data, root, optmsk, endch); + + xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); + assert(result.offset >= 0 && static_cast(result.offset) <= length); + + if (result) + { + // since we removed last character, we have to handle the only possible false positive (stray <) + if (endch == '<') + return make_parse_result(status_unrecognized_tag, length - 1); + + // check if there are any element nodes parsed + xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child+ 0; + + if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) + return make_parse_result(status_no_document_element, length - 1); + } + else + { + // roll back offset if it occurs on a null terminator in the source buffer + if (result.offset > 0 && static_cast(result.offset) == length - 1 && endch == 0) + result.offset--; + } + + return result; + } + }; + + // Output facilities + PUGI__FN xml_encoding get_write_native_encoding() + { + #ifdef PUGIXML_WCHAR_MODE + return get_wchar_encoding(); + #else + return encoding_utf8; + #endif + } + + PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) + { + // replace wchar encoding with utf implementation + if (encoding == encoding_wchar) return get_wchar_encoding(); + + // replace utf16 encoding with utf16 with specific endianness + if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + // replace utf32 encoding with utf32 with specific endianness + if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + // only do autodetection if no explicit encoding is requested + if (encoding != encoding_auto) return encoding; + + // assume utf8 encoding + return encoding_utf8; + } + + template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) + { + PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + + typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); + + return static_cast(end - dest) * sizeof(*dest); + } + + template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap) + { + PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + + typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); + + if (opt_swap) + { + for (typename T::value_type i = dest; i != end; ++i) + *i = endian_swap(*i); + } + + return static_cast(end - dest) * sizeof(*dest); + } + +#ifdef PUGIXML_WCHAR_MODE + PUGI__FN size_t get_valid_length(const char_t* data, size_t length) + { + if (length < 1) return 0; + + // discard last character if it's the lead of a surrogate pair + return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; + } + + PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) + { + // only endian-swapping is required + if (need_endian_swap_utf(encoding, get_wchar_encoding())) + { + convert_wchar_endian_swap(r_char, data, length); + + return length * sizeof(char_t); + } + + // convert to utf8 + if (encoding == encoding_utf8) + return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer()); + + // convert to utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding); + } + + // convert to utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding); + } + + // convert to latin1 + if (encoding == encoding_latin1) + return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer()); + + assert(false && "Invalid encoding"); // unreachable + return 0; + } +#else + PUGI__FN size_t get_valid_length(const char_t* data, size_t length) + { + if (length < 5) return 0; + + for (size_t i = 1; i <= 4; ++i) + { + uint8_t ch = static_cast(data[length - i]); + + // either a standalone character or a leading one + if ((ch & 0xc0) != 0x80) return length - i; + } + + // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk + return length; + } + + PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) + { + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding); + } + + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding); + } + + if (encoding == encoding_latin1) + return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer()); + + assert(false && "Invalid encoding"); // unreachable + return 0; + } +#endif + + class xml_buffered_writer + { + xml_buffered_writer(const xml_buffered_writer&); + xml_buffered_writer& operator=(const xml_buffered_writer&); + + public: + xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) + { + PUGI__STATIC_ASSERT(bufcapacity >= 8); + } + + size_t flush() + { + flush(buffer, bufsize); + bufsize = 0; + return 0; + } + + void flush(const char_t* data, size_t size) + { + if (size == 0) return; + + // fast path, just write data + if (encoding == get_write_native_encoding()) + writer.write(data, size * sizeof(char_t)); + else + { + // convert chunk + size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); + assert(result <= sizeof(scratch)); + + // write data + writer.write(scratch.data_u8, result); + } + } + + void write_direct(const char_t* data, size_t length) + { + // flush the remaining buffer contents + flush(); + + // handle large chunks + if (length > bufcapacity) + { + if (encoding == get_write_native_encoding()) + { + // fast path, can just write data chunk + writer.write(data, length * sizeof(char_t)); + return; + } + + // need to convert in suitable chunks + while (length > bufcapacity) + { + // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer + // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) + size_t chunk_size = get_valid_length(data, bufcapacity); + assert(chunk_size); + + // convert chunk and write + flush(data, chunk_size); + + // iterate + data += chunk_size; + length -= chunk_size; + } + + // small tail is copied below + bufsize = 0; + } + + memcpy(buffer + bufsize, data, length * sizeof(char_t)); + bufsize += length; + } + + void write_buffer(const char_t* data, size_t length) + { + size_t offset = bufsize; + + if (offset + length <= bufcapacity) + { + memcpy(buffer + offset, data, length * sizeof(char_t)); + bufsize = offset + length; + } + else + { + write_direct(data, length); + } + } + + void write_string(const char_t* data) + { + // write the part of the string that fits in the buffer + size_t offset = bufsize; + + while (*data && offset < bufcapacity) + buffer[offset++] = *data++; + + // write the rest + if (offset < bufcapacity) + { + bufsize = offset; + } + else + { + // backtrack a bit if we have split the codepoint + size_t length = offset - bufsize; + size_t extra = length - get_valid_length(data - length, length); + + bufsize = offset - extra; + + write_direct(data - extra, strlength(data) + extra); + } + } + + void write(char_t d0) + { + size_t offset = bufsize; + if (offset > bufcapacity - 1) offset = flush(); + + buffer[offset + 0] = d0; + bufsize = offset + 1; + } + + void write(char_t d0, char_t d1) + { + size_t offset = bufsize; + if (offset > bufcapacity - 2) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + bufsize = offset + 2; + } + + void write(char_t d0, char_t d1, char_t d2) + { + size_t offset = bufsize; + if (offset > bufcapacity - 3) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + bufsize = offset + 3; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3) + { + size_t offset = bufsize; + if (offset > bufcapacity - 4) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + buffer[offset + 3] = d3; + bufsize = offset + 4; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) + { + size_t offset = bufsize; + if (offset > bufcapacity - 5) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + buffer[offset + 3] = d3; + buffer[offset + 4] = d4; + bufsize = offset + 5; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) + { + size_t offset = bufsize; + if (offset > bufcapacity - 6) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + buffer[offset + 3] = d3; + buffer[offset + 4] = d4; + buffer[offset + 5] = d5; + bufsize = offset + 6; + } + + // utf8 maximum expansion: x4 (-> utf32) + // utf16 maximum expansion: x2 (-> utf32) + // utf32 maximum expansion: x1 + enum + { + bufcapacitybytes = + #ifdef PUGIXML_MEMORY_OUTPUT_STACK + PUGIXML_MEMORY_OUTPUT_STACK + #else + 10240 + #endif + , + bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) + }; + + char_t buffer[bufcapacity]; + + union + { + uint8_t data_u8[4 * bufcapacity]; + uint16_t data_u16[2 * bufcapacity]; + uint32_t data_u32[bufcapacity]; + char_t data_char[bufcapacity]; + } scratch; + + xml_writer& writer; + size_t bufsize; + xml_encoding encoding; + }; + + PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) + { + while (*s) + { + const char_t* prev = s; + + // While *s is a usual symbol + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type)); + + writer.write_buffer(prev, static_cast(s - prev)); + + switch (*s) + { + case 0: break; + case '&': + writer.write('&', 'a', 'm', 'p', ';'); + ++s; + break; + case '<': + writer.write('&', 'l', 't', ';'); + ++s; + break; + case '>': + writer.write('&', 'g', 't', ';'); + ++s; + break; + case '"': + if (flags & format_attribute_single_quote) + writer.write('"'); + else + writer.write('&', 'q', 'u', 'o', 't', ';'); + ++s; + break; + case '\'': + if (flags & format_attribute_single_quote) + writer.write('&', 'a', 'p', 'o', 's', ';'); + else + writer.write('\''); + ++s; + break; + default: // s is not a usual symbol + { + unsigned int ch = static_cast(*s++); + assert(ch < 32); + + if (!(flags & format_skip_control_chars)) + writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); + } + } + } + } + + PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) + { + if (flags & format_no_escapes) + writer.write_string(s); + else + text_output_escaped(writer, s, type, flags); + } + + PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) + { + do + { + writer.write('<', '!', '[', 'C', 'D'); + writer.write('A', 'T', 'A', '['); + + const char_t* prev = s; + + // look for ]]> sequence - we can't output it as is since it terminates CDATA + while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; + + // skip ]] if we stopped at ]]>, > will go to the next CDATA section + if (*s) s += 2; + + writer.write_buffer(prev, static_cast(s - prev)); + + writer.write(']', ']', '>'); + } + while (*s); + } + + PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) + { + switch (indent_length) + { + case 1: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0]); + break; + } + + case 2: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0], indent[1]); + break; + } + + case 3: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0], indent[1], indent[2]); + break; + } + + case 4: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0], indent[1], indent[2], indent[3]); + break; + } + + default: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write_buffer(indent, indent_length); + } + } + } + + PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) + { + writer.write('<', '!', '-', '-'); + + while (*s) + { + const char_t* prev = s; + + // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body + while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s; + + writer.write_buffer(prev, static_cast(s - prev)); + + if (*s) + { + assert(*s == '-'); + + writer.write('-', ' '); + ++s; + } + } + + writer.write('-', '-', '>'); + } + + PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) + { + while (*s) + { + const char_t* prev = s; + + // look for ?> sequence - we can't output it since ?> terminates PI + while (*s && !(s[0] == '?' && s[1] == '>')) ++s; + + writer.write_buffer(prev, static_cast(s - prev)); + + if (*s) + { + assert(s[0] == '?' && s[1] == '>'); + + writer.write('?', ' ', '>'); + s += 2; + } + } + } + + PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t enquotation_char = (flags & format_attribute_single_quote) ? '\'' : '"'; + + for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) + { + if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes) + { + writer.write('\n'); + + text_output_indent(writer, indent, indent_length, depth + 1); + } + else + { + writer.write(' '); + } + + writer.write_string(a->name ? a->name + 0 : default_name); + writer.write('=', enquotation_char); + + if (a->value) + text_output(writer, a->value, ctx_special_attr, flags); + + writer.write(enquotation_char); + } + } + + PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t* name = node->name ? node->name + 0 : default_name; + + writer.write('<'); + writer.write_string(name); + + if (node->first_attribute) + node_output_attributes(writer, node, indent, indent_length, flags, depth); + + // element nodes can have value if parse_embed_pcdata was used + if (!node->value) + { + if (!node->first_child) + { + if (flags & format_no_empty_element_tags) + { + writer.write('>', '<', '/'); + writer.write_string(name); + writer.write('>'); + + return false; + } + else + { + if ((flags & format_raw) == 0) + writer.write(' '); + + writer.write('/', '>'); + + return false; + } + } + else + { + writer.write('>'); + + return true; + } + } + else + { + writer.write('>'); + + text_output(writer, node->value, ctx_special_pcdata, flags); + + if (!node->first_child) + { + writer.write('<', '/'); + writer.write_string(name); + writer.write('>'); + + return false; + } + else + { + return true; + } + } + } + + PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t* name = node->name ? node->name + 0 : default_name; + + writer.write('<', '/'); + writer.write_string(name); + writer.write('>'); + } + + PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + + switch (PUGI__NODETYPE(node)) + { + case node_pcdata: + text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags); + break; + + case node_cdata: + text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); + break; + + case node_comment: + node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); + break; + + case node_pi: + writer.write('<', '?'); + writer.write_string(node->name ? node->name + 0 : default_name); + + if (node->value) + { + writer.write(' '); + node_output_pi_value(writer, node->value); + } + + writer.write('?', '>'); + break; + + case node_declaration: + writer.write('<', '?'); + writer.write_string(node->name ? node->name + 0 : default_name); + node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0); + writer.write('?', '>'); + break; + + case node_doctype: + writer.write('<', '!', 'D', 'O', 'C'); + writer.write('T', 'Y', 'P', 'E'); + + if (node->value) + { + writer.write(' '); + writer.write_string(node->value); + } + + writer.write('>'); + break; + + default: + assert(false && "Invalid node type"); // unreachable + } + } + + enum indent_flags_t + { + indent_newline = 1, + indent_indent = 2 + }; + + PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) + { + size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0; + unsigned int indent_flags = indent_indent; + + xml_node_struct* node = root; + + do + { + assert(node); + + // begin writing current node + if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) + { + node_output_simple(writer, node, flags); + + indent_flags = 0; + } + else + { + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + + if ((indent_flags & indent_indent) && indent_length) + text_output_indent(writer, indent, indent_length, depth); + + if (PUGI__NODETYPE(node) == node_element) + { + indent_flags = indent_newline | indent_indent; + + if (node_output_start(writer, node, indent, indent_length, flags, depth)) + { + // element nodes can have value if parse_embed_pcdata was used + if (node->value) + indent_flags = 0; + + node = node->first_child; + depth++; + continue; + } + } + else if (PUGI__NODETYPE(node) == node_document) + { + indent_flags = indent_indent; + + if (node->first_child) + { + node = node->first_child; + continue; + } + } + else + { + node_output_simple(writer, node, flags); + + indent_flags = indent_newline | indent_indent; + } + } + + // continue to the next node + while (node != root) + { + if (node->next_sibling) + { + node = node->next_sibling; + break; + } + + node = node->parent; + + // write closing node + if (PUGI__NODETYPE(node) == node_element) + { + depth--; + + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + + if ((indent_flags & indent_indent) && indent_length) + text_output_indent(writer, indent, indent_length, depth); + + node_output_end(writer, node); + + indent_flags = indent_newline | indent_indent; + } + } + } + while (node != root); + + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + } + + PUGI__FN bool has_declaration(xml_node_struct* node) + { + for (xml_node_struct* child = node->first_child; child; child = child->next_sibling) + { + xml_node_type type = PUGI__NODETYPE(child); + + if (type == node_declaration) return true; + if (type == node_element) return false; + } + + return false; + } + + PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) + { + for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) + if (a == attr) + return true; + + return false; + } + + PUGI__FN bool allow_insert_attribute(xml_node_type parent) + { + return parent == node_element || parent == node_declaration; + } + + PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child) + { + if (parent != node_document && parent != node_element) return false; + if (child == node_document || child == node_null) return false; + if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; + + return true; + } + + PUGI__FN bool allow_move(xml_node parent, xml_node child) + { + // check that child can be a child of parent + if (!allow_insert_child(parent.type(), child.type())) + return false; + + // check that node is not moved between documents + if (parent.root() != child.root()) + return false; + + // check that new parent is not in the child subtree + xml_node cur = parent; + + while (cur) + { + if (cur == child) + return false; + + cur = cur.parent(); + } + + return true; + } + + template + PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc) + { + assert(!dest && (header & header_mask) == 0); + + if (source) + { + if (alloc && (source_header & header_mask) == 0) + { + dest = source; + + // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared + header |= xml_memory_page_contents_shared_mask; + source_header |= xml_memory_page_contents_shared_mask; + } + else + strcpy_insitu(dest, header, header_mask, source, strlength(source)); + } + } + + PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) + { + node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc); + node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc); + + for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute) + { + xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn)); + + if (da) + { + node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); + node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + } + } + } + + PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) + { + xml_allocator& alloc = get_allocator(dn); + xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0; + + node_copy_contents(dn, sn, shared_alloc); + + xml_node_struct* dit = dn; + xml_node_struct* sit = sn->first_child; + + while (sit && sit != sn) + { + // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop + if (sit != dn) + { + xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit)); + + if (copy) + { + node_copy_contents(copy, sit, shared_alloc); + + if (sit->first_child) + { + dit = copy; + sit = sit->first_child; + continue; + } + } + } + + // continue to the next node + do + { + if (sit->next_sibling) + { + sit = sit->next_sibling; + break; + } + + sit = sit->parent; + dit = dit->parent; + } + while (sit != sn); + } + } + + PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) + { + xml_allocator& alloc = get_allocator(da); + xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0; + + node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); + node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + } + + inline bool is_text_node(xml_node_struct* node) + { + xml_node_type type = PUGI__NODETYPE(node); + + return type == node_pcdata || type == node_cdata; + } + + // get value with conversion functions + template PUGI__FN PUGI__UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv) + { + U result = 0; + const char_t* s = value; + + while (PUGI__IS_CHARTYPE(*s, ct_space)) + s++; + + bool negative = (*s == '-'); + + s += (*s == '+' || *s == '-'); + + bool overflow = false; + + if (s[0] == '0' && (s[1] | ' ') == 'x') + { + s += 2; + + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; + + const char_t* start = s; + + for (;;) + { + if (static_cast(*s - '0') < 10) + result = result * 16 + (*s - '0'); + else if (static_cast((*s | ' ') - 'a') < 6) + result = result * 16 + ((*s | ' ') - 'a' + 10); + else + break; + + s++; + } + + size_t digits = static_cast(s - start); + + overflow = digits > sizeof(U) * 2; + } + else + { + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; + + const char_t* start = s; + + for (;;) + { + if (static_cast(*s - '0') < 10) + result = result * 10 + (*s - '0'); + else + break; + + s++; + } + + size_t digits = static_cast(s - start); + + PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2); + + const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5; + const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6'; + const size_t high_bit = sizeof(U) * 8 - 1; + + overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit))); + } + + if (negative) + { + // Workaround for crayc++ CC-3059: Expected no overflow in routine. + #ifdef _CRAYC + return (overflow || result > ~minv + 1) ? minv : ~result + 1; + #else + return (overflow || result > 0 - minv) ? minv : 0 - result; + #endif + } + else + return (overflow || result > maxv) ? maxv : result; + } + + PUGI__FN int get_value_int(const char_t* value) + { + return string_to_integer(value, static_cast(INT_MIN), INT_MAX); + } + + PUGI__FN unsigned int get_value_uint(const char_t* value) + { + return string_to_integer(value, 0, UINT_MAX); + } + + PUGI__FN double get_value_double(const char_t* value) + { + #ifdef PUGIXML_WCHAR_MODE + return wcstod(value, 0); + #else + return strtod(value, 0); + #endif + } + + PUGI__FN float get_value_float(const char_t* value) + { + #ifdef PUGIXML_WCHAR_MODE + return static_cast(wcstod(value, 0)); + #else + return static_cast(strtod(value, 0)); + #endif + } + + PUGI__FN bool get_value_bool(const char_t* value) + { + // only look at first char + char_t first = *value; + + // 1*, t* (true), T* (True), y* (yes), Y* (YES) + return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN long long get_value_llong(const char_t* value) + { + return string_to_integer(value, static_cast(LLONG_MIN), LLONG_MAX); + } + + PUGI__FN unsigned long long get_value_ullong(const char_t* value) + { + return string_to_integer(value, 0, ULLONG_MAX); + } +#endif + + template PUGI__FN PUGI__UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative) + { + char_t* result = end - 1; + U rest = negative ? 0 - value : value; + + do + { + *result-- = static_cast('0' + (rest % 10)); + rest /= 10; + } + while (rest); + + assert(result >= begin); + (void)begin; + + *result = '-'; + + return result + !negative; + } + + // set value with conversion functions + template + PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf) + { + #ifdef PUGIXML_WCHAR_MODE + char_t wbuf[128]; + assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0])); + + size_t offset = 0; + for (; buf[offset]; ++offset) wbuf[offset] = buf[offset]; + + return strcpy_insitu(dest, header, header_mask, wbuf, offset); + #else + return strcpy_insitu(dest, header, header_mask, buf, strlen(buf)); + #endif + } + + template + PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative) + { + char_t buf[64]; + char_t* end = buf + sizeof(buf) / sizeof(buf[0]); + char_t* begin = integer_to_string(buf, end, value, negative); + + return strcpy_insitu(dest, header, header_mask, begin, end - begin); + } + + template + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value) + { + char buf[128]; + PUGI__SNPRINTF(buf, "%.9g", double(value)); + + return set_value_ascii(dest, header, header_mask, buf); + } + + template + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value) + { + char buf[128]; + PUGI__SNPRINTF(buf, "%.17g", value); + + return set_value_ascii(dest, header, header_mask, buf); + } + + template + PUGI__FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value) + { + return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5); + } + + PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) + { + // check input buffer + if (!contents && size) return make_parse_result(status_io_error); + + // get actual encoding + xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); + + // get private buffer + char_t* buffer = 0; + size_t length = 0; + + // coverity[var_deref_model] + if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); + + // delete original buffer if we performed a conversion + if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); + + // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself + if (own || buffer != contents) *out_buffer = buffer; + + // store buffer for offset_debug + doc->buffer = buffer; + + // parse + xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); + + // remember encoding + res.encoding = buffer_encoding; + + return res; + } + + // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick + PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) + { + #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) + // there are 64-bit versions of fseek/ftell, let's use them + typedef __int64 length_type; + + _fseeki64(file, 0, SEEK_END); + length_type length = _ftelli64(file); + _fseeki64(file, 0, SEEK_SET); + #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)) + // there are 64-bit versions of fseek/ftell, let's use them + typedef off64_t length_type; + + fseeko64(file, 0, SEEK_END); + length_type length = ftello64(file); + fseeko64(file, 0, SEEK_SET); + #else + // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. + typedef long length_type; + + fseek(file, 0, SEEK_END); + length_type length = ftell(file); + fseek(file, 0, SEEK_SET); + #endif + + // check for I/O errors + if (length < 0) return status_io_error; + + // check for overflow + size_t result = static_cast(length); + + if (static_cast(result) != length) return status_out_of_memory; + + // finalize + out_result = result; + + return status_ok; + } + + // This function assumes that buffer has extra sizeof(char_t) writable bytes after size + PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) + { + // We only need to zero-terminate if encoding conversion does not do it for us + #ifdef PUGIXML_WCHAR_MODE + xml_encoding wchar_encoding = get_wchar_encoding(); + + if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) + { + size_t length = size / sizeof(char_t); + + static_cast(buffer)[length] = 0; + return (length + 1) * sizeof(char_t); + } + #else + if (encoding == encoding_utf8) + { + static_cast(buffer)[size] = 0; + return size + 1; + } + #endif + + return size; + } + + PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer) + { + if (!file) return make_parse_result(status_file_not_found); + + // get file size (can result in I/O errors) + size_t size = 0; + xml_parse_status size_status = get_file_size(file, size); + if (size_status != status_ok) return make_parse_result(size_status); + + size_t max_suffix_size = sizeof(char_t); + + // allocate buffer for the whole file + char* contents = static_cast(xml_memory::allocate(size + max_suffix_size)); + if (!contents) return make_parse_result(status_out_of_memory); + + // read file in memory + size_t read_size = fread(contents, 1, size, file); + + if (read_size != size) + { + xml_memory::deallocate(contents); + return make_parse_result(status_io_error); + } + + xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); + + return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer); + } + + PUGI__FN void close_file(FILE* file) + { + fclose(file); + } + +#ifndef PUGIXML_NO_STL + template struct xml_stream_chunk + { + static xml_stream_chunk* create() + { + void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); + if (!memory) return 0; + + return new (memory) xml_stream_chunk(); + } + + static void destroy(xml_stream_chunk* chunk) + { + // free chunk chain + while (chunk) + { + xml_stream_chunk* next_ = chunk->next; + + xml_memory::deallocate(chunk); + + chunk = next_; + } + } + + xml_stream_chunk(): next(0), size(0) + { + } + + xml_stream_chunk* next; + size_t size; + + T data[xml_memory_page_size / sizeof(T)]; + }; + + template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + { + auto_deleter > chunks(0, xml_stream_chunk::destroy); + + // read file to a chunk list + size_t total = 0; + xml_stream_chunk* last = 0; + + while (!stream.eof()) + { + // allocate new chunk + xml_stream_chunk* chunk = xml_stream_chunk::create(); + if (!chunk) return status_out_of_memory; + + // append chunk to list + if (last) last = last->next = chunk; + else chunks.data = last = chunk; + + // read data to chunk + stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); + chunk->size = static_cast(stream.gcount()) * sizeof(T); + + // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors + if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; + + // guard against huge files (chunk size is small enough to make this overflow check work) + if (total + chunk->size < total) return status_out_of_memory; + total += chunk->size; + } + + size_t max_suffix_size = sizeof(char_t); + + // copy chunk list to a contiguous buffer + char* buffer = static_cast(xml_memory::allocate(total + max_suffix_size)); + if (!buffer) return status_out_of_memory; + + char* write = buffer; + + for (xml_stream_chunk* chunk = chunks.data; chunk; chunk = chunk->next) + { + assert(write + chunk->size <= buffer + total); + memcpy(write, chunk->data, chunk->size); + write += chunk->size; + } + + assert(write == buffer + total); + + // return buffer + *out_buffer = buffer; + *out_size = total; + + return status_ok; + } + + template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + { + // get length of remaining data in stream + typename std::basic_istream::pos_type pos = stream.tellg(); + stream.seekg(0, std::ios::end); + std::streamoff length = stream.tellg() - pos; + stream.seekg(pos); + + if (stream.fail() || pos < 0) return status_io_error; + + // guard against huge files + size_t read_length = static_cast(length); + + if (static_cast(read_length) != length || length < 0) return status_out_of_memory; + + size_t max_suffix_size = sizeof(char_t); + + // read stream data into memory (guard against stream exceptions with buffer holder) + auto_deleter buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); + if (!buffer.data) return status_out_of_memory; + + stream.read(static_cast(buffer.data), static_cast(read_length)); + + // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors + if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; + + // return buffer + size_t actual_length = static_cast(stream.gcount()); + assert(actual_length <= read_length); + + *out_buffer = buffer.release(); + *out_size = actual_length * sizeof(T); + + return status_ok; + } + + template PUGI__FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer) + { + void* buffer = 0; + size_t size = 0; + xml_parse_status status = status_ok; + + // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) + if (stream.fail()) return make_parse_result(status_io_error); + + // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) + if (stream.tellg() < 0) + { + stream.clear(); // clear error flags that could be set by a failing tellg + status = load_stream_data_noseek(stream, &buffer, &size); + } + else + status = load_stream_data_seek(stream, &buffer, &size); + + if (status != status_ok) return make_parse_result(status); + + xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); + + return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer); + } +#endif + +#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) + PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) + { + return _wfopen(path, mode); + } +#else + PUGI__FN char* convert_path_heap(const wchar_t* str) + { + assert(str); + + // first pass: get length in utf8 characters + size_t length = strlength_wide(str); + size_t size = as_utf8_begin(str, length); + + // allocate resulting string + char* result = static_cast(xml_memory::allocate(size + 1)); + if (!result) return 0; + + // second pass: convert to utf8 + as_utf8_end(result, size, str, length); + + // zero-terminate + result[size] = 0; + + return result; + } + + PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) + { + // there is no standard function to open wide paths, so our best bet is to try utf8 path + char* path_utf8 = convert_path_heap(path); + if (!path_utf8) return 0; + + // convert mode to ASCII (we mirror _wfopen interface) + char mode_ascii[4] = {0}; + for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]); + + // try to open the utf8 path + FILE* result = fopen(path_utf8, mode_ascii); + + // free dummy buffer + xml_memory::deallocate(path_utf8); + + return result; + } +#endif + + PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) + { + if (!file) return false; + + xml_writer_file writer(file); + doc.save(writer, indent, flags, encoding); + + return ferror(file) == 0; + } + + struct name_null_sentry + { + xml_node_struct* node; + char_t* name; + + name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name) + { + node->name = 0; + } + + ~name_null_sentry() + { + node->name = name; + } + }; +PUGI__NS_END + +namespace pugi +{ + PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) + { + } + + PUGI__FN void xml_writer_file::write(const void* data, size_t size) + { + size_t result = fwrite(data, 1, size, static_cast(file)); + (void)!result; // unfortunately we can't do proper error handling here + } + +#ifndef PUGIXML_NO_STL + PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) + { + } + + PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) + { + } + + PUGI__FN void xml_writer_stream::write(const void* data, size_t size) + { + if (narrow_stream) + { + assert(!wide_stream); + narrow_stream->write(reinterpret_cast(data), static_cast(size)); + } + else + { + assert(wide_stream); + assert(size % sizeof(wchar_t) == 0); + + wide_stream->write(reinterpret_cast(data), static_cast(size / sizeof(wchar_t))); + } + } +#endif + + PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) + { + } + + PUGI__FN xml_tree_walker::~xml_tree_walker() + { + } + + PUGI__FN int xml_tree_walker::depth() const + { + return _depth; + } + + PUGI__FN bool xml_tree_walker::begin(xml_node&) + { + return true; + } + + PUGI__FN bool xml_tree_walker::end(xml_node&) + { + return true; + } + + PUGI__FN xml_attribute::xml_attribute(): _attr(0) + { + } + + PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) + { + } + + PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) + { + } + + PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const + { + return _attr ? unspecified_bool_xml_attribute : 0; + } + + PUGI__FN bool xml_attribute::operator!() const + { + return !_attr; + } + + PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const + { + return (_attr == r._attr); + } + + PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const + { + return (_attr != r._attr); + } + + PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const + { + return (_attr < r._attr); + } + + PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const + { + return (_attr > r._attr); + } + + PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const + { + return (_attr <= r._attr); + } + + PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const + { + return (_attr >= r._attr); + } + + PUGI__FN xml_attribute xml_attribute::next_attribute() const + { + return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); + } + + PUGI__FN xml_attribute xml_attribute::previous_attribute() const + { + return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); + } + + PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const + { + return (_attr && _attr->value) ? _attr->value + 0 : def; + } + + PUGI__FN int xml_attribute::as_int(int def) const + { + return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def; + } + + PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const + { + return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def; + } + + PUGI__FN double xml_attribute::as_double(double def) const + { + return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def; + } + + PUGI__FN float xml_attribute::as_float(float def) const + { + return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def; + } + + PUGI__FN bool xml_attribute::as_bool(bool def) const + { + return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN long long xml_attribute::as_llong(long long def) const + { + return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def; + } + + PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const + { + return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def; + } +#endif + + PUGI__FN bool xml_attribute::empty() const + { + return !_attr; + } + + PUGI__FN const char_t* xml_attribute::name() const + { + return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* xml_attribute::value() const + { + return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN size_t xml_attribute::hash_value() const + { + return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); + } + + PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const + { + return _attr; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(float rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) + { + set_value(rhs); + return *this; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) + { + set_value(rhs); + return *this; + } +#endif + + PUGI__FN bool xml_attribute::set_name(const char_t* rhs) + { + if (!_attr) return false; + + return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN bool xml_attribute::set_value(const char_t* rhs) + { + if (!_attr) return false; + + return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN bool xml_attribute::set_value(int rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned int rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } + + PUGI__FN bool xml_attribute::set_value(long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } + + PUGI__FN bool xml_attribute::set_value(double rhs) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + + PUGI__FN bool xml_attribute::set_value(float rhs) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + + PUGI__FN bool xml_attribute::set_value(bool rhs) + { + if (!_attr) return false; + + return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN bool xml_attribute::set_value(long long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } +#endif + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN xml_node::xml_node(): _root(0) + { + } + + PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) + { + } + + PUGI__FN static void unspecified_bool_xml_node(xml_node***) + { + } + + PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const + { + return _root ? unspecified_bool_xml_node : 0; + } + + PUGI__FN bool xml_node::operator!() const + { + return !_root; + } + + PUGI__FN xml_node::iterator xml_node::begin() const + { + return iterator(_root ? _root->first_child + 0 : 0, _root); + } + + PUGI__FN xml_node::iterator xml_node::end() const + { + return iterator(0, _root); + } + + PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const + { + return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root); + } + + PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const + { + return attribute_iterator(0, _root); + } + + PUGI__FN xml_object_range xml_node::children() const + { + return xml_object_range(begin(), end()); + } + + PUGI__FN xml_object_range xml_node::children(const char_t* name_) const + { + return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_)); + } + + PUGI__FN xml_object_range xml_node::attributes() const + { + return xml_object_range(attributes_begin(), attributes_end()); + } + + PUGI__FN bool xml_node::operator==(const xml_node& r) const + { + return (_root == r._root); + } + + PUGI__FN bool xml_node::operator!=(const xml_node& r) const + { + return (_root != r._root); + } + + PUGI__FN bool xml_node::operator<(const xml_node& r) const + { + return (_root < r._root); + } + + PUGI__FN bool xml_node::operator>(const xml_node& r) const + { + return (_root > r._root); + } + + PUGI__FN bool xml_node::operator<=(const xml_node& r) const + { + return (_root <= r._root); + } + + PUGI__FN bool xml_node::operator>=(const xml_node& r) const + { + return (_root >= r._root); + } + + PUGI__FN bool xml_node::empty() const + { + return !_root; + } + + PUGI__FN const char_t* xml_node::name() const + { + return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN xml_node_type xml_node::type() const + { + return _root ? PUGI__NODETYPE(_root) : node_null; + } + + PUGI__FN const char_t* xml_node::value() const + { + return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN xml_node xml_node::child(const char_t* name_) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + + return xml_node(); + } + + PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const + { + if (!_root) return xml_attribute(); + + for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) + if (i->name && impl::strequal(name_, i->name)) + return xml_attribute(i); + + return xml_attribute(); + } + + PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) + if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + + return xml_node(); + } + + PUGI__FN xml_node xml_node::next_sibling() const + { + return _root ? xml_node(_root->next_sibling) : xml_node(); + } + + PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) + if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + + return xml_node(); + } + + PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const + { + xml_attribute_struct* hint = hint_._attr; + + // if hint is not an attribute of node, behavior is not defined + assert(!hint || (_root && impl::is_attribute_of(hint, _root))); + + if (!_root) return xml_attribute(); + + // optimistically search from hint up until the end + for (xml_attribute_struct* i = hint; i; i = i->next_attribute) + if (i->name && impl::strequal(name_, i->name)) + { + // update hint to maximize efficiency of searching for consecutive attributes + hint_._attr = i->next_attribute; + + return xml_attribute(i); + } + + // wrap around and search from the first attribute until the hint + // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails + for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) + if (j->name && impl::strequal(name_, j->name)) + { + // update hint to maximize efficiency of searching for consecutive attributes + hint_._attr = j->next_attribute; + + return xml_attribute(j); + } + + return xml_attribute(); + } + + PUGI__FN xml_node xml_node::previous_sibling() const + { + if (!_root) return xml_node(); + + if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); + else return xml_node(); + } + + PUGI__FN xml_node xml_node::parent() const + { + return _root ? xml_node(_root->parent) : xml_node(); + } + + PUGI__FN xml_node xml_node::root() const + { + return _root ? xml_node(&impl::get_document(_root)) : xml_node(); + } + + PUGI__FN xml_text xml_node::text() const + { + return xml_text(_root); + } + + PUGI__FN const char_t* xml_node::child_value() const + { + if (!_root) return PUGIXML_TEXT(""); + + // element nodes can have value if parse_embed_pcdata was used + if (PUGI__NODETYPE(_root) == node_element && _root->value) + return _root->value; + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (impl::is_text_node(i) && i->value) + return i->value; + + return PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const + { + return child(name_).child_value(); + } + + PUGI__FN xml_attribute xml_node::first_attribute() const + { + return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); + } + + PUGI__FN xml_attribute xml_node::last_attribute() const + { + return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); + } + + PUGI__FN xml_node xml_node::first_child() const + { + return _root ? xml_node(_root->first_child) : xml_node(); + } + + PUGI__FN xml_node xml_node::last_child() const + { + return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); + } + + PUGI__FN bool xml_node::set_name(const char_t* rhs) + { + xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; + + if (type_ != node_element && type_ != node_pi && type_ != node_declaration) + return false; + + return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN bool xml_node::set_value(const char_t* rhs) + { + xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; + + if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) + return false; + + return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::append_attribute(a._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::prepend_attribute(a._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_after(a._attr, attr._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_before(a._attr, attr._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::append_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::prepend_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_after(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_before(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_node xml_node::append_child(xml_node_type type_) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::append_node(n._root, _root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::prepend_node(n._root, _root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_before(n._root, node._root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_after(n._root, node._root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::append_child(const char_t* name_) + { + xml_node result = append_child(node_element); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::prepend_child(const char_t* name_) + { + xml_node result = prepend_child(node_element); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) + { + xml_node result = insert_child_after(node_element, node); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) + { + xml_node result = insert_child_before(node_element, node); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::append_copy(const xml_node& proto) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::append_node(n._root, _root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::prepend_node(n._root, _root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_after(n._root, node._root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_before(n._root, node._root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::append_move(const xml_node& moved) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::append_node(moved._root, _root); + + return moved; + } + + PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::prepend_node(moved._root, _root); + + return moved; + } + + PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + if (moved._root == node._root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::insert_node_after(moved._root, node._root); + + return moved; + } + + PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + if (moved._root == node._root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::insert_node_before(moved._root, node._root); + + return moved; + } + + PUGI__FN bool xml_node::remove_attribute(const char_t* name_) + { + return remove_attribute(attribute(name_)); + } + + PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a) + { + if (!_root || !a._attr) return false; + if (!impl::is_attribute_of(a._attr, _root)) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + impl::remove_attribute(a._attr, _root); + impl::destroy_attribute(a._attr, alloc); + + return true; + } + + PUGI__FN bool xml_node::remove_child(const char_t* name_) + { + return remove_child(child(name_)); + } + + PUGI__FN bool xml_node::remove_child(const xml_node& n) + { + if (!_root || !n._root || n._root->parent != _root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + impl::remove_node(n._root); + impl::destroy_node(n._root, alloc); + + return true; + } + + PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + // append_buffer is only valid for elements/documents + if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); + + // get document node + impl::xml_document_struct* doc = &impl::get_document(_root); + + // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense + doc->header |= impl::xml_memory_page_contents_shared_mask; + + // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) + impl::xml_memory_page* page = 0; + impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer) + sizeof(void*), page)); + (void)page; + + if (!extra) return impl::make_parse_result(status_out_of_memory); + + #ifdef PUGIXML_COMPACT + // align the memory block to a pointer boundary; this is required for compact mode where memory allocations are only 4b aligned + // note that this requires up to sizeof(void*)-1 additional memory, which the allocation above takes into account + extra = reinterpret_cast((reinterpret_cast(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1)); + #endif + + // add extra buffer to the list + extra->buffer = 0; + extra->next = doc->extra_buffers; + doc->extra_buffers = extra; + + // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level + impl::name_null_sentry sentry(_root); + + return impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &extra->buffer); + } + + PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (i->name && impl::strequal(name_, i->name)) + { + for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) + if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) + return xml_node(i); + } + + return xml_node(); + } + + PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) + if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN string_t xml_node::path(char_t delimiter) const + { + if (!_root) return string_t(); + + size_t offset = 0; + + for (xml_node_struct* i = _root; i; i = i->parent) + { + offset += (i != _root); + offset += i->name ? impl::strlength(i->name) : 0; + } + + string_t result; + result.resize(offset); + + for (xml_node_struct* j = _root; j; j = j->parent) + { + if (j != _root) + result[--offset] = delimiter; + + if (j->name) + { + size_t length = impl::strlength(j->name); + + offset -= length; + memcpy(&result[offset], j->name, length * sizeof(char_t)); + } + } + + assert(offset == 0); + + return result; + } +#endif + + PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const + { + xml_node found = *this; // Current search context. + + if (!_root || !path_[0]) return found; + + if (path_[0] == delimiter) + { + // Absolute path; e.g. '/foo/bar' + found = found.root(); + ++path_; + } + + const char_t* path_segment = path_; + + while (*path_segment == delimiter) ++path_segment; + + const char_t* path_segment_end = path_segment; + + while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; + + if (path_segment == path_segment_end) return found; + + const char_t* next_segment = path_segment_end; + + while (*next_segment == delimiter) ++next_segment; + + if (*path_segment == '.' && path_segment + 1 == path_segment_end) + return found.first_element_by_path(next_segment, delimiter); + else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) + return found.parent().first_element_by_path(next_segment, delimiter); + else + { + for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) + { + if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) + { + xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); + + if (subsearch) return subsearch; + } + } + + return xml_node(); + } + } + + PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) + { + walker._depth = -1; + + xml_node arg_begin(_root); + if (!walker.begin(arg_begin)) return false; + + xml_node_struct* cur = _root ? _root->first_child + 0 : 0; + + if (cur) + { + ++walker._depth; + + do + { + xml_node arg_for_each(cur); + if (!walker.for_each(arg_for_each)) + return false; + + if (cur->first_child) + { + ++walker._depth; + cur = cur->first_child; + } + else if (cur->next_sibling) + cur = cur->next_sibling; + else + { + while (!cur->next_sibling && cur != _root && cur->parent) + { + --walker._depth; + cur = cur->parent; + } + + if (cur != _root) + cur = cur->next_sibling; + } + } + while (cur && cur != _root); + } + + assert(walker._depth == -1); + + xml_node arg_end(_root); + return walker.end(arg_end); + } + + PUGI__FN size_t xml_node::hash_value() const + { + return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); + } + + PUGI__FN xml_node_struct* xml_node::internal_object() const + { + return _root; + } + + PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + { + if (!_root) return; + + impl::xml_buffered_writer buffered_writer(writer, encoding); + + impl::node_output(buffered_writer, _root, indent, flags, depth); + + buffered_writer.flush(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + { + xml_writer_stream writer(stream); + + print(writer, indent, flags, encoding, depth); + } + + PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const + { + xml_writer_stream writer(stream); + + print(writer, indent, flags, encoding_wchar, depth); + } +#endif + + PUGI__FN ptrdiff_t xml_node::offset_debug() const + { + if (!_root) return -1; + + impl::xml_document_struct& doc = impl::get_document(_root); + + // we can determine the offset reliably only if there is exactly once parse buffer + if (!doc.buffer || doc.extra_buffers) return -1; + + switch (type()) + { + case node_document: + return 0; + + case node_element: + case node_declaration: + case node_pi: + return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1; + + case node_pcdata: + case node_cdata: + case node_comment: + case node_doctype: + return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1; + + default: + assert(false && "Invalid node type"); // unreachable + return -1; + } + } + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xml_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xml_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) + { + } + + PUGI__FN xml_node_struct* xml_text::_data() const + { + if (!_root || impl::is_text_node(_root)) return _root; + + // element nodes can have value if parse_embed_pcdata was used + if (PUGI__NODETYPE(_root) == node_element && _root->value) + return _root; + + for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) + if (impl::is_text_node(node)) + return node; + + return 0; + } + + PUGI__FN xml_node_struct* xml_text::_data_new() + { + xml_node_struct* d = _data(); + if (d) return d; + + return xml_node(_root).append_child(node_pcdata).internal_object(); + } + + PUGI__FN xml_text::xml_text(): _root(0) + { + } + + PUGI__FN static void unspecified_bool_xml_text(xml_text***) + { + } + + PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const + { + return _data() ? unspecified_bool_xml_text : 0; + } + + PUGI__FN bool xml_text::operator!() const + { + return !_data(); + } + + PUGI__FN bool xml_text::empty() const + { + return _data() == 0; + } + + PUGI__FN const char_t* xml_text::get() const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? d->value + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* xml_text::as_string(const char_t* def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? d->value + 0 : def; + } + + PUGI__FN int xml_text::as_int(int def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_int(d->value) : def; + } + + PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_uint(d->value) : def; + } + + PUGI__FN double xml_text::as_double(double def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_double(d->value) : def; + } + + PUGI__FN float xml_text::as_float(float def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_float(d->value) : def; + } + + PUGI__FN bool xml_text::as_bool(bool def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_bool(d->value) : def; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN long long xml_text::as_llong(long long def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_llong(d->value) : def; + } + + PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_ullong(d->value) : def; + } +#endif + + PUGI__FN bool xml_text::set(const char_t* rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false; + } + + PUGI__FN bool xml_text::set(int rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned int rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } + + PUGI__FN bool xml_text::set(long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } + + PUGI__FN bool xml_text::set(float rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + } + + PUGI__FN bool xml_text::set(double rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + } + + PUGI__FN bool xml_text::set(bool rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN bool xml_text::set(long long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned long long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } +#endif + + PUGI__FN xml_text& xml_text::operator=(const char_t* rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(int rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(double rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(float rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(bool rhs) + { + set(rhs); + return *this; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN xml_text& xml_text::operator=(long long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs) + { + set(rhs); + return *this; + } +#endif + + PUGI__FN xml_node xml_text::data() const + { + return xml_node(_data()); + } + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xml_text& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN xml_node_iterator::xml_node_iterator() + { + } + + PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) + { + } + + PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const + { + return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; + } + + PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const + { + return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; + } + + PUGI__FN xml_node& xml_node_iterator::operator*() const + { + assert(_wrap._root); + return _wrap; + } + + PUGI__FN xml_node* xml_node_iterator::operator->() const + { + assert(_wrap._root); + return const_cast(&_wrap); // BCC5 workaround + } + + PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() + { + assert(_wrap._root); + _wrap._root = _wrap._root->next_sibling; + return *this; + } + + PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) + { + xml_node_iterator temp = *this; + ++*this; + return temp; + } + + PUGI__FN const xml_node_iterator& xml_node_iterator::operator--() + { + _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); + return *this; + } + + PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) + { + xml_node_iterator temp = *this; + --*this; + return temp; + } + + PUGI__FN xml_attribute_iterator::xml_attribute_iterator() + { + } + + PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) + { + } + + PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const + { + return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; + } + + PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const + { + return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; + } + + PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const + { + assert(_wrap._attr); + return _wrap; + } + + PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const + { + assert(_wrap._attr); + return const_cast(&_wrap); // BCC5 workaround + } + + PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() + { + assert(_wrap._attr); + _wrap._attr = _wrap._attr->next_attribute; + return *this; + } + + PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) + { + xml_attribute_iterator temp = *this; + ++*this; + return temp; + } + + PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--() + { + _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); + return *this; + } + + PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) + { + xml_attribute_iterator temp = *this; + --*this; + return temp; + } + + PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) + { + } + + PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) + { + } + + PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name) + { + } + + PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const + { + return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; + } + + PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const + { + return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; + } + + PUGI__FN xml_node& xml_named_node_iterator::operator*() const + { + assert(_wrap._root); + return _wrap; + } + + PUGI__FN xml_node* xml_named_node_iterator::operator->() const + { + assert(_wrap._root); + return const_cast(&_wrap); // BCC5 workaround + } + + PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() + { + assert(_wrap._root); + _wrap = _wrap.next_sibling(_name); + return *this; + } + + PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) + { + xml_named_node_iterator temp = *this; + ++*this; + return temp; + } + + PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--() + { + if (_wrap._root) + _wrap = _wrap.previous_sibling(_name); + else + { + _wrap = _parent.last_child(); + + if (!impl::strequal(_wrap.name(), _name)) + _wrap = _wrap.previous_sibling(_name); + } + + return *this; + } + + PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) + { + xml_named_node_iterator temp = *this; + --*this; + return temp; + } + + PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) + { + } + + PUGI__FN xml_parse_result::operator bool() const + { + return status == status_ok; + } + + PUGI__FN const char* xml_parse_result::description() const + { + switch (status) + { + case status_ok: return "No error"; + + case status_file_not_found: return "File was not found"; + case status_io_error: return "Error reading from file/stream"; + case status_out_of_memory: return "Could not allocate memory"; + case status_internal_error: return "Internal error occurred"; + + case status_unrecognized_tag: return "Could not determine tag type"; + + case status_bad_pi: return "Error parsing document declaration/processing instruction"; + case status_bad_comment: return "Error parsing comment"; + case status_bad_cdata: return "Error parsing CDATA section"; + case status_bad_doctype: return "Error parsing document type declaration"; + case status_bad_pcdata: return "Error parsing PCDATA section"; + case status_bad_start_element: return "Error parsing start element tag"; + case status_bad_attribute: return "Error parsing element attribute"; + case status_bad_end_element: return "Error parsing end element tag"; + case status_end_element_mismatch: return "Start-end tags mismatch"; + + case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; + + case status_no_document_element: return "No document element found"; + + default: return "Unknown error"; + } + } + + PUGI__FN xml_document::xml_document(): _buffer(0) + { + _create(); + } + + PUGI__FN xml_document::~xml_document() + { + _destroy(); + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0) + { + _create(); + _move(rhs); + } + + PUGI__FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT + { + if (this == &rhs) return *this; + + _destroy(); + _create(); + _move(rhs); + + return *this; + } +#endif + + PUGI__FN void xml_document::reset() + { + _destroy(); + _create(); + } + + PUGI__FN void xml_document::reset(const xml_document& proto) + { + reset(); + + for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) + append_copy(cur); + } + + PUGI__FN void xml_document::_create() + { + assert(!_root); + + #ifdef PUGIXML_COMPACT + // space for page marker for the first page (uint32_t), rounded up to pointer size; assumes pointers are at least 32-bit + const size_t page_offset = sizeof(void*); + #else + const size_t page_offset = 0; + #endif + + // initialize sentinel page + PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory)); + + // prepare page structure + impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory); + assert(page); + + page->busy_size = impl::xml_memory_page_size; + + // setup first page marker + #ifdef PUGIXML_COMPACT + // round-trip through void* to avoid 'cast increases required alignment of target type' warning + page->compact_page_marker = reinterpret_cast(static_cast(reinterpret_cast(page) + sizeof(impl::xml_memory_page))); + *page->compact_page_marker = sizeof(impl::xml_memory_page); + #endif + + // allocate new root + _root = new (reinterpret_cast(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page); + _root->prev_sibling_c = _root; + + // setup sentinel page + page->allocator = static_cast(_root); + + // setup hash table pointer in allocator + #ifdef PUGIXML_COMPACT + page->allocator->_hash = &static_cast(_root)->hash; + #endif + + // verify the document allocation + assert(reinterpret_cast(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); + } + + PUGI__FN void xml_document::_destroy() + { + assert(_root); + + // destroy static storage + if (_buffer) + { + impl::xml_memory::deallocate(_buffer); + _buffer = 0; + } + + // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) + for (impl::xml_extra_buffer* extra = static_cast(_root)->extra_buffers; extra; extra = extra->next) + { + if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); + } + + // destroy dynamic storage, leave sentinel page (it's in static memory) + impl::xml_memory_page* root_page = PUGI__GETPAGE(_root); + assert(root_page && !root_page->prev); + assert(reinterpret_cast(root_page) >= _memory && reinterpret_cast(root_page) < _memory + sizeof(_memory)); + + for (impl::xml_memory_page* page = root_page->next; page; ) + { + impl::xml_memory_page* next = page->next; + + impl::xml_allocator::deallocate_page(page); + + page = next; + } + + #ifdef PUGIXML_COMPACT + // destroy hash table + static_cast(_root)->hash.clear(); + #endif + + _root = 0; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT + { + impl::xml_document_struct* doc = static_cast(_root); + impl::xml_document_struct* other = static_cast(rhs._root); + + // save first child pointer for later; this needs hash access + xml_node_struct* other_first_child = other->first_child; + + #ifdef PUGIXML_COMPACT + // reserve space for the hash table up front; this is the only operation that can fail + // if it does, we have no choice but to throw (if we have exceptions) + if (other_first_child) + { + size_t other_children = 0; + for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) + other_children++; + + // in compact mode, each pointer assignment could result in a hash table request + // during move, we have to relocate document first_child and parents of all children + // normally there's just one child and its parent has a pointerless encoding but + // we assume the worst here + if (!other->_hash->reserve(other_children + 1)) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return; + #else + throw std::bad_alloc(); + #endif + } + } + #endif + + // move allocation state + doc->_root = other->_root; + doc->_busy_size = other->_busy_size; + + // move buffer state + doc->buffer = other->buffer; + doc->extra_buffers = other->extra_buffers; + _buffer = rhs._buffer; + + #ifdef PUGIXML_COMPACT + // move compact hash; note that the hash table can have pointers to other but they will be "inactive", similarly to nodes removed with remove_child + doc->hash = other->hash; + doc->_hash = &doc->hash; + + // make sure we don't access other hash up until the end when we reinitialize other document + other->_hash = 0; + #endif + + // move page structure + impl::xml_memory_page* doc_page = PUGI__GETPAGE(doc); + assert(doc_page && !doc_page->prev && !doc_page->next); + + impl::xml_memory_page* other_page = PUGI__GETPAGE(other); + assert(other_page && !other_page->prev); + + // relink pages since root page is embedded into xml_document + if (impl::xml_memory_page* page = other_page->next) + { + assert(page->prev == other_page); + + page->prev = doc_page; + + doc_page->next = page; + other_page->next = 0; + } + + // make sure pages point to the correct document state + for (impl::xml_memory_page* page = doc_page->next; page; page = page->next) + { + assert(page->allocator == other); + + page->allocator = doc; + + #ifdef PUGIXML_COMPACT + // this automatically migrates most children between documents and prevents ->parent assignment from allocating + if (page->compact_shared_parent == other) + page->compact_shared_parent = doc; + #endif + } + + // move tree structure + assert(!doc->first_child); + + doc->first_child = other_first_child; + + for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) + { + #ifdef PUGIXML_COMPACT + // most children will have migrated when we reassigned compact_shared_parent + assert(node->parent == other || node->parent == doc); + + node->parent = doc; + #else + assert(node->parent == other); + node->parent = doc; + #endif + } + + // reset other document + new (other) impl::xml_document_struct(PUGI__GETPAGE(other)); + rhs._buffer = 0; + } +#endif + +#ifndef PUGIXML_NO_STL + PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_stream_impl(static_cast(_root), stream, options, encoding, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) + { + reset(); + + return impl::load_stream_impl(static_cast(_root), stream, options, encoding_wchar, &_buffer); + } +#endif + + PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) + { + // Force native encoding (skip autodetection) + #ifdef PUGIXML_WCHAR_MODE + xml_encoding encoding = encoding_wchar; + #else + xml_encoding encoding = encoding_utf8; + #endif + + return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); + } + + PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) + { + return load_string(contents, options); + } + + PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) + { + reset(); + + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(fopen(path_, "rb"), impl::close_file); + + return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) + { + reset(); + + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(impl::open_file_wide(path_, L"rb"), impl::close_file); + + return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), size, options, encoding, false, false, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, false, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, true, &_buffer); + } + + PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + impl::xml_buffered_writer buffered_writer(writer, encoding); + + if ((flags & format_write_bom) && encoding != encoding_latin1) + { + // BOM always represents the codepoint U+FEFF, so just write it in native encoding + #ifdef PUGIXML_WCHAR_MODE + unsigned int bom = 0xfeff; + buffered_writer.write(static_cast(bom)); + #else + buffered_writer.write('\xef', '\xbb', '\xbf'); + #endif + } + + if (!(flags & format_no_declaration) && !impl::has_declaration(_root)) + { + buffered_writer.write_string(PUGIXML_TEXT("'); + if (!(flags & format_raw)) buffered_writer.write('\n'); + } + + impl::node_output(buffered_writer, _root, indent, flags, 0); + + buffered_writer.flush(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + xml_writer_stream writer(stream); + + save(writer, indent, flags, encoding); + } + + PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const + { + xml_writer_stream writer(stream); + + save(writer, indent, flags, encoding_wchar); + } +#endif + + PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); + + return impl::save_file_impl(*this, file.data, indent, flags, encoding); + } + + PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file); + + return impl::save_file_impl(*this, file.data, indent, flags, encoding); + } + + PUGI__FN xml_node xml_document::document_element() const + { + assert(_root); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (PUGI__NODETYPE(i) == node_element) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) + { + assert(str); + + return impl::as_utf8_impl(str, impl::strlength_wide(str)); + } + + PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) + { + return impl::as_utf8_impl(str.c_str(), str.size()); + } + + PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) + { + assert(str); + + return impl::as_wide_impl(str, strlen(str)); + } + + PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) + { + return impl::as_wide_impl(str.c_str(), str.size()); + } +#endif + + PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) + { + impl::xml_memory::allocate = allocate; + impl::xml_memory::deallocate = deallocate; + } + + PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() + { + return impl::xml_memory::allocate; + } + + PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() + { + return impl::xml_memory::deallocate; + } +} + +#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) +namespace std +{ + // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) + PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) +namespace std +{ + // Workarounds for (non-standard) iterator category detection + PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#ifndef PUGIXML_NO_XPATH +// STL replacements +PUGI__NS_BEGIN + struct equal_to + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs == rhs; + } + }; + + struct not_equal_to + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs != rhs; + } + }; + + struct less + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs < rhs; + } + }; + + struct less_equal + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs <= rhs; + } + }; + + template inline void swap(T& lhs, T& rhs) + { + T temp = lhs; + lhs = rhs; + rhs = temp; + } + + template PUGI__FN I min_element(I begin, I end, const Pred& pred) + { + I result = begin; + + for (I it = begin + 1; it != end; ++it) + if (pred(*it, *result)) + result = it; + + return result; + } + + template PUGI__FN void reverse(I begin, I end) + { + while (end - begin > 1) + swap(*begin++, *--end); + } + + template PUGI__FN I unique(I begin, I end) + { + // fast skip head + while (end - begin > 1 && *begin != *(begin + 1)) + begin++; + + if (begin == end) + return begin; + + // last written element + I write = begin++; + + // merge unique elements + while (begin != end) + { + if (*begin != *write) + *++write = *begin++; + else + begin++; + } + + // past-the-end (write points to live element) + return write + 1; + } + + template PUGI__FN void insertion_sort(T* begin, T* end, const Pred& pred) + { + if (begin == end) + return; + + for (T* it = begin + 1; it != end; ++it) + { + T val = *it; + T* hole = it; + + // move hole backwards + while (hole > begin && pred(val, *(hole - 1))) + { + *hole = *(hole - 1); + hole--; + } + + // fill hole with element + *hole = val; + } + } + + template inline I median3(I first, I middle, I last, const Pred& pred) + { + if (pred(*middle, *first)) + swap(middle, first); + if (pred(*last, *middle)) + swap(last, middle); + if (pred(*middle, *first)) + swap(middle, first); + + return middle; + } + + template PUGI__FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend) + { + // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups) + T* eq = begin; + T* lt = begin; + T* gt = end; + + while (lt < gt) + { + if (pred(*lt, pivot)) + lt++; + else if (*lt == pivot) + swap(*eq++, *lt++); + else + swap(*lt, *--gt); + } + + // we now have just 4 groups: = < >; move equal elements to the middle + T* eqbeg = gt; + + for (T* it = begin; it != eq; ++it) + swap(*it, *--eqbeg); + + *out_eqbeg = eqbeg; + *out_eqend = gt; + } + + template PUGI__FN void sort(I begin, I end, const Pred& pred) + { + // sort large chunks + while (end - begin > 16) + { + // find median element + I middle = begin + (end - begin) / 2; + I median = median3(begin, middle, end - 1, pred); + + // partition in three chunks (< = >) + I eqbeg, eqend; + partition3(begin, end, *median, pred, &eqbeg, &eqend); + + // loop on larger half + if (eqbeg - begin > end - eqend) + { + sort(eqend, end, pred); + end = eqbeg; + } + else + { + sort(begin, eqbeg, pred); + begin = eqend; + } + } + + // insertion sort small chunk + insertion_sort(begin, end, pred); + } + + PUGI__FN bool hash_insert(const void** table, size_t size, const void* key) + { + assert(key); + + unsigned int h = static_cast(reinterpret_cast(key)); + + // MurmurHash3 32-bit finalizer + h ^= h >> 16; + h *= 0x85ebca6bu; + h ^= h >> 13; + h *= 0xc2b2ae35u; + h ^= h >> 16; + + size_t hashmod = size - 1; + size_t bucket = h & hashmod; + + for (size_t probe = 0; probe <= hashmod; ++probe) + { + if (table[bucket] == 0) + { + table[bucket] = key; + return true; + } + + if (table[bucket] == key) + return false; + + // hash collision, quadratic probing + bucket = (bucket + probe + 1) & hashmod; + } + + assert(false && "Hash table is full"); // unreachable + return false; + } +PUGI__NS_END + +// Allocator used for AST and evaluation stacks +PUGI__NS_BEGIN + static const size_t xpath_memory_page_size = + #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE + PUGIXML_MEMORY_XPATH_PAGE_SIZE + #else + 4096 + #endif + ; + + static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*); + + struct xpath_memory_block + { + xpath_memory_block* next; + size_t capacity; + + union + { + char data[xpath_memory_page_size]; + double alignment; + }; + }; + + struct xpath_allocator + { + xpath_memory_block* _root; + size_t _root_size; + bool* _error; + + xpath_allocator(xpath_memory_block* root, bool* error = 0): _root(root), _root_size(0), _error(error) + { + } + + void* allocate(size_t size) + { + // round size up to block alignment boundary + size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + + if (_root_size + size <= _root->capacity) + { + void* buf = &_root->data[0] + _root_size; + _root_size += size; + return buf; + } + else + { + // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests + size_t block_capacity_base = sizeof(_root->data); + size_t block_capacity_req = size + block_capacity_base / 4; + size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req; + + size_t block_size = block_capacity + offsetof(xpath_memory_block, data); + + xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); + if (!block) + { + if (_error) *_error = true; + return 0; + } + + block->next = _root; + block->capacity = block_capacity; + + _root = block; + _root_size = size; + + return block->data; + } + } + + void* reallocate(void* ptr, size_t old_size, size_t new_size) + { + // round size up to block alignment boundary + old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + + // we can only reallocate the last object + assert(ptr == 0 || static_cast(ptr) + old_size == &_root->data[0] + _root_size); + + // try to reallocate the object inplace + if (ptr && _root_size - old_size + new_size <= _root->capacity) + { + _root_size = _root_size - old_size + new_size; + return ptr; + } + + // allocate a new block + void* result = allocate(new_size); + if (!result) return 0; + + // we have a new block + if (ptr) + { + // copy old data (we only support growing) + assert(new_size >= old_size); + memcpy(result, ptr, old_size); + + // free the previous page if it had no other objects + assert(_root->data == result); + assert(_root->next); + + if (_root->next->data == ptr) + { + // deallocate the whole page, unless it was the first one + xpath_memory_block* next = _root->next->next; + + if (next) + { + xml_memory::deallocate(_root->next); + _root->next = next; + } + } + } + + return result; + } + + void revert(const xpath_allocator& state) + { + // free all new pages + xpath_memory_block* cur = _root; + + while (cur != state._root) + { + xpath_memory_block* next = cur->next; + + xml_memory::deallocate(cur); + + cur = next; + } + + // restore state + _root = state._root; + _root_size = state._root_size; + } + + void release() + { + xpath_memory_block* cur = _root; + assert(cur); + + while (cur->next) + { + xpath_memory_block* next = cur->next; + + xml_memory::deallocate(cur); + + cur = next; + } + } + }; + + struct xpath_allocator_capture + { + xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) + { + } + + ~xpath_allocator_capture() + { + _target->revert(_state); + } + + xpath_allocator* _target; + xpath_allocator _state; + }; + + struct xpath_stack + { + xpath_allocator* result; + xpath_allocator* temp; + }; + + struct xpath_stack_data + { + xpath_memory_block blocks[2]; + xpath_allocator result; + xpath_allocator temp; + xpath_stack stack; + bool oom; + + xpath_stack_data(): result(blocks + 0, &oom), temp(blocks + 1, &oom), oom(false) + { + blocks[0].next = blocks[1].next = 0; + blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data); + + stack.result = &result; + stack.temp = &temp; + } + + ~xpath_stack_data() + { + result.release(); + temp.release(); + } + }; +PUGI__NS_END + +// String class +PUGI__NS_BEGIN + class xpath_string + { + const char_t* _buffer; + bool _uses_heap; + size_t _length_heap; + + static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) + { + char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); + if (!result) return 0; + + memcpy(result, string, length * sizeof(char_t)); + result[length] = 0; + + return result; + } + + xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap) + { + } + + public: + static xpath_string from_const(const char_t* str) + { + return xpath_string(str, false, 0); + } + + static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end) + { + assert(begin <= end && *end == 0); + + return xpath_string(begin, true, static_cast(end - begin)); + } + + static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc) + { + assert(begin <= end); + + if (begin == end) + return xpath_string(); + + size_t length = static_cast(end - begin); + const char_t* data = duplicate_string(begin, length, alloc); + + return data ? xpath_string(data, true, length) : xpath_string(); + } + + xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) + { + } + + void append(const xpath_string& o, xpath_allocator* alloc) + { + // skip empty sources + if (!*o._buffer) return; + + // fast append for constant empty target and constant source + if (!*_buffer && !_uses_heap && !o._uses_heap) + { + _buffer = o._buffer; + } + else + { + // need to make heap copy + size_t target_length = length(); + size_t source_length = o.length(); + size_t result_length = target_length + source_length; + + // allocate new buffer + char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); + if (!result) return; + + // append first string to the new buffer in case there was no reallocation + if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); + + // append second string to the new buffer + memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); + result[result_length] = 0; + + // finalize + _buffer = result; + _uses_heap = true; + _length_heap = result_length; + } + } + + const char_t* c_str() const + { + return _buffer; + } + + size_t length() const + { + return _uses_heap ? _length_heap : strlength(_buffer); + } + + char_t* data(xpath_allocator* alloc) + { + // make private heap copy + if (!_uses_heap) + { + size_t length_ = strlength(_buffer); + const char_t* data_ = duplicate_string(_buffer, length_, alloc); + + if (!data_) return 0; + + _buffer = data_; + _uses_heap = true; + _length_heap = length_; + } + + return const_cast(_buffer); + } + + bool empty() const + { + return *_buffer == 0; + } + + bool operator==(const xpath_string& o) const + { + return strequal(_buffer, o._buffer); + } + + bool operator!=(const xpath_string& o) const + { + return !strequal(_buffer, o._buffer); + } + + bool uses_heap() const + { + return _uses_heap; + } + }; +PUGI__NS_END + +PUGI__NS_BEGIN + PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) + { + while (*pattern && *string == *pattern) + { + string++; + pattern++; + } + + return *pattern == 0; + } + + PUGI__FN const char_t* find_char(const char_t* s, char_t c) + { + #ifdef PUGIXML_WCHAR_MODE + return wcschr(s, c); + #else + return strchr(s, c); + #endif + } + + PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p) + { + #ifdef PUGIXML_WCHAR_MODE + // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) + return (*p == 0) ? s : wcsstr(s, p); + #else + return strstr(s, p); + #endif + } + + // Converts symbol to lower case, if it is an ASCII one + PUGI__FN char_t tolower_ascii(char_t ch) + { + return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; + } + + PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) + { + if (na.attribute()) + return xpath_string::from_const(na.attribute().value()); + else + { + xml_node n = na.node(); + + switch (n.type()) + { + case node_pcdata: + case node_cdata: + case node_comment: + case node_pi: + return xpath_string::from_const(n.value()); + + case node_document: + case node_element: + { + xpath_string result; + + // element nodes can have value if parse_embed_pcdata was used + if (n.value()[0]) + result.append(xpath_string::from_const(n.value()), alloc); + + xml_node cur = n.first_child(); + + while (cur && cur != n) + { + if (cur.type() == node_pcdata || cur.type() == node_cdata) + result.append(xpath_string::from_const(cur.value()), alloc); + + if (cur.first_child()) + cur = cur.first_child(); + else if (cur.next_sibling()) + cur = cur.next_sibling(); + else + { + while (!cur.next_sibling() && cur != n) + cur = cur.parent(); + + if (cur != n) cur = cur.next_sibling(); + } + } + + return result; + } + + default: + return xpath_string(); + } + } + } + + PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) + { + assert(ln->parent == rn->parent); + + // there is no common ancestor (the shared parent is null), nodes are from different documents + if (!ln->parent) return ln < rn; + + // determine sibling order + xml_node_struct* ls = ln; + xml_node_struct* rs = rn; + + while (ls && rs) + { + if (ls == rn) return true; + if (rs == ln) return false; + + ls = ls->next_sibling; + rs = rs->next_sibling; + } + + // if rn sibling chain ended ln must be before rn + return !rs; + } + + PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) + { + // find common ancestor at the same depth, if any + xml_node_struct* lp = ln; + xml_node_struct* rp = rn; + + while (lp && rp && lp->parent != rp->parent) + { + lp = lp->parent; + rp = rp->parent; + } + + // parents are the same! + if (lp && rp) return node_is_before_sibling(lp, rp); + + // nodes are at different depths, need to normalize heights + bool left_higher = !lp; + + while (lp) + { + lp = lp->parent; + ln = ln->parent; + } + + while (rp) + { + rp = rp->parent; + rn = rn->parent; + } + + // one node is the ancestor of the other + if (ln == rn) return left_higher; + + // find common ancestor... again + while (ln->parent != rn->parent) + { + ln = ln->parent; + rn = rn->parent; + } + + return node_is_before_sibling(ln, rn); + } + + PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) + { + while (node && node != parent) node = node->parent; + + return parent && node == parent; + } + + PUGI__FN const void* document_buffer_order(const xpath_node& xnode) + { + xml_node_struct* node = xnode.node().internal_object(); + + if (node) + { + if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0) + { + if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name; + if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value; + } + + return 0; + } + + xml_attribute_struct* attr = xnode.attribute().internal_object(); + + if (attr) + { + if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0) + { + if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name; + if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value; + } + + return 0; + } + + return 0; + } + + struct document_order_comparator + { + bool operator()(const xpath_node& lhs, const xpath_node& rhs) const + { + // optimized document order based check + const void* lo = document_buffer_order(lhs); + const void* ro = document_buffer_order(rhs); + + if (lo && ro) return lo < ro; + + // slow comparison + xml_node ln = lhs.node(), rn = rhs.node(); + + // compare attributes + if (lhs.attribute() && rhs.attribute()) + { + // shared parent + if (lhs.parent() == rhs.parent()) + { + // determine sibling order + for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) + if (a == rhs.attribute()) + return true; + + return false; + } + + // compare attribute parents + ln = lhs.parent(); + rn = rhs.parent(); + } + else if (lhs.attribute()) + { + // attributes go after the parent element + if (lhs.parent() == rhs.node()) return false; + + ln = lhs.parent(); + } + else if (rhs.attribute()) + { + // attributes go after the parent element + if (rhs.parent() == lhs.node()) return true; + + rn = rhs.parent(); + } + + if (ln == rn) return false; + + if (!ln || !rn) return ln < rn; + + return node_is_before(ln.internal_object(), rn.internal_object()); + } + }; + + PUGI__FN double gen_nan() + { + #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) + PUGI__STATIC_ASSERT(sizeof(float) == sizeof(uint32_t)); + typedef uint32_t UI; // BCC5 workaround + union { float f; UI i; } u; + u.i = 0x7fc00000; + return double(u.f); + #else + // fallback + const volatile double zero = 0.0; + return zero / zero; + #endif + } + + PUGI__FN bool is_nan(double value) + { + #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) + return !!_isnan(value); + #elif defined(fpclassify) && defined(FP_NAN) + return fpclassify(value) == FP_NAN; + #else + // fallback + const volatile double v = value; + return v != v; + #endif + } + + PUGI__FN const char_t* convert_number_to_string_special(double value) + { + #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) + if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; + if (_isnan(value)) return PUGIXML_TEXT("NaN"); + return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); + #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) + switch (fpclassify(value)) + { + case FP_NAN: + return PUGIXML_TEXT("NaN"); + + case FP_INFINITE: + return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); + + case FP_ZERO: + return PUGIXML_TEXT("0"); + + default: + return 0; + } + #else + // fallback + const volatile double v = value; + + if (v == 0) return PUGIXML_TEXT("0"); + if (v != v) return PUGIXML_TEXT("NaN"); + if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); + return 0; + #endif + } + + PUGI__FN bool convert_number_to_boolean(double value) + { + return (value != 0 && !is_nan(value)); + } + + PUGI__FN void truncate_zeros(char* begin, char* end) + { + while (begin != end && end[-1] == '0') end--; + + *end = 0; + } + + // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent +#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) + PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) + { + // get base values + int sign, exponent; + _ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign); + + // truncate redundant zeros + truncate_zeros(buffer, buffer + strlen(buffer)); + + // fill results + *out_mantissa = buffer; + *out_exponent = exponent; + } +#else + PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) + { + // get a scientific notation value with IEEE DBL_DIG decimals + PUGI__SNPRINTF(buffer, "%.*e", DBL_DIG, value); + + // get the exponent (possibly negative) + char* exponent_string = strchr(buffer, 'e'); + assert(exponent_string); + + int exponent = atoi(exponent_string + 1); + + // extract mantissa string: skip sign + char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; + assert(mantissa[0] != '0' && mantissa[1] == '.'); + + // divide mantissa by 10 to eliminate integer part + mantissa[1] = mantissa[0]; + mantissa++; + exponent++; + + // remove extra mantissa digits and zero-terminate mantissa + truncate_zeros(mantissa, exponent_string); + + // fill results + *out_mantissa = mantissa; + *out_exponent = exponent; + } +#endif + + PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) + { + // try special number conversion + const char_t* special = convert_number_to_string_special(value); + if (special) return xpath_string::from_const(special); + + // get mantissa + exponent form + char mantissa_buffer[32]; + + char* mantissa; + int exponent; + convert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent); + + // allocate a buffer of suitable length for the number + size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; + char_t* result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); + if (!result) return xpath_string(); + + // make the number! + char_t* s = result; + + // sign + if (value < 0) *s++ = '-'; + + // integer part + if (exponent <= 0) + { + *s++ = '0'; + } + else + { + while (exponent > 0) + { + assert(*mantissa == 0 || static_cast(*mantissa - '0') <= 9); + *s++ = *mantissa ? *mantissa++ : '0'; + exponent--; + } + } + + // fractional part + if (*mantissa) + { + // decimal point + *s++ = '.'; + + // extra zeroes from negative exponent + while (exponent < 0) + { + *s++ = '0'; + exponent++; + } + + // extra mantissa digits + while (*mantissa) + { + assert(static_cast(*mantissa - '0') <= 9); + *s++ = *mantissa++; + } + } + + // zero-terminate + assert(s < result + result_size); + *s = 0; + + return xpath_string::from_heap_preallocated(result, s); + } + + PUGI__FN bool check_string_to_number_format(const char_t* string) + { + // parse leading whitespace + while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; + + // parse sign + if (*string == '-') ++string; + + if (!*string) return false; + + // if there is no integer part, there should be a decimal part with at least one digit + if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; + + // parse integer part + while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; + + // parse decimal part + if (*string == '.') + { + ++string; + + while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; + } + + // parse trailing whitespace + while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; + + return *string == 0; + } + + PUGI__FN double convert_string_to_number(const char_t* string) + { + // check string format + if (!check_string_to_number_format(string)) return gen_nan(); + + // parse string + #ifdef PUGIXML_WCHAR_MODE + return wcstod(string, 0); + #else + return strtod(string, 0); + #endif + } + + PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) + { + size_t length = static_cast(end - begin); + char_t* scratch = buffer; + + if (length >= sizeof(buffer) / sizeof(buffer[0])) + { + // need to make dummy on-heap copy + scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!scratch) return false; + } + + // copy string to zero-terminated buffer and perform conversion + memcpy(scratch, begin, length * sizeof(char_t)); + scratch[length] = 0; + + *out_result = convert_string_to_number(scratch); + + // free dummy buffer + if (scratch != buffer) xml_memory::deallocate(scratch); + + return true; + } + + PUGI__FN double round_nearest(double value) + { + return floor(value + 0.5); + } + + PUGI__FN double round_nearest_nzero(double value) + { + // same as round_nearest, but returns -0 for [-0.5, -0] + // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) + return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); + } + + PUGI__FN const char_t* qualified_name(const xpath_node& node) + { + return node.attribute() ? node.attribute().name() : node.node().name(); + } + + PUGI__FN const char_t* local_name(const xpath_node& node) + { + const char_t* name = qualified_name(node); + const char_t* p = find_char(name, ':'); + + return p ? p + 1 : name; + } + + struct namespace_uri_predicate + { + const char_t* prefix; + size_t prefix_length; + + namespace_uri_predicate(const char_t* name) + { + const char_t* pos = find_char(name, ':'); + + prefix = pos ? name : 0; + prefix_length = pos ? static_cast(pos - name) : 0; + } + + bool operator()(xml_attribute a) const + { + const char_t* name = a.name(); + + if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; + + return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; + } + }; + + PUGI__FN const char_t* namespace_uri(xml_node node) + { + namespace_uri_predicate pred = node.name(); + + xml_node p = node; + + while (p) + { + xml_attribute a = p.find_attribute(pred); + + if (a) return a.value(); + + p = p.parent(); + } + + return PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) + { + namespace_uri_predicate pred = attr.name(); + + // Default namespace does not apply to attributes + if (!pred.prefix) return PUGIXML_TEXT(""); + + xml_node p = parent; + + while (p) + { + xml_attribute a = p.find_attribute(pred); + + if (a) return a.value(); + + p = p.parent(); + } + + return PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* namespace_uri(const xpath_node& node) + { + return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); + } + + PUGI__FN char_t* normalize_space(char_t* buffer) + { + char_t* write = buffer; + + for (char_t* it = buffer; *it; ) + { + char_t ch = *it++; + + if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + // replace whitespace sequence with single space + while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; + + // avoid leading spaces + if (write != buffer) *write++ = ' '; + } + else *write++ = ch; + } + + // remove trailing space + if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; + + // zero-terminate + *write = 0; + + return write; + } + + PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) + { + char_t* write = buffer; + + while (*buffer) + { + PUGI__DMC_VOLATILE char_t ch = *buffer++; + + const char_t* pos = find_char(from, ch); + + if (!pos) + *write++ = ch; // do not process + else if (static_cast(pos - from) < to_length) + *write++ = to[pos - from]; // replace + } + + // zero-terminate + *write = 0; + + return write; + } + + PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) + { + unsigned char table[128] = {0}; + + while (*from) + { + unsigned int fc = static_cast(*from); + unsigned int tc = static_cast(*to); + + if (fc >= 128 || tc >= 128) + return 0; + + // code=128 means "skip character" + if (!table[fc]) + table[fc] = static_cast(tc ? tc : 128); + + from++; + if (tc) to++; + } + + for (int i = 0; i < 128; ++i) + if (!table[i]) + table[i] = static_cast(i); + + void* result = alloc->allocate(sizeof(table)); + if (!result) return 0; + + memcpy(result, table, sizeof(table)); + + return static_cast(result); + } + + PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table) + { + char_t* write = buffer; + + while (*buffer) + { + char_t ch = *buffer++; + unsigned int index = static_cast(ch); + + if (index < 128) + { + unsigned char code = table[index]; + + // code=128 means "skip character" (table size is 128 so 128 can be a special value) + // this code skips these characters without extra branches + *write = static_cast(code); + write += 1 - (code >> 7); + } + else + { + *write++ = ch; + } + } + + // zero-terminate + *write = 0; + + return write; + } + + inline bool is_xpath_attribute(const char_t* name) + { + return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')); + } + + struct xpath_variable_boolean: xpath_variable + { + xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false) + { + } + + bool value; + char_t name[1]; + }; + + struct xpath_variable_number: xpath_variable + { + xpath_variable_number(): xpath_variable(xpath_type_number), value(0) + { + } + + double value; + char_t name[1]; + }; + + struct xpath_variable_string: xpath_variable + { + xpath_variable_string(): xpath_variable(xpath_type_string), value(0) + { + } + + ~xpath_variable_string() + { + if (value) xml_memory::deallocate(value); + } + + char_t* value; + char_t name[1]; + }; + + struct xpath_variable_node_set: xpath_variable + { + xpath_variable_node_set(): xpath_variable(xpath_type_node_set) + { + } + + xpath_node_set value; + char_t name[1]; + }; + + static const xpath_node_set dummy_node_set; + + PUGI__FN PUGI__UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str) + { + // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) + unsigned int result = 0; + + while (*str) + { + result += static_cast(*str++); + result += result << 10; + result ^= result >> 6; + } + + result += result << 3; + result ^= result >> 11; + result += result << 15; + + return result; + } + + template PUGI__FN T* new_xpath_variable(const char_t* name) + { + size_t length = strlength(name); + if (length == 0) return 0; // empty variable names are invalid + + // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters + void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); + if (!memory) return 0; + + T* result = new (memory) T(); + + memcpy(result->name, name, (length + 1) * sizeof(char_t)); + + return result; + } + + PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) + { + switch (type) + { + case xpath_type_node_set: + return new_xpath_variable(name); + + case xpath_type_number: + return new_xpath_variable(name); + + case xpath_type_string: + return new_xpath_variable(name); + + case xpath_type_boolean: + return new_xpath_variable(name); + + default: + return 0; + } + } + + template PUGI__FN void delete_xpath_variable(T* var) + { + var->~T(); + xml_memory::deallocate(var); + } + + PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) + { + switch (type) + { + case xpath_type_node_set: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_number: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_string: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_boolean: + delete_xpath_variable(static_cast(var)); + break; + + default: + assert(false && "Invalid variable type"); // unreachable + } + } + + PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) + { + switch (rhs->type()) + { + case xpath_type_node_set: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_number: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_string: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_boolean: + return lhs->set(static_cast(rhs)->value); + + default: + assert(false && "Invalid variable type"); // unreachable + return false; + } + } + + PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) + { + size_t length = static_cast(end - begin); + char_t* scratch = buffer; + + if (length >= sizeof(buffer) / sizeof(buffer[0])) + { + // need to make dummy on-heap copy + scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!scratch) return false; + } + + // copy string to zero-terminated buffer and perform lookup + memcpy(scratch, begin, length * sizeof(char_t)); + scratch[length] = 0; + + *out_result = set->get(scratch); + + // free dummy buffer + if (scratch != buffer) xml_memory::deallocate(scratch); + + return true; + } +PUGI__NS_END + +// Internal node set class +PUGI__NS_BEGIN + PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) + { + if (end - begin < 2) + return xpath_node_set::type_sorted; + + document_order_comparator cmp; + + bool first = cmp(begin[0], begin[1]); + + for (const xpath_node* it = begin + 1; it + 1 < end; ++it) + if (cmp(it[0], it[1]) != first) + return xpath_node_set::type_unsorted; + + return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse; + } + + PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) + { + xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; + + if (type == xpath_node_set::type_unsorted) + { + xpath_node_set::type_t sorted = xpath_get_order(begin, end); + + if (sorted == xpath_node_set::type_unsorted) + { + sort(begin, end, document_order_comparator()); + + type = xpath_node_set::type_sorted; + } + else + type = sorted; + } + + if (type != order) reverse(begin, end); + + return order; + } + + PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) + { + if (begin == end) return xpath_node(); + + switch (type) + { + case xpath_node_set::type_sorted: + return *begin; + + case xpath_node_set::type_sorted_reverse: + return *(end - 1); + + case xpath_node_set::type_unsorted: + return *min_element(begin, end, document_order_comparator()); + + default: + assert(false && "Invalid node set type"); // unreachable + return xpath_node(); + } + } + + class xpath_node_set_raw + { + xpath_node_set::type_t _type; + + xpath_node* _begin; + xpath_node* _end; + xpath_node* _eos; + + public: + xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0) + { + } + + xpath_node* begin() const + { + return _begin; + } + + xpath_node* end() const + { + return _end; + } + + bool empty() const + { + return _begin == _end; + } + + size_t size() const + { + return static_cast(_end - _begin); + } + + xpath_node first() const + { + return xpath_first(_begin, _end, _type); + } + + void push_back_grow(const xpath_node& node, xpath_allocator* alloc); + + void push_back(const xpath_node& node, xpath_allocator* alloc) + { + if (_end != _eos) + *_end++ = node; + else + push_back_grow(node, alloc); + } + + void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) + { + if (begin_ == end_) return; + + size_t size_ = static_cast(_end - _begin); + size_t capacity = static_cast(_eos - _begin); + size_t count = static_cast(end_ - begin_); + + if (size_ + count > capacity) + { + // reallocate the old array or allocate a new one + xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); + if (!data) return; + + // finalize + _begin = data; + _end = data + size_; + _eos = data + size_ + count; + } + + memcpy(_end, begin_, count * sizeof(xpath_node)); + _end += count; + } + + void sort_do() + { + _type = xpath_sort(_begin, _end, _type, false); + } + + void truncate(xpath_node* pos) + { + assert(_begin <= pos && pos <= _end); + + _end = pos; + } + + void remove_duplicates(xpath_allocator* alloc) + { + if (_type == xpath_node_set::type_unsorted && _end - _begin > 2) + { + xpath_allocator_capture cr(alloc); + + size_t size_ = static_cast(_end - _begin); + + size_t hash_size = 1; + while (hash_size < size_ + size_ / 2) hash_size *= 2; + + const void** hash_data = static_cast(alloc->allocate(hash_size * sizeof(void**))); + if (!hash_data) return; + + memset(hash_data, 0, hash_size * sizeof(const void**)); + + xpath_node* write = _begin; + + for (xpath_node* it = _begin; it != _end; ++it) + { + const void* attr = it->attribute().internal_object(); + const void* node = it->node().internal_object(); + const void* key = attr ? attr : node; + + if (key && hash_insert(hash_data, hash_size, key)) + { + *write++ = *it; + } + } + + _end = write; + } + else + { + _end = unique(_begin, _end); + } + } + + xpath_node_set::type_t type() const + { + return _type; + } + + void set_type(xpath_node_set::type_t value) + { + _type = value; + } + }; + + PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) + { + size_t capacity = static_cast(_eos - _begin); + + // get new capacity (1.5x rule) + size_t new_capacity = capacity + capacity / 2 + 1; + + // reallocate the old array or allocate a new one + xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); + if (!data) return; + + // finalize + _begin = data; + _end = data + capacity; + _eos = data + new_capacity; + + // push + *_end++ = node; + } +PUGI__NS_END + +PUGI__NS_BEGIN + struct xpath_context + { + xpath_node n; + size_t position, size; + + xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) + { + } + }; + + enum lexeme_t + { + lex_none = 0, + lex_equal, + lex_not_equal, + lex_less, + lex_greater, + lex_less_or_equal, + lex_greater_or_equal, + lex_plus, + lex_minus, + lex_multiply, + lex_union, + lex_var_ref, + lex_open_brace, + lex_close_brace, + lex_quoted_string, + lex_number, + lex_slash, + lex_double_slash, + lex_open_square_brace, + lex_close_square_brace, + lex_string, + lex_comma, + lex_axis_attribute, + lex_dot, + lex_double_dot, + lex_double_colon, + lex_eof + }; + + struct xpath_lexer_string + { + const char_t* begin; + const char_t* end; + + xpath_lexer_string(): begin(0), end(0) + { + } + + bool operator==(const char_t* other) const + { + size_t length = static_cast(end - begin); + + return strequalrange(other, begin, length); + } + }; + + class xpath_lexer + { + const char_t* _cur; + const char_t* _cur_lexeme_pos; + xpath_lexer_string _cur_lexeme_contents; + + lexeme_t _cur_lexeme; + + public: + explicit xpath_lexer(const char_t* query): _cur(query) + { + next(); + } + + const char_t* state() const + { + return _cur; + } + + void next() + { + const char_t* cur = _cur; + + while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur; + + // save lexeme position for error reporting + _cur_lexeme_pos = cur; + + switch (*cur) + { + case 0: + _cur_lexeme = lex_eof; + break; + + case '>': + if (*(cur+1) == '=') + { + cur += 2; + _cur_lexeme = lex_greater_or_equal; + } + else + { + cur += 1; + _cur_lexeme = lex_greater; + } + break; + + case '<': + if (*(cur+1) == '=') + { + cur += 2; + _cur_lexeme = lex_less_or_equal; + } + else + { + cur += 1; + _cur_lexeme = lex_less; + } + break; + + case '!': + if (*(cur+1) == '=') + { + cur += 2; + _cur_lexeme = lex_not_equal; + } + else + { + _cur_lexeme = lex_none; + } + break; + + case '=': + cur += 1; + _cur_lexeme = lex_equal; + + break; + + case '+': + cur += 1; + _cur_lexeme = lex_plus; + + break; + + case '-': + cur += 1; + _cur_lexeme = lex_minus; + + break; + + case '*': + cur += 1; + _cur_lexeme = lex_multiply; + + break; + + case '|': + cur += 1; + _cur_lexeme = lex_union; + + break; + + case '$': + cur += 1; + + if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname + { + cur++; // : + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_var_ref; + } + else + { + _cur_lexeme = lex_none; + } + + break; + + case '(': + cur += 1; + _cur_lexeme = lex_open_brace; + + break; + + case ')': + cur += 1; + _cur_lexeme = lex_close_brace; + + break; + + case '[': + cur += 1; + _cur_lexeme = lex_open_square_brace; + + break; + + case ']': + cur += 1; + _cur_lexeme = lex_close_square_brace; + + break; + + case ',': + cur += 1; + _cur_lexeme = lex_comma; + + break; + + case '/': + if (*(cur+1) == '/') + { + cur += 2; + _cur_lexeme = lex_double_slash; + } + else + { + cur += 1; + _cur_lexeme = lex_slash; + } + break; + + case '.': + if (*(cur+1) == '.') + { + cur += 2; + _cur_lexeme = lex_double_dot; + } + else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit)) + { + _cur_lexeme_contents.begin = cur; // . + + ++cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_number; + } + else + { + cur += 1; + _cur_lexeme = lex_dot; + } + break; + + case '@': + cur += 1; + _cur_lexeme = lex_axis_attribute; + + break; + + case '"': + case '\'': + { + char_t terminator = *cur; + + ++cur; + + _cur_lexeme_contents.begin = cur; + while (*cur && *cur != terminator) cur++; + _cur_lexeme_contents.end = cur; + + if (!*cur) + _cur_lexeme = lex_none; + else + { + cur += 1; + _cur_lexeme = lex_quoted_string; + } + + break; + } + + case ':': + if (*(cur+1) == ':') + { + cur += 2; + _cur_lexeme = lex_double_colon; + } + else + { + _cur_lexeme = lex_none; + } + break; + + default: + if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) + { + _cur_lexeme_contents.begin = cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + + if (*cur == '.') + { + cur++; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_number; + } + else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':') + { + if (cur[1] == '*') // namespace test ncname:* + { + cur += 2; // :* + } + else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname + { + cur++; // : + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + } + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_string; + } + else + { + _cur_lexeme = lex_none; + } + } + + _cur = cur; + } + + lexeme_t current() const + { + return _cur_lexeme; + } + + const char_t* current_pos() const + { + return _cur_lexeme_pos; + } + + const xpath_lexer_string& contents() const + { + assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); + + return _cur_lexeme_contents; + } + }; + + enum ast_type_t + { + ast_unknown, + ast_op_or, // left or right + ast_op_and, // left and right + ast_op_equal, // left = right + ast_op_not_equal, // left != right + ast_op_less, // left < right + ast_op_greater, // left > right + ast_op_less_or_equal, // left <= right + ast_op_greater_or_equal, // left >= right + ast_op_add, // left + right + ast_op_subtract, // left - right + ast_op_multiply, // left * right + ast_op_divide, // left / right + ast_op_mod, // left % right + ast_op_negate, // left - right + ast_op_union, // left | right + ast_predicate, // apply predicate to set; next points to next predicate + ast_filter, // select * from left where right + ast_string_constant, // string constant + ast_number_constant, // number constant + ast_variable, // variable + ast_func_last, // last() + ast_func_position, // position() + ast_func_count, // count(left) + ast_func_id, // id(left) + ast_func_local_name_0, // local-name() + ast_func_local_name_1, // local-name(left) + ast_func_namespace_uri_0, // namespace-uri() + ast_func_namespace_uri_1, // namespace-uri(left) + ast_func_name_0, // name() + ast_func_name_1, // name(left) + ast_func_string_0, // string() + ast_func_string_1, // string(left) + ast_func_concat, // concat(left, right, siblings) + ast_func_starts_with, // starts_with(left, right) + ast_func_contains, // contains(left, right) + ast_func_substring_before, // substring-before(left, right) + ast_func_substring_after, // substring-after(left, right) + ast_func_substring_2, // substring(left, right) + ast_func_substring_3, // substring(left, right, third) + ast_func_string_length_0, // string-length() + ast_func_string_length_1, // string-length(left) + ast_func_normalize_space_0, // normalize-space() + ast_func_normalize_space_1, // normalize-space(left) + ast_func_translate, // translate(left, right, third) + ast_func_boolean, // boolean(left) + ast_func_not, // not(left) + ast_func_true, // true() + ast_func_false, // false() + ast_func_lang, // lang(left) + ast_func_number_0, // number() + ast_func_number_1, // number(left) + ast_func_sum, // sum(left) + ast_func_floor, // floor(left) + ast_func_ceiling, // ceiling(left) + ast_func_round, // round(left) + ast_step, // process set left with step + ast_step_root, // select root node + + ast_opt_translate_table, // translate(left, right, third) where right/third are constants + ast_opt_compare_attribute // @name = 'string' + }; + + enum axis_t + { + axis_ancestor, + axis_ancestor_or_self, + axis_attribute, + axis_child, + axis_descendant, + axis_descendant_or_self, + axis_following, + axis_following_sibling, + axis_namespace, + axis_parent, + axis_preceding, + axis_preceding_sibling, + axis_self + }; + + enum nodetest_t + { + nodetest_none, + nodetest_name, + nodetest_type_node, + nodetest_type_comment, + nodetest_type_pi, + nodetest_type_text, + nodetest_pi, + nodetest_all, + nodetest_all_in_namespace + }; + + enum predicate_t + { + predicate_default, + predicate_posinv, + predicate_constant, + predicate_constant_one + }; + + enum nodeset_eval_t + { + nodeset_eval_all, + nodeset_eval_any, + nodeset_eval_first + }; + + template struct axis_to_type + { + static const axis_t axis; + }; + + template const axis_t axis_to_type::axis = N; + + class xpath_ast_node + { + private: + // node type + char _type; + char _rettype; + + // for ast_step + char _axis; + + // for ast_step/ast_predicate/ast_filter + char _test; + + // tree node structure + xpath_ast_node* _left; + xpath_ast_node* _right; + xpath_ast_node* _next; + + union + { + // value for ast_string_constant + const char_t* string; + // value for ast_number_constant + double number; + // variable for ast_variable + xpath_variable* variable; + // node test for ast_step (node name/namespace/node type/pi target) + const char_t* nodetest; + // table for ast_opt_translate_table + const unsigned char* table; + } _data; + + xpath_ast_node(const xpath_ast_node&); + xpath_ast_node& operator=(const xpath_ast_node&); + + template static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) + { + xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); + + if (lt != xpath_type_node_set && rt != xpath_type_node_set) + { + if (lt == xpath_type_boolean || rt == xpath_type_boolean) + return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); + else if (lt == xpath_type_number || rt == xpath_type_number) + return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); + else if (lt == xpath_type_string || rt == xpath_type_string) + { + xpath_allocator_capture cr(stack.result); + + xpath_string ls = lhs->eval_string(c, stack); + xpath_string rs = rhs->eval_string(c, stack); + + return comp(ls, rs); + } + } + else if (lt == xpath_type_node_set && rt == xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) + return true; + } + + return false; + } + else + { + if (lt == xpath_type_node_set) + { + swap(lhs, rhs); + swap(lt, rt); + } + + if (lt == xpath_type_boolean) + return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); + else if (lt == xpath_type_number) + { + xpath_allocator_capture cr(stack.result); + + double l = lhs->eval_number(c, stack); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) + return true; + } + + return false; + } + else if (lt == xpath_type_string) + { + xpath_allocator_capture cr(stack.result); + + xpath_string l = lhs->eval_string(c, stack); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(l, string_value(*ri, stack.result))) + return true; + } + + return false; + } + } + + assert(false && "Wrong types"); // unreachable + return false; + } + + static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval) + { + return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any; + } + + template static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) + { + xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); + + if (lt != xpath_type_node_set && rt != xpath_type_node_set) + return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); + else if (lt == xpath_type_node_set && rt == xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) + { + xpath_allocator_capture cri(stack.result); + + double l = convert_string_to_number(string_value(*li, stack.result).c_str()); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture crii(stack.result); + + if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) + return true; + } + } + + return false; + } + else if (lt != xpath_type_node_set && rt == xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + double l = lhs->eval_number(c, stack); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) + return true; + } + + return false; + } + else if (lt == xpath_type_node_set && rt != xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); + double r = rhs->eval_number(c, stack); + + for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) + { + xpath_allocator_capture cri(stack.result); + + if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) + return true; + } + + return false; + } + else + { + assert(false && "Wrong types"); // unreachable + return false; + } + } + + static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) + { + assert(ns.size() >= first); + assert(expr->rettype() != xpath_type_number); + + size_t i = 1; + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + // remove_if... or well, sort of + for (xpath_node* it = last; it != ns.end(); ++it, ++i) + { + xpath_context c(*it, i, size); + + if (expr->eval_boolean(c, stack)) + { + *last++ = *it; + + if (once) break; + } + } + + ns.truncate(last); + } + + static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) + { + assert(ns.size() >= first); + assert(expr->rettype() == xpath_type_number); + + size_t i = 1; + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + // remove_if... or well, sort of + for (xpath_node* it = last; it != ns.end(); ++it, ++i) + { + xpath_context c(*it, i, size); + + if (expr->eval_number(c, stack) == i) + { + *last++ = *it; + + if (once) break; + } + } + + ns.truncate(last); + } + + static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) + { + assert(ns.size() >= first); + assert(expr->rettype() == xpath_type_number); + + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + xpath_context c(xpath_node(), 1, size); + + double er = expr->eval_number(c, stack); + + if (er >= 1.0 && er <= size) + { + size_t eri = static_cast(er); + + if (er == eri) + { + xpath_node r = last[eri - 1]; + + *last++ = r; + } + } + + ns.truncate(last); + } + + void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once) + { + if (ns.size() == first) return; + + assert(_type == ast_filter || _type == ast_predicate); + + if (_test == predicate_constant || _test == predicate_constant_one) + apply_predicate_number_const(ns, first, _right, stack); + else if (_right->rettype() == xpath_type_number) + apply_predicate_number(ns, first, _right, stack, once); + else + apply_predicate_boolean(ns, first, _right, stack, once); + } + + void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval) + { + if (ns.size() == first) return; + + bool last_once = eval_once(ns.type(), eval); + + for (xpath_ast_node* pred = _right; pred; pred = pred->_next) + pred->apply_predicate(ns, first, stack, !pred->_next && last_once); + } + + bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc) + { + assert(a); + + const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT(""); + + switch (_test) + { + case nodetest_name: + if (strequal(name, _data.nodetest) && is_xpath_attribute(name)) + { + ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); + return true; + } + break; + + case nodetest_type_node: + case nodetest_all: + if (is_xpath_attribute(name)) + { + ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); + return true; + } + break; + + case nodetest_all_in_namespace: + if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) + { + ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); + return true; + } + break; + + default: + ; + } + + return false; + } + + bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) + { + assert(n); + + xml_node_type type = PUGI__NODETYPE(n); + + switch (_test) + { + case nodetest_name: + if (type == node_element && n->name && strequal(n->name, _data.nodetest)) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_type_node: + ns.push_back(xml_node(n), alloc); + return true; + + case nodetest_type_comment: + if (type == node_comment) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_type_text: + if (type == node_pcdata || type == node_cdata) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_type_pi: + if (type == node_pi) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_pi: + if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_all: + if (type == node_element) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_all_in_namespace: + if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + default: + assert(false && "Unknown axis"); // unreachable + } + + return false; + } + + template void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T) + { + const axis_t axis = T::axis; + + switch (axis) + { + case axis_attribute: + { + for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) + if (step_push(ns, a, n, alloc) & once) + return; + + break; + } + + case axis_child: + { + for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) + if (step_push(ns, c, alloc) & once) + return; + + break; + } + + case axis_descendant: + case axis_descendant_or_self: + { + if (axis == axis_descendant_or_self) + if (step_push(ns, n, alloc) & once) + return; + + xml_node_struct* cur = n->first_child; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + if (cur->first_child) + cur = cur->first_child; + else + { + while (!cur->next_sibling) + { + cur = cur->parent; + + if (cur == n) return; + } + + cur = cur->next_sibling; + } + } + + break; + } + + case axis_following_sibling: + { + for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) + if (step_push(ns, c, alloc) & once) + return; + + break; + } + + case axis_preceding_sibling: + { + for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) + if (step_push(ns, c, alloc) & once) + return; + + break; + } + + case axis_following: + { + xml_node_struct* cur = n; + + // exit from this node so that we don't include descendants + while (!cur->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->next_sibling; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + if (cur->first_child) + cur = cur->first_child; + else + { + while (!cur->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->next_sibling; + } + } + + break; + } + + case axis_preceding: + { + xml_node_struct* cur = n; + + // exit from this node so that we don't include descendants + while (!cur->prev_sibling_c->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->prev_sibling_c; + + while (cur) + { + if (cur->first_child) + cur = cur->first_child->prev_sibling_c; + else + { + // leaf node, can't be ancestor + if (step_push(ns, cur, alloc) & once) + return; + + while (!cur->prev_sibling_c->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + + if (!node_is_ancestor(cur, n)) + if (step_push(ns, cur, alloc) & once) + return; + } + + cur = cur->prev_sibling_c; + } + } + + break; + } + + case axis_ancestor: + case axis_ancestor_or_self: + { + if (axis == axis_ancestor_or_self) + if (step_push(ns, n, alloc) & once) + return; + + xml_node_struct* cur = n->parent; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + cur = cur->parent; + } + + break; + } + + case axis_self: + { + step_push(ns, n, alloc); + + break; + } + + case axis_parent: + { + if (n->parent) + step_push(ns, n->parent, alloc); + + break; + } + + default: + assert(false && "Unimplemented axis"); // unreachable + } + } + + template void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) + { + const axis_t axis = T::axis; + + switch (axis) + { + case axis_ancestor: + case axis_ancestor_or_self: + { + if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test + if (step_push(ns, a, p, alloc) & once) + return; + + xml_node_struct* cur = p; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + cur = cur->parent; + } + + break; + } + + case axis_descendant_or_self: + case axis_self: + { + if (_test == nodetest_type_node) // reject attributes based on principal node type test + step_push(ns, a, p, alloc); + + break; + } + + case axis_following: + { + xml_node_struct* cur = p; + + while (cur) + { + if (cur->first_child) + cur = cur->first_child; + else + { + while (!cur->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->next_sibling; + } + + if (step_push(ns, cur, alloc) & once) + return; + } + + break; + } + + case axis_parent: + { + step_push(ns, p, alloc); + + break; + } + + case axis_preceding: + { + // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding + step_fill(ns, p, alloc, once, v); + break; + } + + default: + assert(false && "Unimplemented axis"); // unreachable + } + } + + template void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v) + { + const axis_t axis = T::axis; + const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); + + if (xn.node()) + step_fill(ns, xn.node().internal_object(), alloc, once, v); + else if (axis_has_attributes && xn.attribute() && xn.parent()) + step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v); + } + + template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v) + { + const axis_t axis = T::axis; + const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); + const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; + + bool once = + (axis == axis_attribute && _test == nodetest_name) || + (!_right && eval_once(axis_type, eval)) || + // coverity[mixed_enums] + (_right && !_right->_next && _right->_test == predicate_constant_one); + + xpath_node_set_raw ns; + ns.set_type(axis_type); + + if (_left) + { + xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all); + + // self axis preserves the original order + if (axis == axis_self) ns.set_type(s.type()); + + for (const xpath_node* it = s.begin(); it != s.end(); ++it) + { + size_t size = ns.size(); + + // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes + if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); + + step_fill(ns, *it, stack.result, once, v); + if (_right) apply_predicates(ns, size, stack, eval); + } + } + else + { + step_fill(ns, c.n, stack.result, once, v); + if (_right) apply_predicates(ns, 0, stack, eval); + } + + // child, attribute and self axes always generate unique set of nodes + // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice + if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) + ns.remove_duplicates(stack.temp); + + return ns; + } + + public: + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) + { + assert(type == ast_string_constant); + _data.string = value; + } + + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) + { + assert(type == ast_number_constant); + _data.number = value; + } + + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) + { + assert(type == ast_variable); + _data.variable = value; + } + + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) + { + } + + xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): + _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) + { + assert(type == ast_step); + _data.nodetest = contents; + } + + xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test): + _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast(test)), _left(left), _right(right), _next(0) + { + assert(type == ast_filter || type == ast_predicate); + } + + void set_next(xpath_ast_node* value) + { + _next = value; + } + + void set_right(xpath_ast_node* value) + { + _right = value; + } + + bool eval_boolean(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_op_or: + return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); + + case ast_op_and: + return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); + + case ast_op_equal: + return compare_eq(_left, _right, c, stack, equal_to()); + + case ast_op_not_equal: + return compare_eq(_left, _right, c, stack, not_equal_to()); + + case ast_op_less: + return compare_rel(_left, _right, c, stack, less()); + + case ast_op_greater: + return compare_rel(_right, _left, c, stack, less()); + + case ast_op_less_or_equal: + return compare_rel(_left, _right, c, stack, less_equal()); + + case ast_op_greater_or_equal: + return compare_rel(_right, _left, c, stack, less_equal()); + + case ast_func_starts_with: + { + xpath_allocator_capture cr(stack.result); + + xpath_string lr = _left->eval_string(c, stack); + xpath_string rr = _right->eval_string(c, stack); + + return starts_with(lr.c_str(), rr.c_str()); + } + + case ast_func_contains: + { + xpath_allocator_capture cr(stack.result); + + xpath_string lr = _left->eval_string(c, stack); + xpath_string rr = _right->eval_string(c, stack); + + return find_substring(lr.c_str(), rr.c_str()) != 0; + } + + case ast_func_boolean: + return _left->eval_boolean(c, stack); + + case ast_func_not: + return !_left->eval_boolean(c, stack); + + case ast_func_true: + return true; + + case ast_func_false: + return false; + + case ast_func_lang: + { + if (c.n.attribute()) return false; + + xpath_allocator_capture cr(stack.result); + + xpath_string lang = _left->eval_string(c, stack); + + for (xml_node n = c.n.node(); n; n = n.parent()) + { + xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); + + if (a) + { + const char_t* value = a.value(); + + // strnicmp / strncasecmp is not portable + for (const char_t* lit = lang.c_str(); *lit; ++lit) + { + if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; + ++value; + } + + return *value == 0 || *value == '-'; + } + } + + return false; + } + + case ast_opt_compare_attribute: + { + const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string(); + + xml_attribute attr = c.n.node().attribute(_left->_data.nodetest); + + return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name()); + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_boolean) + return _data.variable->get_boolean(); + } + + // fallthrough + default: + { + switch (_rettype) + { + case xpath_type_number: + return convert_number_to_boolean(eval_number(c, stack)); + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return !eval_string(c, stack).empty(); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return !eval_node_set(c, stack, nodeset_eval_any).empty(); + } + + default: + assert(false && "Wrong expression for return type boolean"); // unreachable + return false; + } + } + } + } + + double eval_number(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_op_add: + return _left->eval_number(c, stack) + _right->eval_number(c, stack); + + case ast_op_subtract: + return _left->eval_number(c, stack) - _right->eval_number(c, stack); + + case ast_op_multiply: + return _left->eval_number(c, stack) * _right->eval_number(c, stack); + + case ast_op_divide: + return _left->eval_number(c, stack) / _right->eval_number(c, stack); + + case ast_op_mod: + return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); + + case ast_op_negate: + return -_left->eval_number(c, stack); + + case ast_number_constant: + return _data.number; + + case ast_func_last: + return static_cast(c.size); + + case ast_func_position: + return static_cast(c.position); + + case ast_func_count: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(_left->eval_node_set(c, stack, nodeset_eval_all).size()); + } + + case ast_func_string_length_0: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(string_value(c.n, stack.result).length()); + } + + case ast_func_string_length_1: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(_left->eval_string(c, stack).length()); + } + + case ast_func_number_0: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(string_value(c.n, stack.result).c_str()); + } + + case ast_func_number_1: + return _left->eval_number(c, stack); + + case ast_func_sum: + { + xpath_allocator_capture cr(stack.result); + + double r = 0; + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) + { + xpath_allocator_capture cri(stack.result); + + r += convert_string_to_number(string_value(*it, stack.result).c_str()); + } + + return r; + } + + case ast_func_floor: + { + double r = _left->eval_number(c, stack); + + return r == r ? floor(r) : r; + } + + case ast_func_ceiling: + { + double r = _left->eval_number(c, stack); + + return r == r ? ceil(r) : r; + } + + case ast_func_round: + return round_nearest_nzero(_left->eval_number(c, stack)); + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_number) + return _data.variable->get_number(); + } + + // fallthrough + default: + { + switch (_rettype) + { + case xpath_type_boolean: + return eval_boolean(c, stack) ? 1 : 0; + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + default: + assert(false && "Wrong expression for return type number"); // unreachable + return 0; + } + + } + } + } + + xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) + { + assert(_type == ast_func_concat); + + xpath_allocator_capture ct(stack.temp); + + // count the string number + size_t count = 1; + for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; + + // allocate a buffer for temporary string objects + xpath_string* buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); + if (!buffer) return xpath_string(); + + // evaluate all strings to temporary stack + xpath_stack swapped_stack = {stack.temp, stack.result}; + + buffer[0] = _left->eval_string(c, swapped_stack); + + size_t pos = 1; + for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack); + assert(pos == count); + + // get total length + size_t length = 0; + for (size_t i = 0; i < count; ++i) length += buffer[i].length(); + + // create final string + char_t* result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); + if (!result) return xpath_string(); + + char_t* ri = result; + + for (size_t j = 0; j < count; ++j) + for (const char_t* bi = buffer[j].c_str(); *bi; ++bi) + *ri++ = *bi; + + *ri = 0; + + return xpath_string::from_heap_preallocated(result, ri); + } + + xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_string_constant: + return xpath_string::from_const(_data.string); + + case ast_func_local_name_0: + { + xpath_node na = c.n; + + return xpath_string::from_const(local_name(na)); + } + + case ast_func_local_name_1: + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); + xpath_node na = ns.first(); + + return xpath_string::from_const(local_name(na)); + } + + case ast_func_name_0: + { + xpath_node na = c.n; + + return xpath_string::from_const(qualified_name(na)); + } + + case ast_func_name_1: + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); + xpath_node na = ns.first(); + + return xpath_string::from_const(qualified_name(na)); + } + + case ast_func_namespace_uri_0: + { + xpath_node na = c.n; + + return xpath_string::from_const(namespace_uri(na)); + } + + case ast_func_namespace_uri_1: + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); + xpath_node na = ns.first(); + + return xpath_string::from_const(namespace_uri(na)); + } + + case ast_func_string_0: + return string_value(c.n, stack.result); + + case ast_func_string_1: + return _left->eval_string(c, stack); + + case ast_func_concat: + return eval_string_concat(c, stack); + + case ast_func_substring_before: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + xpath_string p = _right->eval_string(c, swapped_stack); + + const char_t* pos = find_substring(s.c_str(), p.c_str()); + + return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); + } + + case ast_func_substring_after: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + xpath_string p = _right->eval_string(c, swapped_stack); + + const char_t* pos = find_substring(s.c_str(), p.c_str()); + if (!pos) return xpath_string(); + + const char_t* rbegin = pos + p.length(); + const char_t* rend = s.c_str() + s.length(); + + return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); + } + + case ast_func_substring_2: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + size_t s_length = s.length(); + + double first = round_nearest(_right->eval_number(c, stack)); + + if (is_nan(first)) return xpath_string(); // NaN + else if (first >= s_length + 1) return xpath_string(); + + size_t pos = first < 1 ? 1 : static_cast(first); + assert(1 <= pos && pos <= s_length + 1); + + const char_t* rbegin = s.c_str() + (pos - 1); + const char_t* rend = s.c_str() + s.length(); + + return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); + } + + case ast_func_substring_3: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + size_t s_length = s.length(); + + double first = round_nearest(_right->eval_number(c, stack)); + double last = first + round_nearest(_right->_next->eval_number(c, stack)); + + if (is_nan(first) || is_nan(last)) return xpath_string(); + else if (first >= s_length + 1) return xpath_string(); + else if (first >= last) return xpath_string(); + else if (last < 1) return xpath_string(); + + size_t pos = first < 1 ? 1 : static_cast(first); + size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); + + assert(1 <= pos && pos <= end && end <= s_length + 1); + const char_t* rbegin = s.c_str() + (pos - 1); + const char_t* rend = s.c_str() + (end - 1); + + return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result); + } + + case ast_func_normalize_space_0: + { + xpath_string s = string_value(c.n, stack.result); + + char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + + char_t* end = normalize_space(begin); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_func_normalize_space_1: + { + xpath_string s = _left->eval_string(c, stack); + + char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + + char_t* end = normalize_space(begin); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_func_translate: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, stack); + xpath_string from = _right->eval_string(c, swapped_stack); + xpath_string to = _right->_next->eval_string(c, swapped_stack); + + char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + + char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_opt_translate_table: + { + xpath_string s = _left->eval_string(c, stack); + + char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + + char_t* end = translate_table(begin, _data.table); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_string) + return xpath_string::from_const(_data.variable->get_string()); + } + + // fallthrough + default: + { + switch (_rettype) + { + case xpath_type_boolean: + return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); + + case xpath_type_number: + return convert_number_to_string(eval_number(c, stack), stack.result); + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); + return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); + } + + default: + assert(false && "Wrong expression for return type string"); // unreachable + return xpath_string(); + } + } + } + } + + xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval) + { + switch (_type) + { + case ast_op_union: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_node_set_raw ls = _left->eval_node_set(c, stack, eval); + xpath_node_set_raw rs = _right->eval_node_set(c, swapped_stack, eval); + + // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother + ls.set_type(xpath_node_set::type_unsorted); + + ls.append(rs.begin(), rs.end(), stack.result); + ls.remove_duplicates(stack.temp); + + return ls; + } + + case ast_filter: + { + xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all); + + // either expression is a number or it contains position() call; sort by document order + if (_test != predicate_posinv) set.sort_do(); + + bool once = eval_once(set.type(), eval); + + apply_predicate(set, 0, stack, once); + + return set; + } + + case ast_func_id: + return xpath_node_set_raw(); + + case ast_step: + { + switch (_axis) + { + case axis_ancestor: + return step_do(c, stack, eval, axis_to_type()); + + case axis_ancestor_or_self: + return step_do(c, stack, eval, axis_to_type()); + + case axis_attribute: + return step_do(c, stack, eval, axis_to_type()); + + case axis_child: + return step_do(c, stack, eval, axis_to_type()); + + case axis_descendant: + return step_do(c, stack, eval, axis_to_type()); + + case axis_descendant_or_self: + return step_do(c, stack, eval, axis_to_type()); + + case axis_following: + return step_do(c, stack, eval, axis_to_type()); + + case axis_following_sibling: + return step_do(c, stack, eval, axis_to_type()); + + case axis_namespace: + // namespaced axis is not supported + return xpath_node_set_raw(); + + case axis_parent: + return step_do(c, stack, eval, axis_to_type()); + + case axis_preceding: + return step_do(c, stack, eval, axis_to_type()); + + case axis_preceding_sibling: + return step_do(c, stack, eval, axis_to_type()); + + case axis_self: + return step_do(c, stack, eval, axis_to_type()); + + default: + assert(false && "Unknown axis"); // unreachable + return xpath_node_set_raw(); + } + } + + case ast_step_root: + { + assert(!_right); // root step can't have any predicates + + xpath_node_set_raw ns; + + ns.set_type(xpath_node_set::type_sorted); + + if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); + else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); + + return ns; + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_node_set) + { + const xpath_node_set& s = _data.variable->get_node_set(); + + xpath_node_set_raw ns; + + ns.set_type(s.type()); + ns.append(s.begin(), s.end(), stack.result); + + return ns; + } + } + + // fallthrough + default: + assert(false && "Wrong expression for return type node set"); // unreachable + return xpath_node_set_raw(); + } + } + + void optimize(xpath_allocator* alloc) + { + if (_left) + _left->optimize(alloc); + + if (_right) + _right->optimize(alloc); + + if (_next) + _next->optimize(alloc); + + // coverity[var_deref_model] + optimize_self(alloc); + } + + void optimize_self(xpath_allocator* alloc) + { + // Rewrite [position()=expr] with [expr] + // Note that this step has to go before classification to recognize [position()=1] + if ((_type == ast_filter || _type == ast_predicate) && + _right && // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) + _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) + { + _right = _right->_right; + } + + // Classify filter/predicate ops to perform various optimizations during evaluation + if ((_type == ast_filter || _type == ast_predicate) && _right) // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) + { + assert(_test == predicate_default); + + if (_right->_type == ast_number_constant && _right->_data.number == 1.0) + _test = predicate_constant_one; + else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last)) + _test = predicate_constant; + else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) + _test = predicate_posinv; + } + + // Rewrite descendant-or-self::node()/child::foo with descendant::foo + // The former is a full form of //foo, the latter is much faster since it executes the node test immediately + // Do a similar kind of rewrite for self/descendant/descendant-or-self axes + // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) + if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && + _left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && + is_posinv_step()) + { + if (_axis == axis_child || _axis == axis_descendant) + _axis = axis_descendant; + else + _axis = axis_descendant_or_self; + + _left = _left->_left; + } + + // Use optimized lookup table implementation for translate() with constant arguments + if (_type == ast_func_translate && + _right && // workaround for clang static analyzer (_right is never null for ast_func_translate) + _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) + { + unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); + + if (table) + { + _type = ast_opt_translate_table; + _data.table = table; + } + } + + // Use optimized path for @attr = 'value' or @attr = $value + if (_type == ast_op_equal && + _left && _right && // workaround for clang static analyzer and Coverity (_left and _right are never null for ast_op_equal) + // coverity[mixed_enums] + _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && + (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) + { + _type = ast_opt_compare_attribute; + } + } + + bool is_posinv_expr() const + { + switch (_type) + { + case ast_func_position: + case ast_func_last: + return false; + + case ast_string_constant: + case ast_number_constant: + case ast_variable: + return true; + + case ast_step: + case ast_step_root: + return true; + + case ast_predicate: + case ast_filter: + return true; + + default: + if (_left && !_left->is_posinv_expr()) return false; + + for (xpath_ast_node* n = _right; n; n = n->_next) + if (!n->is_posinv_expr()) return false; + + return true; + } + } + + bool is_posinv_step() const + { + assert(_type == ast_step); + + for (xpath_ast_node* n = _right; n; n = n->_next) + { + assert(n->_type == ast_predicate); + + if (n->_test != predicate_posinv) + return false; + } + + return true; + } + + xpath_value_type rettype() const + { + return static_cast(_rettype); + } + }; + + struct xpath_parser + { + xpath_allocator* _alloc; + xpath_lexer _lexer; + + const char_t* _query; + xpath_variable_set* _variables; + + xpath_parse_result* _result; + + char_t _scratch[32]; + + xpath_ast_node* error(const char* message) + { + _result->error = message; + _result->offset = _lexer.current_pos() - _query; + + return 0; + } + + xpath_ast_node* error_oom() + { + assert(_alloc->_error); + *_alloc->_error = true; + + return 0; + } + + void* alloc_node() + { + return _alloc->allocate(sizeof(xpath_ast_node)); + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, left, right) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, left, right, test) : 0; + } + + const char_t* alloc_string(const xpath_lexer_string& value) + { + if (!value.begin) + return PUGIXML_TEXT(""); + + size_t length = static_cast(value.end - value.begin); + + char_t* c = static_cast(_alloc->allocate((length + 1) * sizeof(char_t))); + if (!c) return 0; + + memcpy(c, value.begin, length * sizeof(char_t)); + c[length] = 0; + + return c; + } + + xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) + { + switch (name.begin[0]) + { + case 'b': + if (name == PUGIXML_TEXT("boolean") && argc == 1) + return alloc_node(ast_func_boolean, xpath_type_boolean, args[0]); + + break; + + case 'c': + if (name == PUGIXML_TEXT("count") && argc == 1) + { + if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(ast_func_count, xpath_type_number, args[0]); + } + else if (name == PUGIXML_TEXT("contains") && argc == 2) + return alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); + else if (name == PUGIXML_TEXT("concat") && argc >= 2) + return alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("ceiling") && argc == 1) + return alloc_node(ast_func_ceiling, xpath_type_number, args[0]); + + break; + + case 'f': + if (name == PUGIXML_TEXT("false") && argc == 0) + return alloc_node(ast_func_false, xpath_type_boolean); + else if (name == PUGIXML_TEXT("floor") && argc == 1) + return alloc_node(ast_func_floor, xpath_type_number, args[0]); + + break; + + case 'i': + if (name == PUGIXML_TEXT("id") && argc == 1) + return alloc_node(ast_func_id, xpath_type_node_set, args[0]); + + break; + + case 'l': + if (name == PUGIXML_TEXT("last") && argc == 0) + return alloc_node(ast_func_last, xpath_type_number); + else if (name == PUGIXML_TEXT("lang") && argc == 1) + return alloc_node(ast_func_lang, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("local-name") && argc <= 1) + { + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]); + } + + break; + + case 'n': + if (name == PUGIXML_TEXT("name") && argc <= 1) + { + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]); + } + else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) + { + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]); + } + else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("not") && argc == 1) + return alloc_node(ast_func_not, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("number") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); + + break; + + case 'p': + if (name == PUGIXML_TEXT("position") && argc == 0) + return alloc_node(ast_func_position, xpath_type_number); + + break; + + case 'r': + if (name == PUGIXML_TEXT("round") && argc == 1) + return alloc_node(ast_func_round, xpath_type_number, args[0]); + + break; + + case 's': + if (name == PUGIXML_TEXT("string") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); + else if (name == PUGIXML_TEXT("string-length") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); + else if (name == PUGIXML_TEXT("starts-with") && argc == 2) + return alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-before") && argc == 2) + return alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-after") && argc == 2) + return alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) + return alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("sum") && argc == 1) + { + if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(ast_func_sum, xpath_type_number, args[0]); + } + + break; + + case 't': + if (name == PUGIXML_TEXT("translate") && argc == 3) + return alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("true") && argc == 0) + return alloc_node(ast_func_true, xpath_type_boolean); + + break; + + default: + break; + } + + return error("Unrecognized function or wrong parameter count"); + } + + axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) + { + specified = true; + + switch (name.begin[0]) + { + case 'a': + if (name == PUGIXML_TEXT("ancestor")) + return axis_ancestor; + else if (name == PUGIXML_TEXT("ancestor-or-self")) + return axis_ancestor_or_self; + else if (name == PUGIXML_TEXT("attribute")) + return axis_attribute; + + break; + + case 'c': + if (name == PUGIXML_TEXT("child")) + return axis_child; + + break; + + case 'd': + if (name == PUGIXML_TEXT("descendant")) + return axis_descendant; + else if (name == PUGIXML_TEXT("descendant-or-self")) + return axis_descendant_or_self; + + break; + + case 'f': + if (name == PUGIXML_TEXT("following")) + return axis_following; + else if (name == PUGIXML_TEXT("following-sibling")) + return axis_following_sibling; + + break; + + case 'n': + if (name == PUGIXML_TEXT("namespace")) + return axis_namespace; + + break; + + case 'p': + if (name == PUGIXML_TEXT("parent")) + return axis_parent; + else if (name == PUGIXML_TEXT("preceding")) + return axis_preceding; + else if (name == PUGIXML_TEXT("preceding-sibling")) + return axis_preceding_sibling; + + break; + + case 's': + if (name == PUGIXML_TEXT("self")) + return axis_self; + + break; + + default: + break; + } + + specified = false; + return axis_child; + } + + nodetest_t parse_node_test_type(const xpath_lexer_string& name) + { + switch (name.begin[0]) + { + case 'c': + if (name == PUGIXML_TEXT("comment")) + return nodetest_type_comment; + + break; + + case 'n': + if (name == PUGIXML_TEXT("node")) + return nodetest_type_node; + + break; + + case 'p': + if (name == PUGIXML_TEXT("processing-instruction")) + return nodetest_type_pi; + + break; + + case 't': + if (name == PUGIXML_TEXT("text")) + return nodetest_type_text; + + break; + + default: + break; + } + + return nodetest_none; + } + + // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall + xpath_ast_node* parse_primary_expression() + { + switch (_lexer.current()) + { + case lex_var_ref: + { + xpath_lexer_string name = _lexer.contents(); + + if (!_variables) + return error("Unknown variable: variable set is not provided"); + + xpath_variable* var = 0; + if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) + return error_oom(); + + if (!var) + return error("Unknown variable: variable set does not contain the given name"); + + _lexer.next(); + + return alloc_node(ast_variable, var->type(), var); + } + + case lex_open_brace: + { + _lexer.next(); + + xpath_ast_node* n = parse_expression(); + if (!n) return 0; + + if (_lexer.current() != lex_close_brace) + return error("Expected ')' to match an opening '('"); + + _lexer.next(); + + return n; + } + + case lex_quoted_string: + { + const char_t* value = alloc_string(_lexer.contents()); + if (!value) return 0; + + _lexer.next(); + + return alloc_node(ast_string_constant, xpath_type_string, value); + } + + case lex_number: + { + double value = 0; + + if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) + return error_oom(); + + _lexer.next(); + + return alloc_node(ast_number_constant, xpath_type_number, value); + } + + case lex_string: + { + xpath_ast_node* args[2] = {0}; + size_t argc = 0; + + xpath_lexer_string function = _lexer.contents(); + _lexer.next(); + + xpath_ast_node* last_arg = 0; + + if (_lexer.current() != lex_open_brace) + return error("Unrecognized function call"); + _lexer.next(); + + while (_lexer.current() != lex_close_brace) + { + if (argc > 0) + { + if (_lexer.current() != lex_comma) + return error("No comma between function arguments"); + _lexer.next(); + } + + xpath_ast_node* n = parse_expression(); + if (!n) return 0; + + if (argc < 2) args[argc] = n; + else last_arg->set_next(n); + + argc++; + last_arg = n; + } + + _lexer.next(); + + return parse_function(function, argc, args); + } + + default: + return error("Unrecognizable primary expression"); + } + } + + // FilterExpr ::= PrimaryExpr | FilterExpr Predicate + // Predicate ::= '[' PredicateExpr ']' + // PredicateExpr ::= Expr + xpath_ast_node* parse_filter_expression() + { + xpath_ast_node* n = parse_primary_expression(); + if (!n) return 0; + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + if (n->rettype() != xpath_type_node_set) + return error("Predicate has to be applied to node set"); + + xpath_ast_node* expr = parse_expression(); + if (!expr) return 0; + + n = alloc_node(ast_filter, n, expr, predicate_default); + if (!n) return 0; + + if (_lexer.current() != lex_close_square_brace) + return error("Expected ']' to match an opening '['"); + + _lexer.next(); + } + + return n; + } + + // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep + // AxisSpecifier ::= AxisName '::' | '@'? + // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' + // NameTest ::= '*' | NCName ':' '*' | QName + // AbbreviatedStep ::= '.' | '..' + xpath_ast_node* parse_step(xpath_ast_node* set) + { + if (set && set->rettype() != xpath_type_node_set) + return error("Step has to be applied to node set"); + + bool axis_specified = false; + axis_t axis = axis_child; // implied child axis + + if (_lexer.current() == lex_axis_attribute) + { + axis = axis_attribute; + axis_specified = true; + + _lexer.next(); + } + else if (_lexer.current() == lex_dot) + { + _lexer.next(); + + if (_lexer.current() == lex_open_square_brace) + return error("Predicates are not allowed after an abbreviated step"); + + return alloc_node(ast_step, set, axis_self, nodetest_type_node, 0); + } + else if (_lexer.current() == lex_double_dot) + { + _lexer.next(); + + if (_lexer.current() == lex_open_square_brace) + return error("Predicates are not allowed after an abbreviated step"); + + return alloc_node(ast_step, set, axis_parent, nodetest_type_node, 0); + } + + nodetest_t nt_type = nodetest_none; + xpath_lexer_string nt_name; + + if (_lexer.current() == lex_string) + { + // node name test + nt_name = _lexer.contents(); + _lexer.next(); + + // was it an axis name? + if (_lexer.current() == lex_double_colon) + { + // parse axis name + if (axis_specified) + return error("Two axis specifiers in one step"); + + axis = parse_axis_name(nt_name, axis_specified); + + if (!axis_specified) + return error("Unknown axis"); + + // read actual node test + _lexer.next(); + + if (_lexer.current() == lex_multiply) + { + nt_type = nodetest_all; + nt_name = xpath_lexer_string(); + _lexer.next(); + } + else if (_lexer.current() == lex_string) + { + nt_name = _lexer.contents(); + _lexer.next(); + } + else + { + return error("Unrecognized node test"); + } + } + + if (nt_type == nodetest_none) + { + // node type test or processing-instruction + if (_lexer.current() == lex_open_brace) + { + _lexer.next(); + + if (_lexer.current() == lex_close_brace) + { + _lexer.next(); + + nt_type = parse_node_test_type(nt_name); + + if (nt_type == nodetest_none) + return error("Unrecognized node type"); + + nt_name = xpath_lexer_string(); + } + else if (nt_name == PUGIXML_TEXT("processing-instruction")) + { + if (_lexer.current() != lex_quoted_string) + return error("Only literals are allowed as arguments to processing-instruction()"); + + nt_type = nodetest_pi; + nt_name = _lexer.contents(); + _lexer.next(); + + if (_lexer.current() != lex_close_brace) + return error("Unmatched brace near processing-instruction()"); + _lexer.next(); + } + else + { + return error("Unmatched brace near node type test"); + } + } + // QName or NCName:* + else + { + if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* + { + nt_name.end--; // erase * + + nt_type = nodetest_all_in_namespace; + } + else + { + nt_type = nodetest_name; + } + } + } + } + else if (_lexer.current() == lex_multiply) + { + nt_type = nodetest_all; + _lexer.next(); + } + else + { + return error("Unrecognized node test"); + } + + const char_t* nt_name_copy = alloc_string(nt_name); + if (!nt_name_copy) return 0; + + xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy); + if (!n) return 0; + + xpath_ast_node* last = 0; + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + xpath_ast_node* expr = parse_expression(); + if (!expr) return 0; + + xpath_ast_node* pred = alloc_node(ast_predicate, 0, expr, predicate_default); + if (!pred) return 0; + + if (_lexer.current() != lex_close_square_brace) + return error("Expected ']' to match an opening '['"); + _lexer.next(); + + if (last) last->set_next(pred); + else n->set_right(pred); + + last = pred; + } + + return n; + } + + // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step + xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) + { + xpath_ast_node* n = parse_step(set); + if (!n) return 0; + + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + if (l == lex_double_slash) + { + n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + if (!n) return 0; + } + + n = parse_step(n); + if (!n) return 0; + } + + return n; + } + + // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath + // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath + xpath_ast_node* parse_location_path() + { + if (_lexer.current() == lex_slash) + { + _lexer.next(); + + xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); + if (!n) return 0; + + // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path + lexeme_t l = _lexer.current(); + + if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) + return parse_relative_location_path(n); + else + return n; + } + else if (_lexer.current() == lex_double_slash) + { + _lexer.next(); + + xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); + if (!n) return 0; + + n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + if (!n) return 0; + + return parse_relative_location_path(n); + } + + // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 + return parse_relative_location_path(0); + } + + // PathExpr ::= LocationPath + // | FilterExpr + // | FilterExpr '/' RelativeLocationPath + // | FilterExpr '//' RelativeLocationPath + // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr + // UnaryExpr ::= UnionExpr | '-' UnaryExpr + xpath_ast_node* parse_path_or_unary_expression() + { + // Clarification. + // PathExpr begins with either LocationPath or FilterExpr. + // FilterExpr begins with PrimaryExpr + // PrimaryExpr begins with '$' in case of it being a variable reference, + // '(' in case of it being an expression, string literal, number constant or + // function call. + if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || + _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || + _lexer.current() == lex_string) + { + if (_lexer.current() == lex_string) + { + // This is either a function call, or not - if not, we shall proceed with location path + const char_t* state = _lexer.state(); + + while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; + + if (*state != '(') + return parse_location_path(); + + // This looks like a function call; however this still can be a node-test. Check it. + if (parse_node_test_type(_lexer.contents()) != nodetest_none) + return parse_location_path(); + } + + xpath_ast_node* n = parse_filter_expression(); + if (!n) return 0; + + if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + if (l == lex_double_slash) + { + if (n->rettype() != xpath_type_node_set) + return error("Step has to be applied to node set"); + + n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + if (!n) return 0; + } + + // select from location path + return parse_relative_location_path(n); + } + + return n; + } + else if (_lexer.current() == lex_minus) + { + _lexer.next(); + + // precedence 7+ - only parses union expressions + xpath_ast_node* n = parse_expression(7); + if (!n) return 0; + + return alloc_node(ast_op_negate, xpath_type_number, n); + } + else + { + return parse_location_path(); + } + } + + struct binary_op_t + { + ast_type_t asttype; + xpath_value_type rettype; + int precedence; + + binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0) + { + } + + binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_) + { + } + + static binary_op_t parse(xpath_lexer& lexer) + { + switch (lexer.current()) + { + case lex_string: + if (lexer.contents() == PUGIXML_TEXT("or")) + return binary_op_t(ast_op_or, xpath_type_boolean, 1); + else if (lexer.contents() == PUGIXML_TEXT("and")) + return binary_op_t(ast_op_and, xpath_type_boolean, 2); + else if (lexer.contents() == PUGIXML_TEXT("div")) + return binary_op_t(ast_op_divide, xpath_type_number, 6); + else if (lexer.contents() == PUGIXML_TEXT("mod")) + return binary_op_t(ast_op_mod, xpath_type_number, 6); + else + return binary_op_t(); + + case lex_equal: + return binary_op_t(ast_op_equal, xpath_type_boolean, 3); + + case lex_not_equal: + return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); + + case lex_less: + return binary_op_t(ast_op_less, xpath_type_boolean, 4); + + case lex_greater: + return binary_op_t(ast_op_greater, xpath_type_boolean, 4); + + case lex_less_or_equal: + return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); + + case lex_greater_or_equal: + return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); + + case lex_plus: + return binary_op_t(ast_op_add, xpath_type_number, 5); + + case lex_minus: + return binary_op_t(ast_op_subtract, xpath_type_number, 5); + + case lex_multiply: + return binary_op_t(ast_op_multiply, xpath_type_number, 6); + + case lex_union: + return binary_op_t(ast_op_union, xpath_type_node_set, 7); + + default: + return binary_op_t(); + } + } + }; + + xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit) + { + binary_op_t op = binary_op_t::parse(_lexer); + + while (op.asttype != ast_unknown && op.precedence >= limit) + { + _lexer.next(); + + xpath_ast_node* rhs = parse_path_or_unary_expression(); + if (!rhs) return 0; + + binary_op_t nextop = binary_op_t::parse(_lexer); + + while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) + { + rhs = parse_expression_rec(rhs, nextop.precedence); + if (!rhs) return 0; + + nextop = binary_op_t::parse(_lexer); + } + + if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) + return error("Union operator has to be applied to node sets"); + + lhs = alloc_node(op.asttype, op.rettype, lhs, rhs); + if (!lhs) return 0; + + op = binary_op_t::parse(_lexer); + } + + return lhs; + } + + // Expr ::= OrExpr + // OrExpr ::= AndExpr | OrExpr 'or' AndExpr + // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr + // EqualityExpr ::= RelationalExpr + // | EqualityExpr '=' RelationalExpr + // | EqualityExpr '!=' RelationalExpr + // RelationalExpr ::= AdditiveExpr + // | RelationalExpr '<' AdditiveExpr + // | RelationalExpr '>' AdditiveExpr + // | RelationalExpr '<=' AdditiveExpr + // | RelationalExpr '>=' AdditiveExpr + // AdditiveExpr ::= MultiplicativeExpr + // | AdditiveExpr '+' MultiplicativeExpr + // | AdditiveExpr '-' MultiplicativeExpr + // MultiplicativeExpr ::= UnaryExpr + // | MultiplicativeExpr '*' UnaryExpr + // | MultiplicativeExpr 'div' UnaryExpr + // | MultiplicativeExpr 'mod' UnaryExpr + xpath_ast_node* parse_expression(int limit = 0) + { + xpath_ast_node* n = parse_path_or_unary_expression(); + if (!n) return 0; + + return parse_expression_rec(n, limit); + } + + xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) + { + } + + xpath_ast_node* parse() + { + xpath_ast_node* n = parse_expression(); + if (!n) return 0; + + // check if there are unparsed tokens left + if (_lexer.current() != lex_eof) + return error("Incorrect query"); + + return n; + } + + static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) + { + xpath_parser parser(query, variables, alloc, result); + + return parser.parse(); + } + }; + + struct xpath_query_impl + { + static xpath_query_impl* create() + { + void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); + if (!memory) return 0; + + return new (memory) xpath_query_impl(); + } + + static void destroy(xpath_query_impl* impl) + { + // free all allocated pages + impl->alloc.release(); + + // free allocator memory (with the first page) + xml_memory::deallocate(impl); + } + + xpath_query_impl(): root(0), alloc(&block, &oom), oom(false) + { + block.next = 0; + block.capacity = sizeof(block.data); + } + + xpath_ast_node* root; + xpath_allocator alloc; + xpath_memory_block block; + bool oom; + }; + + PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) + { + if (!impl) return 0; + + if (impl->root->rettype() != xpath_type_node_set) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return 0; + #else + xpath_parse_result res; + res.error = "Expression does not evaluate to node set"; + + throw xpath_exception(res); + #endif + } + + return impl->root; + } +PUGI__NS_END + +namespace pugi +{ +#ifndef PUGIXML_NO_EXCEPTIONS + PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) + { + assert(_result.error); + } + + PUGI__FN const char* xpath_exception::what() const throw() + { + return _result.error; + } + + PUGI__FN const xpath_parse_result& xpath_exception::result() const + { + return _result; + } +#endif + + PUGI__FN xpath_node::xpath_node() + { + } + + PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) + { + } + + PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) + { + } + + PUGI__FN xml_node xpath_node::node() const + { + return _attribute ? xml_node() : _node; + } + + PUGI__FN xml_attribute xpath_node::attribute() const + { + return _attribute; + } + + PUGI__FN xml_node xpath_node::parent() const + { + return _attribute ? _node : _node.parent(); + } + + PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) + { + } + + PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const + { + return (_node || _attribute) ? unspecified_bool_xpath_node : 0; + } + + PUGI__FN bool xpath_node::operator!() const + { + return !(_node || _attribute); + } + + PUGI__FN bool xpath_node::operator==(const xpath_node& n) const + { + return _node == n._node && _attribute == n._attribute; + } + + PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const + { + return _node != n._node || _attribute != n._attribute; + } + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) + { + assert(begin_ <= end_); + + size_t size_ = static_cast(end_ - begin_); + + // use internal buffer for 0 or 1 elements, heap buffer otherwise + xpath_node* storage = (size_ <= 1) ? _storage : static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); + + if (!storage) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return; + #else + throw std::bad_alloc(); + #endif + } + + // deallocate old buffer + if (_begin != _storage) + impl::xml_memory::deallocate(_begin); + + // size check is necessary because for begin_ = end_ = nullptr, memcpy is UB + if (size_) + memcpy(storage, begin_, size_ * sizeof(xpath_node)); + + _begin = storage; + _end = storage + size_; + _type = type_; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT + { + _type = rhs._type; + _storage[0] = rhs._storage[0]; + _begin = (rhs._begin == rhs._storage) ? _storage : rhs._begin; + _end = _begin + (rhs._end - rhs._begin); + + rhs._type = type_unsorted; + rhs._begin = rhs._storage; + rhs._end = rhs._storage; + } +#endif + + PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage) + { + } + + PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(_storage), _end(_storage) + { + _assign(begin_, end_, type_); + } + + PUGI__FN xpath_node_set::~xpath_node_set() + { + if (_begin != _storage) + impl::xml_memory::deallocate(_begin); + } + + PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(_storage), _end(_storage) + { + _assign(ns._begin, ns._end, ns._type); + } + + PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) + { + if (this == &ns) return *this; + + _assign(ns._begin, ns._end, ns._type); + + return *this; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage) + { + _move(rhs); + } + + PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT + { + if (this == &rhs) return *this; + + if (_begin != _storage) + impl::xml_memory::deallocate(_begin); + + _move(rhs); + + return *this; + } +#endif + + PUGI__FN xpath_node_set::type_t xpath_node_set::type() const + { + return _type; + } + + PUGI__FN size_t xpath_node_set::size() const + { + return _end - _begin; + } + + PUGI__FN bool xpath_node_set::empty() const + { + return _begin == _end; + } + + PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const + { + assert(index < size()); + return _begin[index]; + } + + PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const + { + return _begin; + } + + PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const + { + return _end; + } + + PUGI__FN void xpath_node_set::sort(bool reverse) + { + _type = impl::xpath_sort(_begin, _end, _type, reverse); + } + + PUGI__FN xpath_node xpath_node_set::first() const + { + return impl::xpath_first(_begin, _end, _type); + } + + PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) + { + } + + PUGI__FN xpath_parse_result::operator bool() const + { + return error == 0; + } + + PUGI__FN const char* xpath_parse_result::description() const + { + return error ? error : "No error"; + } + + PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) + { + } + + PUGI__FN const char_t* xpath_variable::name() const + { + switch (_type) + { + case xpath_type_node_set: + return static_cast(this)->name; + + case xpath_type_number: + return static_cast(this)->name; + + case xpath_type_string: + return static_cast(this)->name; + + case xpath_type_boolean: + return static_cast(this)->name; + + default: + assert(false && "Invalid variable type"); // unreachable + return 0; + } + } + + PUGI__FN xpath_value_type xpath_variable::type() const + { + return _type; + } + + PUGI__FN bool xpath_variable::get_boolean() const + { + return (_type == xpath_type_boolean) ? static_cast(this)->value : false; + } + + PUGI__FN double xpath_variable::get_number() const + { + return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); + } + + PUGI__FN const char_t* xpath_variable::get_string() const + { + const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; + return value ? value : PUGIXML_TEXT(""); + } + + PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const + { + return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; + } + + PUGI__FN bool xpath_variable::set(bool value) + { + if (_type != xpath_type_boolean) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN bool xpath_variable::set(double value) + { + if (_type != xpath_type_number) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN bool xpath_variable::set(const char_t* value) + { + if (_type != xpath_type_string) return false; + + impl::xpath_variable_string* var = static_cast(this); + + // duplicate string + size_t size = (impl::strlength(value) + 1) * sizeof(char_t); + + char_t* copy = static_cast(impl::xml_memory::allocate(size)); + if (!copy) return false; + + memcpy(copy, value, size); + + // replace old string + if (var->value) impl::xml_memory::deallocate(var->value); + var->value = copy; + + return true; + } + + PUGI__FN bool xpath_variable::set(const xpath_node_set& value) + { + if (_type != xpath_type_node_set) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN xpath_variable_set::xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; + } + + PUGI__FN xpath_variable_set::~xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _destroy(_data[i]); + } + + PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; + + _assign(rhs); + } + + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) + { + if (this == &rhs) return *this; + + _assign(rhs); + + return *this; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } + } + + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + _destroy(_data[i]); + + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } + + return *this; + } +#endif + + PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) + { + xpath_variable_set temp; + + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i])) + return; + + _swap(temp); + } + + PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + xpath_variable* chain = _data[i]; + + _data[i] = rhs._data[i]; + rhs._data[i] = chain; + } + } + + PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const + { + const size_t hash_size = sizeof(_data) / sizeof(_data[0]); + size_t hash = impl::hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (impl::strequal(var->name(), name)) + return var; + + return 0; + } + + PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) + { + xpath_variable* last = 0; + + while (var) + { + // allocate storage for new variable + xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name()); + if (!nvar) return false; + + // link the variable to the result immediately to handle failures gracefully + if (last) + last->_next = nvar; + else + *out_result = nvar; + + last = nvar; + + // copy the value; this can fail due to out-of-memory conditions + if (!impl::copy_xpath_variable(nvar, var)) return false; + + var = var->_next; + } + + return true; + } + + PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var) + { + while (var) + { + xpath_variable* next = var->_next; + + impl::delete_xpath_variable(var->_type, var); + + var = next; + } + } + + PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) + { + const size_t hash_size = sizeof(_data) / sizeof(_data[0]); + size_t hash = impl::hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (impl::strequal(var->name(), name)) + return var->type() == type ? var : 0; + + // add new variable + xpath_variable* result = impl::new_xpath_variable(type, name); + + if (result) + { + result->_next = _data[hash]; + + _data[hash] = result; + } + + return result; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value) + { + xpath_variable* var = add(name, xpath_type_boolean); + return var ? var->set(value) : false; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, double value) + { + xpath_variable* var = add(name, xpath_type_number); + return var ? var->set(value) : false; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value) + { + xpath_variable* var = add(name, xpath_type_string); + return var ? var->set(value) : false; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) + { + xpath_variable* var = add(name, xpath_type_node_set); + return var ? var->set(value) : false; + } + + PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) + { + return _find(name); + } + + PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const + { + return _find(name); + } + + PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) + { + impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); + + if (!qimpl) + { + #ifdef PUGIXML_NO_EXCEPTIONS + _result.error = "Out of memory"; + #else + throw std::bad_alloc(); + #endif + } + else + { + using impl::auto_deleter; // MSVC7 workaround + auto_deleter impl(qimpl, impl::xpath_query_impl::destroy); + + qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); + + if (qimpl->root) + { + qimpl->root->optimize(&qimpl->alloc); + + _impl = impl.release(); + _result.error = 0; + } + else + { + #ifdef PUGIXML_NO_EXCEPTIONS + if (qimpl->oom) _result.error = "Out of memory"; + #else + if (qimpl->oom) throw std::bad_alloc(); + throw xpath_exception(_result); + #endif + } + } + } + + PUGI__FN xpath_query::xpath_query(): _impl(0) + { + } + + PUGI__FN xpath_query::~xpath_query() + { + if (_impl) + impl::xpath_query_impl::destroy(static_cast(_impl)); + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT + { + _impl = rhs._impl; + _result = rhs._result; + rhs._impl = 0; + rhs._result = xpath_parse_result(); + } + + PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT + { + if (this == &rhs) return *this; + + if (_impl) + impl::xpath_query_impl::destroy(static_cast(_impl)); + + _impl = rhs._impl; + _result = rhs._result; + rhs._impl = 0; + rhs._result = xpath_parse_result(); + + return *this; + } +#endif + + PUGI__FN xpath_value_type xpath_query::return_type() const + { + if (!_impl) return xpath_type_none; + + return static_cast(_impl)->root->rettype(); + } + + PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const + { + if (!_impl) return false; + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + bool r = static_cast(_impl)->root->eval_boolean(c, sd.stack); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return false; + #else + throw std::bad_alloc(); + #endif + } + + return r; + } + + PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const + { + if (!_impl) return impl::gen_nan(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + double r = static_cast(_impl)->root->eval_number(c, sd.stack); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return impl::gen_nan(); + #else + throw std::bad_alloc(); + #endif + } + + return r; + } + +#ifndef PUGIXML_NO_STL + PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const + { + if (!_impl) return string_t(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + impl::xpath_string r = static_cast(_impl)->root->eval_string(c, sd.stack); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return string_t(); + #else + throw std::bad_alloc(); + #endif + } + + return string_t(r.c_str(), r.length()); + } +#endif + + PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const + { + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + impl::xpath_string r = _impl ? static_cast(_impl)->root->eval_string(c, sd.stack) : impl::xpath_string(); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + r = impl::xpath_string(); + #else + throw std::bad_alloc(); + #endif + } + + size_t full_size = r.length() + 1; + + if (capacity > 0) + { + size_t size = (full_size < capacity) ? full_size : capacity; + assert(size > 0); + + memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); + buffer[size - 1] = 0; + } + + return full_size; + } + + PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const + { + impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); + if (!root) return xpath_node_set(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return xpath_node_set(); + #else + throw std::bad_alloc(); + #endif + } + + return xpath_node_set(r.begin(), r.end(), r.type()); + } + + PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const + { + impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); + if (!root) return xpath_node(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return xpath_node(); + #else + throw std::bad_alloc(); + #endif + } + + return r.first(); + } + + PUGI__FN const xpath_parse_result& xpath_query::result() const + { + return _result; + } + + PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) + { + } + + PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const + { + return _impl ? unspecified_bool_xpath_query : 0; + } + + PUGI__FN bool xpath_query::operator!() const + { + return !_impl; + } + + PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return q.evaluate_node(*this); + } + + PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const + { + return query.evaluate_node(*this); + } + + PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return q.evaluate_node_set(*this); + } + + PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const + { + return query.evaluate_node_set(*this); + } + + PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return q.evaluate_node(*this); + } + + PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const + { + return query.evaluate_node(*this); + } +} + +#endif + +#ifdef __BORLANDC__ +# pragma option pop +#endif + +// Intel C++ does not properly keep warning state for function templates, +// so popping warning state at the end of translation unit leads to warnings in the middle. +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# pragma warning(pop) +#endif + +#if defined(_MSC_VER) && defined(__c2__) +# pragma clang diagnostic pop +#endif + +// Undefine all local macros (makes sure we're not leaking macros in header-only mode) +#undef PUGI__NO_INLINE +#undef PUGI__UNLIKELY +#undef PUGI__STATIC_ASSERT +#undef PUGI__DMC_VOLATILE +#undef PUGI__UNSIGNED_OVERFLOW +#undef PUGI__MSVC_CRT_VERSION +#undef PUGI__SNPRINTF +#undef PUGI__NS_BEGIN +#undef PUGI__NS_END +#undef PUGI__FN +#undef PUGI__FN_NO_INLINE +#undef PUGI__GETHEADER_IMPL +#undef PUGI__GETPAGE_IMPL +#undef PUGI__GETPAGE +#undef PUGI__NODETYPE +#undef PUGI__IS_CHARTYPE_IMPL +#undef PUGI__IS_CHARTYPE +#undef PUGI__IS_CHARTYPEX +#undef PUGI__ENDSWITH +#undef PUGI__SKIPWS +#undef PUGI__OPTSET +#undef PUGI__PUSHNODE +#undef PUGI__POPNODE +#undef PUGI__SCANFOR +#undef PUGI__SCANWHILE +#undef PUGI__SCANWHILE_UNROLL +#undef PUGI__ENDSEG +#undef PUGI__THROW_ERROR +#undef PUGI__CHECK_ERROR + +#endif + +/** + * Copyright (c) 2006-2019 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/src/3rd-party/pugixml/pugixml.hpp b/src/3rd-party/pugixml/pugixml.hpp new file mode 100644 index 00000000..da8ff36c --- /dev/null +++ b/src/3rd-party/pugixml/pugixml.hpp @@ -0,0 +1,1477 @@ +/** + * pugixml parser - version 1.10 + * -------------------------------------------------------- + * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +#ifndef PUGIXML_VERSION +// Define version macro; evaluates to major * 1000 + minor * 10 + patch so that it's safe to use in less-than comparisons +// Note: pugixml used major * 100 + minor * 10 + patch format up until 1.9 (which had version identifier 190); starting from pugixml 1.10, the minor version number is two digits +# define PUGIXML_VERSION 1100 +#endif + +// Include user configuration file (this can define various configuration macros) +#include "pugiconfig.hpp" + +#ifndef HEADER_PUGIXML_HPP +#define HEADER_PUGIXML_HPP + +// Include stddef.h for size_t and ptrdiff_t +#include + +// Include exception header for XPath +#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) +# include +#endif + +// Include STL headers +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// Macro for deprecated features +#ifndef PUGIXML_DEPRECATED +# if defined(__GNUC__) +# define PUGIXML_DEPRECATED __attribute__((deprecated)) +# elif defined(_MSC_VER) && _MSC_VER >= 1300 +# define PUGIXML_DEPRECATED __declspec(deprecated) +# else +# define PUGIXML_DEPRECATED +# endif +#endif + +// If no API is defined, assume default +#ifndef PUGIXML_API +# define PUGIXML_API +#endif + +// If no API for classes is defined, assume default +#ifndef PUGIXML_CLASS +# define PUGIXML_CLASS PUGIXML_API +#endif + +// If no API for functions is defined, assume default +#ifndef PUGIXML_FUNCTION +# define PUGIXML_FUNCTION PUGIXML_API +#endif + +// If the platform is known to have long long support, enable long long functions +#ifndef PUGIXML_HAS_LONG_LONG +# if __cplusplus >= 201103 +# define PUGIXML_HAS_LONG_LONG +# elif defined(_MSC_VER) && _MSC_VER >= 1400 +# define PUGIXML_HAS_LONG_LONG +# endif +#endif + +// If the platform is known to have move semantics support, compile move ctor/operator implementation +#ifndef PUGIXML_HAS_MOVE +# if __cplusplus >= 201103 +# define PUGIXML_HAS_MOVE +# elif defined(_MSC_VER) && _MSC_VER >= 1600 +# define PUGIXML_HAS_MOVE +# endif +#endif + +// If C++ is 2011 or higher, add 'noexcept' specifiers +#ifndef PUGIXML_NOEXCEPT +# if __cplusplus >= 201103 +# define PUGIXML_NOEXCEPT noexcept +# elif defined(_MSC_VER) && _MSC_VER >= 1900 +# define PUGIXML_NOEXCEPT noexcept +# else +# define PUGIXML_NOEXCEPT +# endif +#endif + +// Some functions can not be noexcept in compact mode +#ifdef PUGIXML_COMPACT +# define PUGIXML_NOEXCEPT_IF_NOT_COMPACT +#else +# define PUGIXML_NOEXCEPT_IF_NOT_COMPACT PUGIXML_NOEXCEPT +#endif + +// If C++ is 2011 or higher, add 'override' qualifiers +#ifndef PUGIXML_OVERRIDE +# if __cplusplus >= 201103 +# define PUGIXML_OVERRIDE override +# elif defined(_MSC_VER) && _MSC_VER >= 1700 +# define PUGIXML_OVERRIDE override +# else +# define PUGIXML_OVERRIDE +# endif +#endif + +// Character interface macros +#ifdef PUGIXML_WCHAR_MODE +# define PUGIXML_TEXT(t) L ## t +# define PUGIXML_CHAR wchar_t +#else +# define PUGIXML_TEXT(t) t +# define PUGIXML_CHAR char +#endif + +namespace pugi +{ + // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE + typedef PUGIXML_CHAR char_t; + +#ifndef PUGIXML_NO_STL + // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE + typedef std::basic_string, std::allocator > string_t; +#endif +} + +// The PugiXML namespace +namespace pugi +{ + // Tree node types + enum xml_node_type + { + node_null, // Empty (null) node handle + node_document, // A document tree's absolute root + node_element, // Element tag, i.e. '' + node_pcdata, // Plain character data, i.e. 'text' + node_cdata, // Character data, i.e. '' + node_comment, // Comment tag, i.e. '' + node_pi, // Processing instruction, i.e. '' + node_declaration, // Document declaration, i.e. '' + node_doctype // Document type declaration, i.e. '' + }; + + // Parsing options + + // Minimal parsing mode (equivalent to turning all other flags off). + // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. + const unsigned int parse_minimal = 0x0000; + + // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. + const unsigned int parse_pi = 0x0001; + + // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. + const unsigned int parse_comments = 0x0002; + + // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. + const unsigned int parse_cdata = 0x0004; + + // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. + // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. + const unsigned int parse_ws_pcdata = 0x0008; + + // This flag determines if character and entity references are expanded during parsing. This flag is on by default. + const unsigned int parse_escapes = 0x0010; + + // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. + const unsigned int parse_eol = 0x0020; + + // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. + const unsigned int parse_wconv_attribute = 0x0040; + + // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. + const unsigned int parse_wnorm_attribute = 0x0080; + + // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. + const unsigned int parse_declaration = 0x0100; + + // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. + const unsigned int parse_doctype = 0x0200; + + // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only + // of whitespace is added to the DOM tree. + // This flag is off by default; turning it on may result in slower parsing and more memory consumption. + const unsigned int parse_ws_pcdata_single = 0x0400; + + // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. + const unsigned int parse_trim_pcdata = 0x0800; + + // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document + // is a valid document. This flag is off by default. + const unsigned int parse_fragment = 0x1000; + + // This flag determines if plain character data is be stored in the parent element's value. This significantly changes the structure of + // the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments. + // This flag is off by default. + const unsigned int parse_embed_pcdata = 0x2000; + + // The default parsing mode. + // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. + const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; + + // The full parsing mode. + // Nodes of all types are added to the DOM tree, character/reference entities are expanded, + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. + const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; + + // These flags determine the encoding of input data for XML document + enum xml_encoding + { + encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range + { + public: + typedef It const_iterator; + typedef It iterator; + + xml_object_range(It b, It e): _begin(b), _end(e) + { + } + + It begin() const { return _begin; } + It end() const { return _end; } + + private: + It _begin, _end; + }; + + // Writer interface for node printing (see xml_node::print) + class PUGIXML_CLASS xml_writer + { + public: + virtual ~xml_writer() {} + + // Write memory chunk into stream/file/whatever + virtual void write(const void* data, size_t size) = 0; + }; + + // xml_writer implementation for FILE* + class PUGIXML_CLASS xml_writer_file: public xml_writer + { + public: + // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio + xml_writer_file(void* file); + + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; + + private: + void* file; + }; + + #ifndef PUGIXML_NO_STL + // xml_writer implementation for streams + class PUGIXML_CLASS xml_writer_stream: public xml_writer + { + public: + // Construct writer from an output stream object + xml_writer_stream(std::basic_ostream >& stream); + xml_writer_stream(std::basic_ostream >& stream); + + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; + + private: + std::basic_ostream >* narrow_stream; + std::basic_ostream >* wide_stream; + }; + #endif + + // A light-weight handle for manipulating attributes in DOM tree + class PUGIXML_CLASS xml_attribute + { + friend class xml_attribute_iterator; + friend class xml_node; + + private: + xml_attribute_struct* _attr; + + typedef void (*unspecified_bool_type)(xml_attribute***); + + public: + // Default constructor. Constructs an empty attribute. + xml_attribute(); + + // Constructs attribute from internal pointer + explicit xml_attribute(xml_attribute_struct* attr); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators (compares wrapped attribute pointers) + bool operator==(const xml_attribute& r) const; + bool operator!=(const xml_attribute& r) const; + bool operator<(const xml_attribute& r) const; + bool operator>(const xml_attribute& r) const; + bool operator<=(const xml_attribute& r) const; + bool operator>=(const xml_attribute& r) const; + + // Check if attribute is empty + bool empty() const; + + // Get attribute name/value, or "" if attribute is empty + const char_t* name() const; + const char_t* value() const; + + // Get attribute value, or the default value if attribute is empty + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; + + // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; + + #ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; + #endif + + // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty + bool as_bool(bool def = false) const; + + // Set attribute name/value (returns false if attribute is empty or there is not enough memory) + bool set_name(const char_t* rhs); + bool set_value(const char_t* rhs); + + // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") + bool set_value(int rhs); + bool set_value(unsigned int rhs); + bool set_value(long rhs); + bool set_value(unsigned long rhs); + bool set_value(double rhs); + bool set_value(float rhs); + bool set_value(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + bool set_value(long long rhs); + bool set_value(unsigned long long rhs); + #endif + + // Set attribute value (equivalent to set_value without error checking) + xml_attribute& operator=(const char_t* rhs); + xml_attribute& operator=(int rhs); + xml_attribute& operator=(unsigned int rhs); + xml_attribute& operator=(long rhs); + xml_attribute& operator=(unsigned long rhs); + xml_attribute& operator=(double rhs); + xml_attribute& operator=(float rhs); + xml_attribute& operator=(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + xml_attribute& operator=(long long rhs); + xml_attribute& operator=(unsigned long long rhs); + #endif + + // Get next/previous attribute in the attribute list of the parent node + xml_attribute next_attribute() const; + xml_attribute previous_attribute() const; + + // Get hash value (unique for handles to the same object) + size_t hash_value() const; + + // Get internal pointer + xml_attribute_struct* internal_object() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); +#endif + + // A light-weight handle for manipulating nodes in DOM tree + class PUGIXML_CLASS xml_node + { + friend class xml_attribute_iterator; + friend class xml_node_iterator; + friend class xml_named_node_iterator; + + protected: + xml_node_struct* _root; + + typedef void (*unspecified_bool_type)(xml_node***); + + public: + // Default constructor. Constructs an empty node. + xml_node(); + + // Constructs node from internal pointer + explicit xml_node(xml_node_struct* p); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators (compares wrapped node pointers) + bool operator==(const xml_node& r) const; + bool operator!=(const xml_node& r) const; + bool operator<(const xml_node& r) const; + bool operator>(const xml_node& r) const; + bool operator<=(const xml_node& r) const; + bool operator>=(const xml_node& r) const; + + // Check if node is empty. + bool empty() const; + + // Get node type + xml_node_type type() const; + + // Get node name, or "" if node is empty or it has no name + const char_t* name() const; + + // Get node value, or "" if node is empty or it has no value + // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. + const char_t* value() const; + + // Get attribute list + xml_attribute first_attribute() const; + xml_attribute last_attribute() const; + + // Get children list + xml_node first_child() const; + xml_node last_child() const; + + // Get next/previous sibling in the children list of the parent node + xml_node next_sibling() const; + xml_node previous_sibling() const; + + // Get parent node + xml_node parent() const; + + // Get root of DOM tree this node belongs to + xml_node root() const; + + // Get text object for the current node + xml_text text() const; + + // Get child, attribute or next/previous sibling with the specified name + xml_node child(const char_t* name) const; + xml_attribute attribute(const char_t* name) const; + xml_node next_sibling(const char_t* name) const; + xml_node previous_sibling(const char_t* name) const; + + // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast) + xml_attribute attribute(const char_t* name, xml_attribute& hint) const; + + // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA + const char_t* child_value() const; + + // Get child value of child with specified name. Equivalent to child(name).child_value(). + const char_t* child_value(const char_t* name) const; + + // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) + bool set_name(const char_t* rhs); + bool set_value(const char_t* rhs); + + // Add attribute with specified name. Returns added attribute, or empty attribute on errors. + xml_attribute append_attribute(const char_t* name); + xml_attribute prepend_attribute(const char_t* name); + xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); + xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); + + // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. + xml_attribute append_copy(const xml_attribute& proto); + xml_attribute prepend_copy(const xml_attribute& proto); + xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); + xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); + + // Add child node with specified type. Returns added node, or empty node on errors. + xml_node append_child(xml_node_type type = node_element); + xml_node prepend_child(xml_node_type type = node_element); + xml_node insert_child_after(xml_node_type type, const xml_node& node); + xml_node insert_child_before(xml_node_type type, const xml_node& node); + + // Add child element with specified name. Returns added node, or empty node on errors. + xml_node append_child(const char_t* name); + xml_node prepend_child(const char_t* name); + xml_node insert_child_after(const char_t* name, const xml_node& node); + xml_node insert_child_before(const char_t* name, const xml_node& node); + + // Add a copy of the specified node as a child. Returns added node, or empty node on errors. + xml_node append_copy(const xml_node& proto); + xml_node prepend_copy(const xml_node& proto); + xml_node insert_copy_after(const xml_node& proto, const xml_node& node); + xml_node insert_copy_before(const xml_node& proto, const xml_node& node); + + // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. + xml_node append_move(const xml_node& moved); + xml_node prepend_move(const xml_node& moved); + xml_node insert_move_after(const xml_node& moved, const xml_node& node); + xml_node insert_move_before(const xml_node& moved, const xml_node& node); + + // Remove specified attribute + bool remove_attribute(const xml_attribute& a); + bool remove_attribute(const char_t* name); + + // Remove specified child + bool remove_child(const xml_node& n); + bool remove_child(const char_t* name); + + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. + // Copies/converts the buffer, so it may be deleted or changed after the function returns. + // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. + xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Find attribute using predicate. Returns first attribute for which predicate returned true. + template xml_attribute find_attribute(Predicate pred) const + { + if (!_root) return xml_attribute(); + + for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) + if (pred(attrib)) + return attrib; + + return xml_attribute(); + } + + // Find child node using predicate. Returns first child for which predicate returned true. + template xml_node find_child(Predicate pred) const + { + if (!_root) return xml_node(); + + for (xml_node node = first_child(); node; node = node.next_sibling()) + if (pred(node)) + return node; + + return xml_node(); + } + + // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. + template xml_node find_node(Predicate pred) const + { + if (!_root) return xml_node(); + + xml_node cur = first_child(); + + while (cur._root && cur._root != _root) + { + if (pred(cur)) return cur; + + if (cur.first_child()) cur = cur.first_child(); + else if (cur.next_sibling()) cur = cur.next_sibling(); + else + { + while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); + + if (cur._root != _root) cur = cur.next_sibling(); + } + } + + return xml_node(); + } + + // Find child node by attribute name/value + xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; + xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; + + #ifndef PUGIXML_NO_STL + // Get the absolute node path from root as a text string. + string_t path(char_t delimiter = '/') const; + #endif + + // Search for a node by path consisting of node names and . or .. elements. + xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; + + // Recursively traverse subtree with xml_tree_walker + bool traverse(xml_tree_walker& walker); + + #ifndef PUGIXML_NO_XPATH + // Select single node by evaluating XPath query. Returns first node from the resulting node set. + xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node select_node(const xpath_query& query) const; + + // Select node set by evaluating XPath query + xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node_set select_nodes(const xpath_query& query) const; + + // (deprecated: use select_node instead) Select single node by evaluating XPath query. + PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; + PUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const; + + #endif + + // Print subtree using a writer object + void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + + #ifndef PUGIXML_NO_STL + // Print subtree to stream + void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; + #endif + + // Child nodes iterators + typedef xml_node_iterator iterator; + + iterator begin() const; + iterator end() const; + + // Attribute iterators + typedef xml_attribute_iterator attribute_iterator; + + attribute_iterator attributes_begin() const; + attribute_iterator attributes_end() const; + + // Range-based for support + xml_object_range children() const; + xml_object_range children(const char_t* name) const; + xml_object_range attributes() const; + + // Get node offset in parsed file/string (in char_t units) for debugging purposes + ptrdiff_t offset_debug() const; + + // Get hash value (unique for handles to the same object) + size_t hash_value() const; + + // Get internal pointer + xml_node_struct* internal_object() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); +#endif + + // A helper for working with text inside PCDATA nodes + class PUGIXML_CLASS xml_text + { + friend class xml_node; + + xml_node_struct* _root; + + typedef void (*unspecified_bool_type)(xml_text***); + + explicit xml_text(xml_node_struct* root); + + xml_node_struct* _data_new(); + xml_node_struct* _data() const; + + public: + // Default constructor. Constructs an empty object. + xml_text(); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Check if text object is empty + bool empty() const; + + // Get text, or "" if object is empty + const char_t* get() const; + + // Get text, or the default value if object is empty + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; + + // Get text as a number, or the default value if conversion did not succeed or object is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; + + #ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; + #endif + + // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty + bool as_bool(bool def = false) const; + + // Set text (returns false if object is empty or there is not enough memory) + bool set(const char_t* rhs); + + // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") + bool set(int rhs); + bool set(unsigned int rhs); + bool set(long rhs); + bool set(unsigned long rhs); + bool set(double rhs); + bool set(float rhs); + bool set(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + bool set(long long rhs); + bool set(unsigned long long rhs); + #endif + + // Set text (equivalent to set without error checking) + xml_text& operator=(const char_t* rhs); + xml_text& operator=(int rhs); + xml_text& operator=(unsigned int rhs); + xml_text& operator=(long rhs); + xml_text& operator=(unsigned long rhs); + xml_text& operator=(double rhs); + xml_text& operator=(float rhs); + xml_text& operator=(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + xml_text& operator=(long long rhs); + xml_text& operator=(unsigned long long rhs); + #endif + + // Get the data node (node_pcdata or node_cdata) for this object + xml_node data() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); +#endif + + // Child node iterator (a bidirectional iterator over a collection of xml_node) + class PUGIXML_CLASS xml_node_iterator + { + friend class xml_node; + + private: + mutable xml_node _wrap; + xml_node _parent; + + xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node* pointer; + typedef xml_node& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_node_iterator(); + + // Construct an iterator which points to the specified node + xml_node_iterator(const xml_node& node); + + // Iterator operators + bool operator==(const xml_node_iterator& rhs) const; + bool operator!=(const xml_node_iterator& rhs) const; + + xml_node& operator*() const; + xml_node* operator->() const; + + const xml_node_iterator& operator++(); + xml_node_iterator operator++(int); + + const xml_node_iterator& operator--(); + xml_node_iterator operator--(int); + }; + + // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) + class PUGIXML_CLASS xml_attribute_iterator + { + friend class xml_node; + + private: + mutable xml_attribute _wrap; + xml_node _parent; + + xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_attribute value_type; + typedef xml_attribute* pointer; + typedef xml_attribute& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_attribute_iterator(); + + // Construct an iterator which points to the specified attribute + xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); + + // Iterator operators + bool operator==(const xml_attribute_iterator& rhs) const; + bool operator!=(const xml_attribute_iterator& rhs) const; + + xml_attribute& operator*() const; + xml_attribute* operator->() const; + + const xml_attribute_iterator& operator++(); + xml_attribute_iterator operator++(int); + + const xml_attribute_iterator& operator--(); + xml_attribute_iterator operator--(int); + }; + + // Named node range helper + class PUGIXML_CLASS xml_named_node_iterator + { + friend class xml_node; + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node* pointer; + typedef xml_node& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_named_node_iterator(); + + // Construct an iterator which points to the specified node + xml_named_node_iterator(const xml_node& node, const char_t* name); + + // Iterator operators + bool operator==(const xml_named_node_iterator& rhs) const; + bool operator!=(const xml_named_node_iterator& rhs) const; + + xml_node& operator*() const; + xml_node* operator->() const; + + const xml_named_node_iterator& operator++(); + xml_named_node_iterator operator++(int); + + const xml_named_node_iterator& operator--(); + xml_named_node_iterator operator--(int); + + private: + mutable xml_node _wrap; + xml_node _parent; + const char_t* _name; + + xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); + }; + + // Abstract tree walker class (see xml_node::traverse) + class PUGIXML_CLASS xml_tree_walker + { + friend class xml_node; + + private: + int _depth; + + protected: + // Get current traversal depth + int depth() const; + + public: + xml_tree_walker(); + virtual ~xml_tree_walker(); + + // Callback that is called when traversal begins + virtual bool begin(xml_node& node); + + // Callback that is called for each node traversed + virtual bool for_each(xml_node& node) = 0; + + // Callback that is called when traversal ends + virtual bool end(xml_node& node); + }; + + // Parsing status, returned as part of xml_parse_result object + enum xml_parse_status + { + status_ok = 0, // No error + + status_file_not_found, // File was not found during load_file() + status_io_error, // Error reading from file/stream + status_out_of_memory, // Could not allocate memory + status_internal_error, // Internal error occurred + + status_unrecognized_tag, // Parser could not determine tag type + + status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction + status_bad_comment, // Parsing error occurred while parsing comment + status_bad_cdata, // Parsing error occurred while parsing CDATA section + status_bad_doctype, // Parsing error occurred while parsing document type declaration + status_bad_pcdata, // Parsing error occurred while parsing PCDATA section + status_bad_start_element, // Parsing error occurred while parsing start element tag + status_bad_attribute, // Parsing error occurred while parsing element attribute + status_bad_end_element, // Parsing error occurred while parsing end element tag + status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) + + status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) + + status_no_document_element // Parsing resulted in a document without element nodes + }; + + // Parsing result + struct PUGIXML_CLASS xml_parse_result + { + // Parsing status (see xml_parse_status) + xml_parse_status status; + + // Last parsed offset (in char_t units from start of input data) + ptrdiff_t offset; + + // Source document encoding + xml_encoding encoding; + + // Default constructor, initializes object to failed state + xml_parse_result(); + + // Cast to bool operator + operator bool() const; + + // Get error description + const char* description() const; + }; + + // Document class (DOM tree root) + class PUGIXML_CLASS xml_document: public xml_node + { + private: + char_t* _buffer; + + char _memory[192]; + + // Non-copyable semantics + xml_document(const xml_document&); + xml_document& operator=(const xml_document&); + + void _create(); + void _destroy(); + void _move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; + + public: + // Default constructor, makes empty document + xml_document(); + + // Destructor, invalidates all node/attribute handles to this document + ~xml_document(); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; + xml_document& operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; + #endif + + // Removes all nodes, leaving the empty document + void reset(); + + // Removes all nodes, then copies the entire contents of the specified document + void reset(const xml_document& proto); + + #ifndef PUGIXML_NO_STL + // Load document from stream. + xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); + #endif + + // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. + PUGIXML_DEPRECATED xml_parse_result load(const char_t* contents, unsigned int options = parse_default); + + // Load document from zero-terminated string. No encoding conversions are applied. + xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); + + // Load document from file + xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. + xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). + // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. + xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). + // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). + xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). + void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + + #ifndef PUGIXML_NO_STL + // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). + void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; + #endif + + // Save XML to file + bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + + // Get document element + xml_node document_element() const; + }; + +#ifndef PUGIXML_NO_XPATH + // XPath query return type + enum xpath_value_type + { + xpath_type_none, // Unknown type (query failed to compile) + xpath_type_node_set, // Node set (xpath_node_set) + xpath_type_number, // Number + xpath_type_string, // String + xpath_type_boolean // Boolean + }; + + // XPath parsing result + struct PUGIXML_CLASS xpath_parse_result + { + // Error message (0 if no error) + const char* error; + + // Last parsed offset (in char_t units from string start) + ptrdiff_t offset; + + // Default constructor, initializes object to failed state + xpath_parse_result(); + + // Cast to bool operator + operator bool() const; + + // Get error description + const char* description() const; + }; + + // A single XPath variable + class PUGIXML_CLASS xpath_variable + { + friend class xpath_variable_set; + + protected: + xpath_value_type _type; + xpath_variable* _next; + + xpath_variable(xpath_value_type type); + + // Non-copyable semantics + xpath_variable(const xpath_variable&); + xpath_variable& operator=(const xpath_variable&); + + public: + // Get variable name + const char_t* name() const; + + // Get variable type + xpath_value_type type() const; + + // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error + bool get_boolean() const; + double get_number() const; + const char_t* get_string() const; + const xpath_node_set& get_node_set() const; + + // Set variable value; no type conversion is performed, false is returned on type mismatch error + bool set(bool value); + bool set(double value); + bool set(const char_t* value); + bool set(const xpath_node_set& value); + }; + + // A set of XPath variables + class PUGIXML_CLASS xpath_variable_set + { + private: + xpath_variable* _data[64]; + + void _assign(const xpath_variable_set& rhs); + void _swap(xpath_variable_set& rhs); + + xpath_variable* _find(const char_t* name) const; + + static bool _clone(xpath_variable* var, xpath_variable** out_result); + static void _destroy(xpath_variable* var); + + public: + // Default constructor/destructor + xpath_variable_set(); + ~xpath_variable_set(); + + // Copy constructor/assignment operator + xpath_variable_set(const xpath_variable_set& rhs); + xpath_variable_set& operator=(const xpath_variable_set& rhs); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT; + xpath_variable_set& operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT; + #endif + + // Add a new variable or get the existing one, if the types match + xpath_variable* add(const char_t* name, xpath_value_type type); + + // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch + bool set(const char_t* name, bool value); + bool set(const char_t* name, double value); + bool set(const char_t* name, const char_t* value); + bool set(const char_t* name, const xpath_node_set& value); + + // Get existing variable by name + xpath_variable* get(const char_t* name); + const xpath_variable* get(const char_t* name) const; + }; + + // A compiled XPath query object + class PUGIXML_CLASS xpath_query + { + private: + void* _impl; + xpath_parse_result _result; + + typedef void (*unspecified_bool_type)(xpath_query***); + + // Non-copyable semantics + xpath_query(const xpath_query&); + xpath_query& operator=(const xpath_query&); + + public: + // Construct a compiled object from XPath expression. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. + explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); + + // Constructor + xpath_query(); + + // Destructor + ~xpath_query(); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT; + xpath_query& operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT; + #endif + + // Get query expression return type + xpath_value_type return_type() const; + + // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + bool evaluate_boolean(const xpath_node& n) const; + + // Evaluate expression as double value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + double evaluate_number(const xpath_node& n) const; + + #ifndef PUGIXML_NO_STL + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + string_t evaluate_string(const xpath_node& n) const; + #endif + + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. + size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; + + // Evaluate expression as node set in the specified context. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. + xpath_node_set evaluate_node_set(const xpath_node& n) const; + + // Evaluate expression as node set in the specified context. + // Return first node in document order, or empty node if node set is empty. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. + xpath_node evaluate_node(const xpath_node& n) const; + + // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) + const xpath_parse_result& result() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + }; + + #ifndef PUGIXML_NO_EXCEPTIONS + #if defined(_MSC_VER) + // C4275 can be ignored in Visual C++ if you are deriving + // from a type in the Standard C++ Library + #pragma warning(push) + #pragma warning(disable: 4275) + #endif + // XPath exception class + class PUGIXML_CLASS xpath_exception: public std::exception + { + private: + xpath_parse_result _result; + + public: + // Construct exception from parse result + explicit xpath_exception(const xpath_parse_result& result); + + // Get error message + virtual const char* what() const throw() PUGIXML_OVERRIDE; + + // Get parse result + const xpath_parse_result& result() const; + }; + #if defined(_MSC_VER) + #pragma warning(pop) + #endif + #endif + + // XPath node class (either xml_node or xml_attribute) + class PUGIXML_CLASS xpath_node + { + private: + xml_node _node; + xml_attribute _attribute; + + typedef void (*unspecified_bool_type)(xpath_node***); + + public: + // Default constructor; constructs empty XPath node + xpath_node(); + + // Construct XPath node from XML node/attribute + xpath_node(const xml_node& node); + xpath_node(const xml_attribute& attribute, const xml_node& parent); + + // Get node/attribute, if any + xml_node node() const; + xml_attribute attribute() const; + + // Get parent of contained node/attribute + xml_node parent() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators + bool operator==(const xpath_node& n) const; + bool operator!=(const xpath_node& n) const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); +#endif + + // A fixed-size collection of XPath nodes + class PUGIXML_CLASS xpath_node_set + { + public: + // Collection type + enum type_t + { + type_unsorted, // Not ordered + type_sorted, // Sorted by document order (ascending) + type_sorted_reverse // Sorted by document order (descending) + }; + + // Constant iterator type + typedef const xpath_node* const_iterator; + + // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work + typedef const xpath_node* iterator; + + // Default constructor. Constructs empty set. + xpath_node_set(); + + // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful + xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); + + // Destructor + ~xpath_node_set(); + + // Copy constructor/assignment operator + xpath_node_set(const xpath_node_set& ns); + xpath_node_set& operator=(const xpath_node_set& ns); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT; + xpath_node_set& operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT; + #endif + + // Get collection type + type_t type() const; + + // Get collection size + size_t size() const; + + // Indexing operator + const xpath_node& operator[](size_t index) const; + + // Collection iterators + const_iterator begin() const; + const_iterator end() const; + + // Sort the collection in ascending/descending order by document order + void sort(bool reverse = false); + + // Get first node in the collection by document order + xpath_node first() const; + + // Check if collection is empty + bool empty() const; + + private: + type_t _type; + + xpath_node _storage[1]; + + xpath_node* _begin; + xpath_node* _end; + + void _assign(const_iterator begin, const_iterator end, type_t type); + void _move(xpath_node_set& rhs) PUGIXML_NOEXCEPT; + }; +#endif + +#ifndef PUGIXML_NO_STL + // Convert wide string to UTF8 + std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); + + // Convert UTF8 to wide string + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); +#endif + + // Memory allocation function interface; returns pointer to allocated memory or NULL on failure + typedef void* (*allocation_function)(size_t size); + + // Memory deallocation function interface + typedef void (*deallocation_function)(void* ptr); + + // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. + void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); + + // Get current memory management functions + allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); + deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); +} + +#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) +namespace std +{ + // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); +} +#endif + +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) +namespace std +{ + // Workarounds for (non-standard) iterator category detection + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); +} +#endif + +#endif + +// Make sure implementation is included in header-only mode +// Use macro expansion in #include to work around QMake (QTBUG-11923) +#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) +# define PUGIXML_SOURCE "pugixml.cpp" +# include PUGIXML_SOURCE +#endif + +/** + * Copyright (c) 2006-2019 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/src/3rd-party/tinyxml2/LICENSE.txt b/src/3rd-party/tinyxml2/LICENSE.txt deleted file mode 100644 index 85a6a36f..00000000 --- a/src/3rd-party/tinyxml2/LICENSE.txt +++ /dev/null @@ -1,18 +0,0 @@ -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. diff --git a/src/3rd-party/tinyxml2/tinyxml2.cpp b/src/3rd-party/tinyxml2/tinyxml2.cpp deleted file mode 100644 index 644c4e3e..00000000 --- a/src/3rd-party/tinyxml2/tinyxml2.cpp +++ /dev/null @@ -1,2949 +0,0 @@ -/* -Original code by Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "tinyxml2.h" - -#include // yes, this one new style header, is in the Android SDK. -#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) -# include -# include -#else -# include -# include -#endif - -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) - // Microsoft Visual Studio, version 2005 and higher. Not WinCE. - /*int _snprintf_s( - TCHAR *buffer, - size_t sizeOfBuffer, - size_t count, - const TCHAR *format [, - argument] ... - );*/ - - - /* - Add by Nomango 2019.10.12 - */ -#ifdef _UNICODE - #define vsnprintf_s _vsnwprintf_s - #define _vsnprintf _vsnwprintf - #define strlen wcslen - #define strncmp wcsncmp - #define strchr wcschr - #define fopen_s _wfopen_s - #define printf wprintf - #define vfprintf vfwprintf - #define TIXML_VSCPRINTF _vscwprintf - #define TIXML_SSCANF swscanf_s -#else - #define TIXML_VSCPRINTF _vscprintf - #define TIXML_SSCANF sscanf_s -#endif - - static inline int TIXML_SNPRINTF( tinyxml2::TCHAR* buffer, size_t size, const tinyxml2::TCHAR* format, ... ) - { - va_list va; - va_start( va, format ); - const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); - va_end( va ); - return result; - } - - static inline int TIXML_VSNPRINTF( tinyxml2::TCHAR* buffer, size_t size, const tinyxml2::TCHAR* format, va_list va ) - { - const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); - return result; - } - -#elif defined _MSC_VER - // Microsoft Visual Studio 2003 and earlier or WinCE - #define TIXML_SNPRINTF _snprintf - #define TIXML_VSNPRINTF _vsnprintf - #define TIXML_SSCANF sscanf - #if (_MSC_VER < 1400 ) && (!defined WINCE) - // Microsoft Visual Studio 2003 and not WinCE. - #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. - #else - // Microsoft Visual Studio 2003 and earlier or WinCE. - static inline int TIXML_VSCPRINTF( const tinyxml2::TCHAR* format, va_list va ) - { - int len = 512; - for (;;) { - len = len*2; - tinyxml2::TCHAR* str = new tinyxml2::TCHAR[len](); - const int required = _vsnprintf(str, len, format, va); - delete[] str; - if ( required != -1 ) { - TIXMLASSERT( required >= 0 ); - len = required; - break; - } - } - TIXMLASSERT( len >= 0 ); - return len; - } - #endif -#else - // GCC version 3 and higher - //#warning( "Using sn* functions." ) - #define TIXML_SNPRINTF snprintf - #define TIXML_VSNPRINTF vsnprintf - static inline int TIXML_VSCPRINTF( const tinyxml2::TCHAR* format, va_list va ) - { - int len = vsnprintf( 0, 0, format, va ); - TIXMLASSERT( len >= 0 ); - return len; - } - #define TIXML_SSCANF sscanf -#endif - - -static const tinyxml2::TCHAR LINE_FEED = static_cast(0x0a); // all line endings are normalized to LF -static const tinyxml2::TCHAR LF = LINE_FEED; -static const tinyxml2::TCHAR CARRIAGE_RETURN = static_cast(0x0d); // CR gets filtered out -static const tinyxml2::TCHAR CR = CARRIAGE_RETURN; -static const tinyxml2::TCHAR SINGLE_QUOTE = '\''; -static const tinyxml2::TCHAR DOUBLE_QUOTE = '\"'; - -// Bunch of unicode info at: -// http://www.unicode.org/faq/utf_bom.html -// ef bb bf (Microsoft "lead bytes") - designates UTF-8 - -static const tinyxml2::TUCHAR TIXML_UTF_LEAD_0 = 0xefU; -static const tinyxml2::TUCHAR TIXML_UTF_LEAD_1 = 0xbbU; -static const tinyxml2::TUCHAR TIXML_UTF_LEAD_2 = 0xbfU; - -namespace tinyxml2 -{ - -struct Entity { - const TCHAR* pattern; - int length; - TCHAR value; -}; - -static const int NUM_ENTITIES = 5; -static const Entity entities[NUM_ENTITIES] = { - { TINYXML2_STR("quot"), 4, DOUBLE_QUOTE }, - { TINYXML2_STR("amp"), 3, '&' }, - { TINYXML2_STR("apos"), 4, SINGLE_QUOTE }, - { TINYXML2_STR("lt"), 2, '<' }, - { TINYXML2_STR("gt"), 2, '>' } -}; - - -StrPair::~StrPair() -{ - Reset(); -} - - -void StrPair::TransferTo( StrPair* other ) -{ - if ( this == other ) { - return; - } - // This in effect implements the assignment operator by "moving" - // ownership (as in auto_ptr). - - TIXMLASSERT( other != 0 ); - TIXMLASSERT( other->_flags == 0 ); - TIXMLASSERT( other->_start == 0 ); - TIXMLASSERT( other->_end == 0 ); - - other->Reset(); - - other->_flags = _flags; - other->_start = _start; - other->_end = _end; - - _flags = 0; - _start = 0; - _end = 0; -} - - -void StrPair::Reset() -{ - if ( _flags & NEEDS_DELETE ) { - delete [] _start; - } - _flags = 0; - _start = 0; - _end = 0; -} - - -void StrPair::SetStr( const TCHAR* str, int flags ) -{ - TIXMLASSERT( str ); - Reset(); - size_t len = strlen( str ); - TIXMLASSERT( _start == 0 ); - _start = new TCHAR[ len+1 ]; - memcpy( _start, str, (len+1) * sizeof(TCHAR)); - _end = _start + len; - _flags = flags | NEEDS_DELETE; -} - - -TCHAR* StrPair::ParseText( TCHAR* p, const TCHAR* endTag, int strFlags, int* curLineNumPtr ) -{ - TIXMLASSERT( p ); - TIXMLASSERT( endTag && *endTag ); - TIXMLASSERT(curLineNumPtr); - - TCHAR* start = p; - const TCHAR endChar = *endTag; - size_t length = strlen( endTag ); - - // Inner loop of text parsing. - while ( *p ) { - if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { - Set( start, p, strFlags ); - return p + length; - } else if (*p == '\n') { - ++(*curLineNumPtr); - } - ++p; - TIXMLASSERT( p ); - } - return 0; -} - - -TCHAR* StrPair::ParseName( TCHAR* p ) -{ - if ( !p || !(*p) ) { - return 0; - } - if ( !XMLUtil::IsNameStartChar( *p ) ) { - return 0; - } - - TCHAR* const start = p; - ++p; - while ( *p && XMLUtil::IsNameChar( *p ) ) { - ++p; - } - - Set( start, p, 0 ); - return p; -} - - -void StrPair::CollapseWhitespace() -{ - // Adjusting _start would cause undefined behavior on delete[] - TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); - // Trim leading space. - _start = XMLUtil::SkipWhiteSpace( _start, 0 ); - - if ( *_start ) { - const TCHAR* p = _start; // the read pointer - TCHAR* q = _start; // the write pointer - - while( *p ) { - if ( XMLUtil::IsWhiteSpace( *p )) { - p = XMLUtil::SkipWhiteSpace( p, 0 ); - if ( *p == 0 ) { - break; // don't write to q; this trims the trailing space. - } - *q = ' '; - ++q; - } - *q = *p; - ++q; - ++p; - } - *q = 0; - } -} - - -const TCHAR* StrPair::GetStr() -{ - TIXMLASSERT( _start ); - TIXMLASSERT( _end ); - if ( _flags & NEEDS_FLUSH ) { - *_end = 0; - _flags ^= NEEDS_FLUSH; - - if ( _flags ) { - const TCHAR* p = _start; // the read pointer - TCHAR* q = _start; // the write pointer - - while( p < _end ) { - if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { - // CR-LF pair becomes LF - // CR alone becomes LF - // LF-CR becomes LF - if ( *(p+1) == LF ) { - p += 2; - } - else { - ++p; - } - *q = LF; - ++q; - } - else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { - if ( *(p+1) == CR ) { - p += 2; - } - else { - ++p; - } - *q = LF; - ++q; - } - else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { - // Entities handled by tinyXML2: - // - special entities in the entity table [in/out] - // - numeric character reference [in] - // 中 or 中 - - if ( *(p+1) == '#' ) { - const int buflen = 10; - TCHAR buf[buflen] = { 0 }; - int len = 0; - const TCHAR* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); - if ( adjusted == 0 ) { - *q = *p; - ++p; - ++q; - } - else { - TIXMLASSERT( 0 <= len && len <= buflen ); - TIXMLASSERT( q + len <= adjusted ); - p = adjusted; - memcpy( q, buf, len * sizeof(TCHAR)); - q += len; - } - } - else { - bool entityFound = false; - for( int i = 0; i < NUM_ENTITIES; ++i ) { - const Entity& entity = entities[i]; - if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 - && *( p + entity.length + 1 ) == ';' ) { - // Found an entity - convert. - *q = entity.value; - ++q; - p += entity.length + 2; - entityFound = true; - break; - } - } - if ( !entityFound ) { - // fixme: treat as error? - ++p; - ++q; - } - } - } - else { - *q = *p; - ++p; - ++q; - } - } - *q = 0; - } - // The loop below has plenty going on, and this - // is a less useful mode. Break it out. - if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { - CollapseWhitespace(); - } - _flags = (_flags & NEEDS_DELETE); - } - TIXMLASSERT( _start ); - return _start; -} - - - - -// --------- XMLUtil ----------- // - -const TCHAR* XMLUtil::writeBoolTrue = TINYXML2_STR("true"); -const TCHAR* XMLUtil::writeBoolFalse = TINYXML2_STR("false"); - -void XMLUtil::SetBoolSerialization(const TCHAR* writeTrue, const TCHAR* writeFalse) -{ - static const TCHAR* defTrue = TINYXML2_STR("true"); - static const TCHAR* defFalse = TINYXML2_STR("false"); - - writeBoolTrue = (writeTrue) ? writeTrue : defTrue; - writeBoolFalse = (writeFalse) ? writeFalse : defFalse; -} - - -const TCHAR* XMLUtil::ReadBOM( const TCHAR* p, bool* bom ) -{ - TIXMLASSERT( p ); - TIXMLASSERT( bom ); - *bom = false; - const TUCHAR* pu = reinterpret_cast(p); - // Check for BOM: - if ( *(pu+0) == TIXML_UTF_LEAD_0 - && *(pu+1) == TIXML_UTF_LEAD_1 - && *(pu+2) == TIXML_UTF_LEAD_2 ) { - *bom = true; - p += 3; - } - TIXMLASSERT( p ); - return p; -} - - -void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, TCHAR* output, int* length ) -{ - const unsigned long BYTE_MASK = 0xBF; - const unsigned long BYTE_MARK = 0x80; - const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - - if (input < 0x80) { - *length = 1; - } - else if ( input < 0x800 ) { - *length = 2; - } - else if ( input < 0x10000 ) { - *length = 3; - } - else if ( input < 0x200000 ) { - *length = 4; - } - else { - *length = 0; // This code won't convert this correctly anyway. - return; - } - - output += *length; - - // Scary scary fall throughs are annotated with carefully designed comments - // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc - switch (*length) { - case 4: - --output; - *output = static_cast((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - //fall through - case 3: - --output; - *output = static_cast((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - //fall through - case 2: - --output; - *output = static_cast((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - //fall through - case 1: - --output; - *output = static_cast(input | FIRST_BYTE_MARK[*length]); - break; - default: - TIXMLASSERT( false ); - } -} - - -const TCHAR* XMLUtil::GetCharacterRef( const TCHAR* p, TCHAR* value, int* length ) -{ - // Presume an entity, and pull it out. - *length = 0; - - if ( *(p+1) == '#' && *(p+2) ) { - unsigned long ucs = 0; - TIXMLASSERT( sizeof( ucs ) >= 4 ); - ptrdiff_t delta = 0; - unsigned mult = 1; - static const TCHAR SEMICOLON = ';'; - - if ( *(p+2) == 'x' ) { - // Hexadecimal. - const TCHAR* q = p+3; - if ( !(*q) ) { - return 0; - } - - q = strchr( q, SEMICOLON ); - - if ( !q ) { - return 0; - } - TIXMLASSERT( *q == SEMICOLON ); - - delta = q-p; - --q; - - while ( *q != 'x' ) { - unsigned int digit = 0; - - if ( *q >= '0' && *q <= '9' ) { - digit = *q - '0'; - } - else if ( *q >= 'a' && *q <= 'f' ) { - digit = *q - 'a' + 10; - } - else if ( *q >= 'A' && *q <= 'F' ) { - digit = *q - 'A' + 10; - } - else { - return 0; - } - TIXMLASSERT( digit < 16 ); - TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); - const unsigned int digitScaled = mult * digit; - TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); - ucs += digitScaled; - TIXMLASSERT( mult <= UINT_MAX / 16 ); - mult *= 16; - --q; - } - } - else { - // Decimal. - const TCHAR* q = p+2; - if ( !(*q) ) { - return 0; - } - - q = strchr( q, SEMICOLON ); - - if ( !q ) { - return 0; - } - TIXMLASSERT( *q == SEMICOLON ); - - delta = q-p; - --q; - - while ( *q != '#' ) { - if ( *q >= '0' && *q <= '9' ) { - const unsigned int digit = *q - '0'; - TIXMLASSERT( digit < 10 ); - TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); - const unsigned int digitScaled = mult * digit; - TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); - ucs += digitScaled; - } - else { - return 0; - } - TIXMLASSERT( mult <= UINT_MAX / 10 ); - mult *= 10; - --q; - } - } - // convert the UCS to UTF-8 - ConvertUTF32ToUTF8( ucs, value, length ); - return p + delta + 1; - } - return p+1; -} - - -void XMLUtil::ToStr( int v, TCHAR* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%d"), v ); -} - - -void XMLUtil::ToStr( unsigned v, TCHAR* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%u"), v ); -} - - -void XMLUtil::ToStr( bool v, TCHAR* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%s"), v ? writeBoolTrue : writeBoolFalse); -} - -/* - ToStr() of a number is a very tricky topic. - https://github.com/leethomason/tinyxml2/issues/106 -*/ -void XMLUtil::ToStr( float v, TCHAR* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%.8g"), v ); -} - - -void XMLUtil::ToStr( double v, TCHAR* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%.17g"), v ); -} - - -void XMLUtil::ToStr( int64_t v, TCHAR* buffer, int bufferSize ) -{ - // horrible syntax trick to make the compiler happy about %lld - TIXML_SNPRINTF(buffer, bufferSize, TINYXML2_STR("%lld"), static_cast(v)); -} - -void XMLUtil::ToStr( uint64_t v, TCHAR* buffer, int bufferSize ) -{ - // horrible syntax trick to make the compiler happy about %llu - TIXML_SNPRINTF(buffer, bufferSize, TINYXML2_STR("%llu"), (long long)v); -} - -bool XMLUtil::ToInt( const TCHAR* str, int* value ) -{ - if ( TIXML_SSCANF( str, TINYXML2_STR("%d"), value ) == 1 ) { - return true; - } - return false; -} - -bool XMLUtil::ToUnsigned( const TCHAR* str, unsigned *value ) -{ - if ( TIXML_SSCANF( str, TINYXML2_STR("%u"), value ) == 1 ) { - return true; - } - return false; -} - -bool XMLUtil::ToBool( const TCHAR* str, bool* value ) -{ - int ival = 0; - if ( ToInt( str, &ival )) { - *value = (ival==0) ? false : true; - return true; - } - static const TCHAR* TRUE[] = { TINYXML2_STR("true"), TINYXML2_STR("True"), TINYXML2_STR("TRUE"), 0 }; - static const TCHAR* FALSE[] = { TINYXML2_STR("false"), TINYXML2_STR("False"), TINYXML2_STR("FALSE"), 0 }; - - for (int i = 0; TRUE[i]; ++i) { - if (StringEqual(str, TRUE[i])) { - *value = true; - return true; - } - } - for (int i = 0; FALSE[i]; ++i) { - if (StringEqual(str, FALSE[i])) { - *value = false; - return true; - } - } - return false; -} - - -bool XMLUtil::ToFloat( const TCHAR* str, float* value ) -{ - if ( TIXML_SSCANF( str, TINYXML2_STR("%f"), value ) == 1 ) { - return true; - } - return false; -} - - -bool XMLUtil::ToDouble( const TCHAR* str, double* value ) -{ - if ( TIXML_SSCANF( str, TINYXML2_STR("%lf"), value ) == 1 ) { - return true; - } - return false; -} - - -bool XMLUtil::ToInt64(const TCHAR* str, int64_t* value) -{ - long long v = 0; // horrible syntax trick to make the compiler happy about %lld - if (TIXML_SSCANF(str, TINYXML2_STR("%lld"), &v) == 1) { - *value = static_cast(v); - return true; - } - return false; -} - - -bool XMLUtil::ToUnsigned64(const TCHAR* str, uint64_t* value) { - unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu - if(TIXML_SSCANF(str, TINYXML2_STR("%llu"), &v) == 1) { - *value = (uint64_t)v; - return true; - } - return false; -} - - -TCHAR* XMLDocument::Identify( TCHAR* p, XMLNode** node ) -{ - TIXMLASSERT( node ); - TIXMLASSERT( p ); - TCHAR* const start = p; - int const startLine = _parseCurLineNum; - p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); - if( !*p ) { - *node = 0; - TIXMLASSERT( p ); - return p; - } - - // These strings define the matching patterns: - static const TCHAR* xmlHeader = { TINYXML2_STR("( _commentPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += xmlHeaderLen; - } - else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { - returnNode = CreateUnlinkedNode( _commentPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += commentHeaderLen; - } - else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { - XMLText* text = CreateUnlinkedNode( _textPool ); - returnNode = text; - returnNode->_parseLineNum = _parseCurLineNum; - p += cdataHeaderLen; - text->SetCData( true ); - } - else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { - returnNode = CreateUnlinkedNode( _commentPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += dtdHeaderLen; - } - else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { - returnNode = CreateUnlinkedNode( _elementPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += elementHeaderLen; - } - else { - returnNode = CreateUnlinkedNode( _textPool ); - returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character - p = start; // Back it up, all the text counts. - _parseCurLineNum = startLine; - } - - TIXMLASSERT( returnNode ); - TIXMLASSERT( p ); - *node = returnNode; - return p; -} - - -bool XMLDocument::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - if ( visitor->VisitEnter( *this ) ) { - for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { - if ( !node->Accept( visitor ) ) { - break; - } - } - } - return visitor->VisitExit( *this ); -} - - -// --------- XMLNode ----------- // - -XMLNode::XMLNode( XMLDocument* doc ) : - _document( doc ), - _parent( 0 ), - _value(), - _parseLineNum( 0 ), - _firstChild( 0 ), _lastChild( 0 ), - _prev( 0 ), _next( 0 ), - _userData( 0 ), - _memPool( 0 ) -{ -} - - -XMLNode::~XMLNode() -{ - DeleteChildren(); - if ( _parent ) { - _parent->Unlink( this ); - } -} - -const TCHAR* XMLNode::Value() const -{ - // Edge case: XMLDocuments don't have a Value. Return null. - if ( this->ToDocument() ) - return 0; - return _value.GetStr(); -} - -void XMLNode::SetValue( const TCHAR* str, bool staticMem ) -{ - if ( staticMem ) { - _value.SetInternedStr( str ); - } - else { - _value.SetStr( str ); - } -} - -XMLNode* XMLNode::DeepClone(XMLDocument* target) const -{ - XMLNode* clone = this->ShallowClone(target); - if (!clone) return 0; - - for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) { - XMLNode* childClone = child->DeepClone(target); - TIXMLASSERT(childClone); - clone->InsertEndChild(childClone); - } - return clone; -} - -void XMLNode::DeleteChildren() -{ - while( _firstChild ) { - TIXMLASSERT( _lastChild ); - DeleteChild( _firstChild ); - } - _firstChild = _lastChild = 0; -} - - -void XMLNode::Unlink( XMLNode* child ) -{ - TIXMLASSERT( child ); - TIXMLASSERT( child->_document == _document ); - TIXMLASSERT( child->_parent == this ); - if ( child == _firstChild ) { - _firstChild = _firstChild->_next; - } - if ( child == _lastChild ) { - _lastChild = _lastChild->_prev; - } - - if ( child->_prev ) { - child->_prev->_next = child->_next; - } - if ( child->_next ) { - child->_next->_prev = child->_prev; - } - child->_next = 0; - child->_prev = 0; - child->_parent = 0; -} - - -void XMLNode::DeleteChild( XMLNode* node ) -{ - TIXMLASSERT( node ); - TIXMLASSERT( node->_document == _document ); - TIXMLASSERT( node->_parent == this ); - Unlink( node ); - TIXMLASSERT(node->_prev == 0); - TIXMLASSERT(node->_next == 0); - TIXMLASSERT(node->_parent == 0); - DeleteNode( node ); -} - - -XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - InsertChildPreamble( addThis ); - - if ( _lastChild ) { - TIXMLASSERT( _firstChild ); - TIXMLASSERT( _lastChild->_next == 0 ); - _lastChild->_next = addThis; - addThis->_prev = _lastChild; - _lastChild = addThis; - - addThis->_next = 0; - } - else { - TIXMLASSERT( _firstChild == 0 ); - _firstChild = _lastChild = addThis; - - addThis->_prev = 0; - addThis->_next = 0; - } - addThis->_parent = this; - return addThis; -} - - -XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - InsertChildPreamble( addThis ); - - if ( _firstChild ) { - TIXMLASSERT( _lastChild ); - TIXMLASSERT( _firstChild->_prev == 0 ); - - _firstChild->_prev = addThis; - addThis->_next = _firstChild; - _firstChild = addThis; - - addThis->_prev = 0; - } - else { - TIXMLASSERT( _lastChild == 0 ); - _firstChild = _lastChild = addThis; - - addThis->_prev = 0; - addThis->_next = 0; - } - addThis->_parent = this; - return addThis; -} - - -XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - - TIXMLASSERT( afterThis ); - - if ( afterThis->_parent != this ) { - TIXMLASSERT( false ); - return 0; - } - if ( afterThis == addThis ) { - // Current state: BeforeThis -> AddThis -> OneAfterAddThis - // Now AddThis must disappear from it's location and then - // reappear between BeforeThis and OneAfterAddThis. - // So just leave it where it is. - return addThis; - } - - if ( afterThis->_next == 0 ) { - // The last node or the only node. - return InsertEndChild( addThis ); - } - InsertChildPreamble( addThis ); - addThis->_prev = afterThis; - addThis->_next = afterThis->_next; - afterThis->_next->_prev = addThis; - afterThis->_next = addThis; - addThis->_parent = this; - return addThis; -} - - - - -const XMLElement* XMLNode::FirstChildElement( const TCHAR* name ) const -{ - for( const XMLNode* node = _firstChild; node; node = node->_next ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::LastChildElement( const TCHAR* name ) const -{ - for( const XMLNode* node = _lastChild; node; node = node->_prev ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::NextSiblingElement( const TCHAR* name ) const -{ - for( const XMLNode* node = _next; node; node = node->_next ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::PreviousSiblingElement( const TCHAR* name ) const -{ - for( const XMLNode* node = _prev; node; node = node->_prev ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -TCHAR* XMLNode::ParseDeep( TCHAR* p, StrPair* parentEndTag, int* curLineNumPtr ) -{ - // This is a recursive method, but thinking about it TINYXML2_STR("at the current level") - // it is a pretty simple flat list: - // - // - // - // With a special case: - // - // - // - // - // Where the closing element (/foo) *must* be the next thing after the opening - // element, and the names must match. BUT the tricky bit is that the closing - // element will be read by the child. - // - // 'endTag' is the end tag for this node, it is returned by a call to a child. - // 'parentEnd' is the end tag for the parent, which is filled in and returned. - - XMLDocument::DepthTracker tracker(_document); - if (_document->Error()) - return 0; - - while( p && *p ) { - XMLNode* node = 0; - - p = _document->Identify( p, &node ); - TIXMLASSERT( p ); - if ( node == 0 ) { - break; - } - - const int initialLineNum = node->_parseLineNum; - - StrPair endTag; - p = node->ParseDeep( p, &endTag, curLineNumPtr ); - if ( !p ) { - DeleteNode( node ); - if ( !_document->Error() ) { - _document->SetError( XML_ERROR_PARSING, initialLineNum, 0); - } - break; - } - - const XMLDeclaration* const decl = node->ToDeclaration(); - if ( decl ) { - // Declarations are only allowed at document level - // - // Multiple declarations are allowed but all declarations - // must occur before anything else. - // - // Optimized due to a security test case. If the first node is - // a declaration, and the last node is a declaration, then only - // declarations have so far been added. - bool wellLocated = false; - - if (ToDocument()) { - if (FirstChild()) { - wellLocated = - FirstChild() && - FirstChild()->ToDeclaration() && - LastChild() && - LastChild()->ToDeclaration(); - } - else { - wellLocated = true; - } - } - if ( !wellLocated ) { - _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, TINYXML2_STR("XMLDeclaration value=%s"), decl->Value()); - DeleteNode( node ); - break; - } - } - - XMLElement* ele = node->ToElement(); - if ( ele ) { - // We read the end tag. Return it to the parent. - if ( ele->ClosingType() == XMLElement::CLOSING ) { - if ( parentEndTag ) { - ele->_value.TransferTo( parentEndTag ); - } - node->_memPool->SetTracked(); // created and then immediately deleted. - DeleteNode( node ); - return p; - } - - // Handle an end tag returned to this level. - // And handle a bunch of annoying errors. - bool mismatch = false; - if ( endTag.Empty() ) { - if ( ele->ClosingType() == XMLElement::OPEN ) { - mismatch = true; - } - } - else { - if ( ele->ClosingType() != XMLElement::OPEN ) { - mismatch = true; - } - else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { - mismatch = true; - } - } - if ( mismatch ) { - _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, TINYXML2_STR("XMLElement name=%s"), ele->Name()); - DeleteNode( node ); - break; - } - } - InsertEndChild( node ); - } - return 0; -} - -/*static*/ void XMLNode::DeleteNode( XMLNode* node ) -{ - if ( node == 0 ) { - return; - } - TIXMLASSERT(node->_document); - if (!node->ToDocument()) { - node->_document->MarkInUse(node); - } - - MemPool* pool = node->_memPool; - node->~XMLNode(); - pool->Free( node ); -} - -void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const -{ - TIXMLASSERT( insertThis ); - TIXMLASSERT( insertThis->_document == _document ); - - if (insertThis->_parent) { - insertThis->_parent->Unlink( insertThis ); - } - else { - insertThis->_document->MarkInUse(insertThis); - insertThis->_memPool->SetTracked(); - } -} - -const XMLElement* XMLNode::ToElementWithName( const TCHAR* name ) const -{ - const XMLElement* element = this->ToElement(); - if ( element == 0 ) { - return 0; - } - if ( name == 0 ) { - return element; - } - if ( XMLUtil::StringEqual( element->Name(), name ) ) { - return element; - } - return 0; -} - -// --------- XMLText ---------- // -TCHAR* XMLText::ParseDeep( TCHAR* p, StrPair*, int* curLineNumPtr ) -{ - if ( this->CData() ) { - p = _value.ParseText( p, TINYXML2_STR("]]>"), StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 ); - } - return p; - } - else { - int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; - if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { - flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; - } - - p = _value.ParseText( p, TINYXML2_STR("<"), flags, curLineNumPtr ); - if ( p && *p ) { - return p-1; - } - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 ); - } - } - return 0; -} - - -XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? - text->SetCData( this->CData() ); - return text; -} - - -bool XMLText::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLText* text = compare->ToText(); - return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); -} - - -bool XMLText::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - - -// --------- XMLComment ---------- // - -XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLComment::~XMLComment() -{ -} - - -TCHAR* XMLComment::ParseDeep( TCHAR* p, StrPair*, int* curLineNumPtr ) -{ - // Comment parses as text. - p = _value.ParseText( p, TINYXML2_STR("-->"), StrPair::COMMENT, curLineNumPtr ); - if ( p == 0 ) { - _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 ); - } - return p; -} - - -XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? - return comment; -} - - -bool XMLComment::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLComment* comment = compare->ToComment(); - return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); -} - - -bool XMLComment::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - - -// --------- XMLDeclaration ---------- // - -XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLDeclaration::~XMLDeclaration() -{ - //printf( TINYXML2_STR("~XMLDeclaration\n") ); -} - - -TCHAR* XMLDeclaration::ParseDeep( TCHAR* p, StrPair*, int* curLineNumPtr ) -{ - // Declaration parses as text. - p = _value.ParseText( p, TINYXML2_STR("?>"), StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); - if ( p == 0 ) { - _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 ); - } - return p; -} - - -XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? - return dec; -} - - -bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLDeclaration* declaration = compare->ToDeclaration(); - return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); -} - - - -bool XMLDeclaration::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - -// --------- XMLUnknown ---------- // - -XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLUnknown::~XMLUnknown() -{ -} - - -TCHAR* XMLUnknown::ParseDeep( TCHAR* p, StrPair*, int* curLineNumPtr ) -{ - // Unknown parses as text. - p = _value.ParseText( p, TINYXML2_STR(">"), StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 ); - } - return p; -} - - -XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? - return text; -} - - -bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLUnknown* unknown = compare->ToUnknown(); - return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); -} - - -bool XMLUnknown::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - -// --------- XMLAttribute ---------- // - -const TCHAR* XMLAttribute::Name() const -{ - return _name.GetStr(); -} - -const TCHAR* XMLAttribute::Value() const -{ - return _value.GetStr(); -} - -TCHAR* XMLAttribute::ParseDeep( TCHAR* p, bool processEntities, int* curLineNumPtr ) -{ - // Parse using the name rules: bug fix, was using ParseText before - p = _name.ParseName( p ); - if ( !p || !*p ) { - return 0; - } - - // Skip white space before = - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - if ( *p != '=' ) { - return 0; - } - - ++p; // move up to opening quote - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - if ( *p != '\"' && *p != '\'' ) { - return 0; - } - - const TCHAR endTag[2] = { *p, 0 }; - ++p; // move past opening quote - - p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); - return p; -} - - -void XMLAttribute::SetName( const TCHAR* n ) -{ - _name.SetStr( n ); -} - - -XMLError XMLAttribute::QueryIntValue( int* value ) const -{ - if ( XMLUtil::ToInt( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const -{ - if ( XMLUtil::ToUnsigned( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryInt64Value(int64_t* value) const -{ - if (XMLUtil::ToInt64(Value(), value)) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryUnsigned64Value(uint64_t* value) const -{ - if(XMLUtil::ToUnsigned64(Value(), value)) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryBoolValue( bool* value ) const -{ - if ( XMLUtil::ToBool( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryFloatValue( float* value ) const -{ - if ( XMLUtil::ToFloat( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryDoubleValue( double* value ) const -{ - if ( XMLUtil::ToDouble( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -void XMLAttribute::SetAttribute( const TCHAR* v ) -{ - _value.SetStr( v ); -} - - -void XMLAttribute::SetAttribute( int v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -void XMLAttribute::SetAttribute( unsigned v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -void XMLAttribute::SetAttribute(int64_t v) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - _value.SetStr(buf); -} - -void XMLAttribute::SetAttribute(uint64_t v) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - _value.SetStr(buf); -} - - -void XMLAttribute::SetAttribute( bool v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - -void XMLAttribute::SetAttribute( double v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - -void XMLAttribute::SetAttribute( float v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -// --------- XMLElement ---------- // -XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), - _closingType( OPEN ), - _rootAttribute( 0 ) -{ -} - - -XMLElement::~XMLElement() -{ - while( _rootAttribute ) { - XMLAttribute* next = _rootAttribute->_next; - DeleteAttribute( _rootAttribute ); - _rootAttribute = next; - } -} - - -const XMLAttribute* XMLElement::FindAttribute( const TCHAR* name ) const -{ - for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { - if ( XMLUtil::StringEqual( a->Name(), name ) ) { - return a; - } - } - return 0; -} - - -const TCHAR* XMLElement::Attribute( const TCHAR* name, const TCHAR* value ) const -{ - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return 0; - } - if ( !value || XMLUtil::StringEqual( a->Value(), value )) { - return a->Value(); - } - return 0; -} - -int XMLElement::IntAttribute(const TCHAR* name, int defaultValue) const -{ - int i = defaultValue; - QueryIntAttribute(name, &i); - return i; -} - -unsigned XMLElement::UnsignedAttribute(const TCHAR* name, unsigned defaultValue) const -{ - unsigned i = defaultValue; - QueryUnsignedAttribute(name, &i); - return i; -} - -int64_t XMLElement::Int64Attribute(const TCHAR* name, int64_t defaultValue) const -{ - int64_t i = defaultValue; - QueryInt64Attribute(name, &i); - return i; -} - -uint64_t XMLElement::Unsigned64Attribute(const TCHAR* name, uint64_t defaultValue) const -{ - uint64_t i = defaultValue; - QueryUnsigned64Attribute(name, &i); - return i; -} - -bool XMLElement::BoolAttribute(const TCHAR* name, bool defaultValue) const -{ - bool b = defaultValue; - QueryBoolAttribute(name, &b); - return b; -} - -double XMLElement::DoubleAttribute(const TCHAR* name, double defaultValue) const -{ - double d = defaultValue; - QueryDoubleAttribute(name, &d); - return d; -} - -float XMLElement::FloatAttribute(const TCHAR* name, float defaultValue) const -{ - float f = defaultValue; - QueryFloatAttribute(name, &f); - return f; -} - -const TCHAR* XMLElement::GetText() const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - return FirstChild()->Value(); - } - return 0; -} - - -void XMLElement::SetText( const TCHAR* inText ) -{ - if ( FirstChild() && FirstChild()->ToText() ) - FirstChild()->SetValue( inText ); - else { - XMLText* theText = GetDocument()->NewText( inText ); - InsertFirstChild( theText ); - } -} - - -void XMLElement::SetText( int v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( unsigned v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText(int64_t v) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - SetText(buf); -} - -void XMLElement::SetText(uint64_t v) { - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - SetText(buf); -} - - -void XMLElement::SetText( bool v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( float v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( double v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -XMLError XMLElement::QueryIntText( int* ival ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const TCHAR* t = FirstChild()->Value(); - if ( XMLUtil::ToInt( t, ival ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const TCHAR* t = FirstChild()->Value(); - if ( XMLUtil::ToUnsigned( t, uval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryInt64Text(int64_t* ival) const -{ - if (FirstChild() && FirstChild()->ToText()) { - const TCHAR* t = FirstChild()->Value(); - if (XMLUtil::ToInt64(t, ival)) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryUnsigned64Text(uint64_t* ival) const -{ - if(FirstChild() && FirstChild()->ToText()) { - const TCHAR* t = FirstChild()->Value(); - if(XMLUtil::ToUnsigned64(t, ival)) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryBoolText( bool* bval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const TCHAR* t = FirstChild()->Value(); - if ( XMLUtil::ToBool( t, bval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryDoubleText( double* dval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const TCHAR* t = FirstChild()->Value(); - if ( XMLUtil::ToDouble( t, dval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryFloatText( float* fval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const TCHAR* t = FirstChild()->Value(); - if ( XMLUtil::ToFloat( t, fval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - -int XMLElement::IntText(int defaultValue) const -{ - int i = defaultValue; - QueryIntText(&i); - return i; -} - -unsigned XMLElement::UnsignedText(unsigned defaultValue) const -{ - unsigned i = defaultValue; - QueryUnsignedText(&i); - return i; -} - -int64_t XMLElement::Int64Text(int64_t defaultValue) const -{ - int64_t i = defaultValue; - QueryInt64Text(&i); - return i; -} - -uint64_t XMLElement::Unsigned64Text(uint64_t defaultValue) const -{ - uint64_t i = defaultValue; - QueryUnsigned64Text(&i); - return i; -} - -bool XMLElement::BoolText(bool defaultValue) const -{ - bool b = defaultValue; - QueryBoolText(&b); - return b; -} - -double XMLElement::DoubleText(double defaultValue) const -{ - double d = defaultValue; - QueryDoubleText(&d); - return d; -} - -float XMLElement::FloatText(float defaultValue) const -{ - float f = defaultValue; - QueryFloatText(&f); - return f; -} - - -XMLAttribute* XMLElement::FindOrCreateAttribute( const TCHAR* name ) -{ - XMLAttribute* last = 0; - XMLAttribute* attrib = 0; - for( attrib = _rootAttribute; - attrib; - last = attrib, attrib = attrib->_next ) { - if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { - break; - } - } - if ( !attrib ) { - attrib = CreateAttribute(); - TIXMLASSERT( attrib ); - if ( last ) { - TIXMLASSERT( last->_next == 0 ); - last->_next = attrib; - } - else { - TIXMLASSERT( _rootAttribute == 0 ); - _rootAttribute = attrib; - } - attrib->SetName( name ); - } - return attrib; -} - - -void XMLElement::DeleteAttribute( const TCHAR* name ) -{ - XMLAttribute* prev = 0; - for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { - if ( XMLUtil::StringEqual( name, a->Name() ) ) { - if ( prev ) { - prev->_next = a->_next; - } - else { - _rootAttribute = a->_next; - } - DeleteAttribute( a ); - break; - } - prev = a; - } -} - - -TCHAR* XMLElement::ParseAttributes( TCHAR* p, int* curLineNumPtr ) -{ - XMLAttribute* prevAttribute = 0; - - // Read the attributes. - while( p ) { - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - if ( !(*p) ) { - _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, TINYXML2_STR("XMLElement name=%s"), Name() ); - return 0; - } - - // attribute. - if (XMLUtil::IsNameStartChar( *p ) ) { - XMLAttribute* attrib = CreateAttribute(); - TIXMLASSERT( attrib ); - attrib->_parseLineNum = _document->_parseCurLineNum; - - const int attrLineNum = attrib->_parseLineNum; - - p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); - if ( !p || Attribute( attrib->Name() ) ) { - DeleteAttribute( attrib ); - _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, TINYXML2_STR("XMLElement name=%s"), Name() ); - return 0; - } - // There is a minor bug here: if the attribute in the source xml - // document is duplicated, it will not be detected and the - // attribute will be doubly added. However, tracking the 'prevAttribute' - // avoids re-scanning the attribute list. Preferring performance for - // now, may reconsider in the future. - if ( prevAttribute ) { - TIXMLASSERT( prevAttribute->_next == 0 ); - prevAttribute->_next = attrib; - } - else { - TIXMLASSERT( _rootAttribute == 0 ); - _rootAttribute = attrib; - } - prevAttribute = attrib; - } - // end of the tag - else if ( *p == '>' ) { - ++p; - break; - } - // end of the tag - else if ( *p == '/' && *(p+1) == '>' ) { - _closingType = CLOSED; - return p+2; // done; sealed element. - } - else { - _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 ); - return 0; - } - } - return p; -} - -void XMLElement::DeleteAttribute( XMLAttribute* attribute ) -{ - if ( attribute == 0 ) { - return; - } - MemPool* pool = attribute->_memPool; - attribute->~XMLAttribute(); - pool->Free( attribute ); -} - -XMLAttribute* XMLElement::CreateAttribute() -{ - TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); - XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); - TIXMLASSERT( attrib ); - attrib->_memPool = &_document->_attributePool; - attrib->_memPool->SetTracked(); - return attrib; -} - -// -// -// foobar -// -TCHAR* XMLElement::ParseDeep( TCHAR* p, StrPair* parentEndTag, int* curLineNumPtr ) -{ - // Read the element name. - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - - // The closing element is the form. It is - // parsed just like a regular element then deleted from - // the DOM. - if ( *p == '/' ) { - _closingType = CLOSING; - ++p; - } - - p = _value.ParseName( p ); - if ( _value.Empty() ) { - return 0; - } - - p = ParseAttributes( p, curLineNumPtr ); - if ( !p || !*p || _closingType != OPEN ) { - return p; - } - - p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr ); - return p; -} - - - -XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? - for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { - element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? - } - return element; -} - - -bool XMLElement::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLElement* other = compare->ToElement(); - if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { - - const XMLAttribute* a=FirstAttribute(); - const XMLAttribute* b=other->FirstAttribute(); - - while ( a && b ) { - if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { - return false; - } - a = a->Next(); - b = b->Next(); - } - if ( a || b ) { - // different count - return false; - } - return true; - } - return false; -} - - -bool XMLElement::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - if ( visitor->VisitEnter( *this, _rootAttribute ) ) { - for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { - if ( !node->Accept( visitor ) ) { - break; - } - } - } - return visitor->VisitExit( *this ); -} - - -// --------- XMLDocument ----------- // - -// Warning: List must match 'enum XMLError' -const TCHAR* XMLDocument::_errorNames[XML_ERROR_COUNT] = { - TINYXML2_STR("XML_SUCCESS"), - TINYXML2_STR("XML_NO_ATTRIBUTE"), - TINYXML2_STR("XML_WRONG_ATTRIBUTE_TYPE"), - TINYXML2_STR("XML_ERROR_FILE_NOT_FOUND"), - TINYXML2_STR("XML_ERROR_FILE_COULD_NOT_BE_OPENED"), - TINYXML2_STR("XML_ERROR_FILE_READ_ERROR"), - TINYXML2_STR("XML_ERROR_PARSING_ELEMENT"), - TINYXML2_STR("XML_ERROR_PARSING_ATTRIBUTE"), - TINYXML2_STR("XML_ERROR_PARSING_TEXT"), - TINYXML2_STR("XML_ERROR_PARSING_CDATA"), - TINYXML2_STR("XML_ERROR_PARSING_COMMENT"), - TINYXML2_STR("XML_ERROR_PARSING_DECLARATION"), - TINYXML2_STR("XML_ERROR_PARSING_UNKNOWN"), - TINYXML2_STR("XML_ERROR_EMPTY_DOCUMENT"), - TINYXML2_STR("XML_ERROR_MISMATCHED_ELEMENT"), - TINYXML2_STR("XML_ERROR_PARSING"), - TINYXML2_STR("XML_CAN_NOT_CONVERT_TEXT"), - TINYXML2_STR("XML_NO_TEXT_NODE"), - TINYXML2_STR("XML_ELEMENT_DEPTH_EXCEEDED") -}; - - -XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) : - XMLNode( 0 ), - _writeBOM( false ), - _processEntities( processEntities ), - _errorID(XML_SUCCESS), - _whitespaceMode( whitespaceMode ), - _errorStr(), - _errorLineNum( 0 ), - _charBuffer( 0 ), - _parseCurLineNum( 0 ), - _parsingDepth(0), - _unlinked(), - _elementPool(), - _attributePool(), - _textPool(), - _commentPool() -{ - // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) - _document = this; -} - - -XMLDocument::~XMLDocument() -{ - Clear(); -} - - -void XMLDocument::MarkInUse(XMLNode* node) -{ - TIXMLASSERT(node); - TIXMLASSERT(node->_parent == 0); - - for (int i = 0; i < _unlinked.Size(); ++i) { - if (node == _unlinked[i]) { - _unlinked.SwapRemove(i); - break; - } - } -} - -void XMLDocument::Clear() -{ - DeleteChildren(); - while( _unlinked.Size()) { - DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete. - } - -#ifdef TINYXML2_DEBUG - const bool hadError = Error(); -#endif - ClearError(); - - delete [] _charBuffer; - _charBuffer = 0; - _parsingDepth = 0; - -#if 0 - _textPool.Trace( TINYXML2_STR("text") ); - _elementPool.Trace( TINYXML2_STR("element") ); - _commentPool.Trace( TINYXML2_STR("comment") ); - _attributePool.Trace( TINYXML2_STR("attribute") ); -#endif - -#ifdef TINYXML2_DEBUG - if ( !hadError ) { - TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); - TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); - TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); - TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); - } -#endif -} - - -void XMLDocument::DeepCopy(XMLDocument* target) const -{ - TIXMLASSERT(target); - if (target == this) { - return; // technically success - a no-op. - } - - target->Clear(); - for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) { - target->InsertEndChild(node->DeepClone(target)); - } -} - -XMLElement* XMLDocument::NewElement( const TCHAR* name ) -{ - XMLElement* ele = CreateUnlinkedNode( _elementPool ); - ele->SetName( name ); - return ele; -} - - -XMLComment* XMLDocument::NewComment( const TCHAR* str ) -{ - XMLComment* comment = CreateUnlinkedNode( _commentPool ); - comment->SetValue( str ); - return comment; -} - - -XMLText* XMLDocument::NewText( const TCHAR* str ) -{ - XMLText* text = CreateUnlinkedNode( _textPool ); - text->SetValue( str ); - return text; -} - - -XMLDeclaration* XMLDocument::NewDeclaration( const TCHAR* str ) -{ - XMLDeclaration* dec = CreateUnlinkedNode( _commentPool ); - dec->SetValue( str ? str : TINYXML2_STR("xml version=\"1.0\" encoding=\"UTF-8\"") ); - return dec; -} - - -XMLUnknown* XMLDocument::NewUnknown( const TCHAR* str ) -{ - XMLUnknown* unk = CreateUnlinkedNode( _commentPool ); - unk->SetValue( str ); - return unk; -} - -static FILE* callfopen( const TCHAR* filepath, const TCHAR* mode ) -{ - TIXMLASSERT( filepath ); - TIXMLASSERT( mode ); -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) - FILE* fp = 0; - const errno_t err = fopen_s( &fp, filepath, mode ); - if ( err ) { - return 0; - } -#else - FILE* fp = fopen( filepath, mode ); -#endif - return fp; -} - -void XMLDocument::DeleteNode( XMLNode* node ) { - TIXMLASSERT( node ); - TIXMLASSERT(node->_document == this ); - if (node->_parent) { - node->_parent->DeleteChild( node ); - } - else { - // Isn't in the tree. - // Use the parent delete. - // Also, we need to mark it tracked: we 'know' - // it was never used. - node->_memPool->SetTracked(); - // Call the static XMLNode version: - XMLNode::DeleteNode(node); - } -} - - -XMLError XMLDocument::LoadFile( const TCHAR* filename ) -{ - if ( !filename ) { - TIXMLASSERT( false ); - SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, TINYXML2_STR("filename=") ); - return _errorID; - } - - Clear(); - FILE* fp = callfopen( filename, TINYXML2_STR("rb") ); - if ( !fp ) { - SetError( XML_ERROR_FILE_NOT_FOUND, 0, TINYXML2_STR("filename=%s"), filename ); - return _errorID; - } - LoadFile( fp ); - fclose( fp ); - return _errorID; -} - -// This is likely overengineered template art to have a check that unsigned long value incremented -// by one still fits into size_t. If size_t type is larger than unsigned long type -// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit -// -Wtype-limits warning. This piece makes the compiler select code with a check when a check -// is useful and code with no check when a check is redundant depending on how size_t and unsigned long -// types sizes relate to each other. -template -= sizeof(size_t))> -struct LongFitsIntoSizeTMinusOne { - static bool Fits( unsigned long value ) - { - return value < static_cast(-1); - } -}; - -template <> -struct LongFitsIntoSizeTMinusOne { - static bool Fits( unsigned long ) - { - return true; - } -}; - -XMLError XMLDocument::LoadFile( FILE* fp ) -{ - Clear(); - - fseek( fp, 0, SEEK_SET ); - if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - - fseek( fp, 0, SEEK_END ); - const long filelength = ftell( fp ); - fseek( fp, 0, SEEK_SET ); - if ( filelength == -1L ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - TIXMLASSERT( filelength >= 0 ); - - if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) { - // Cannot handle files which won't fit in buffer together with null terminator - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - - if ( filelength == 0 ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return _errorID; - } - - const size_t size = filelength; - TIXMLASSERT( _charBuffer == 0 ); - _charBuffer = new TCHAR[size+1]; - const size_t read = fread( _charBuffer, 1, size * sizeof(TCHAR), fp ); - if ( read != size ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - - _charBuffer[size] = 0; - - Parse(); - return _errorID; -} - - -XMLError XMLDocument::SaveFile( const TCHAR* filename, bool compact ) -{ - if ( !filename ) { - TIXMLASSERT( false ); - SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, TINYXML2_STR("filename=") ); - return _errorID; - } - - FILE* fp = callfopen( filename, TINYXML2_STR("w") ); - if ( !fp ) { - SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, TINYXML2_STR("filename=%s"), filename ); - return _errorID; - } - SaveFile(fp, compact); - fclose( fp ); - return _errorID; -} - - -XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) -{ - // Clear any error from the last save, otherwise it will get reported - // for *this* call. - ClearError(); - XMLPrinter stream( fp, compact ); - Print( &stream ); - return _errorID; -} - - -XMLError XMLDocument::Parse( const TCHAR* p, size_t len ) -{ - Clear(); - - if ( len == 0 || !p || !*p ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return _errorID; - } - if ( len == static_cast(-1) ) { - len = strlen( p ); - } - TIXMLASSERT( _charBuffer == 0 ); - _charBuffer = new TCHAR[ len+1 ]; - memcpy( _charBuffer, p, len * sizeof(TCHAR) ); - _charBuffer[len] = 0; - - Parse(); - if ( Error() ) { - // clean up now essentially dangling memory. - // and the parse fail can put objects in the - // pools that are dead and inaccessible. - DeleteChildren(); - _elementPool.Clear(); - _attributePool.Clear(); - _textPool.Clear(); - _commentPool.Clear(); - } - return _errorID; -} - - -void XMLDocument::Print( XMLPrinter* streamer ) const -{ - if ( streamer ) { - Accept( streamer ); - } - else { - XMLPrinter stdoutStreamer( stdout ); - Accept( &stdoutStreamer ); - } -} - - -void XMLDocument::SetError( XMLError error, int lineNum, const TCHAR* format, ... ) -{ - TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); - _errorID = error; - _errorLineNum = lineNum; - _errorStr.Reset(); - - const size_t BUFFER_SIZE = 1000; - TCHAR* buffer = new TCHAR[BUFFER_SIZE]; - - TIXMLASSERT(sizeof(error) <= sizeof(int)); - TIXML_SNPRINTF(buffer, BUFFER_SIZE, TINYXML2_STR("Error=%s ErrorID=%d (0x%x) Line number=%d"), ErrorIDToName(error), int(error), int(error), lineNum); - - if (format) { - size_t len = strlen(buffer); - TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, TINYXML2_STR(": ")); - len = strlen(buffer); - - va_list va; - va_start(va, format); - TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va); - va_end(va); - } - _errorStr.SetStr(buffer); - delete[] buffer; -} - - -/*static*/ const TCHAR* XMLDocument::ErrorIDToName(XMLError errorID) -{ - TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT ); - const TCHAR* errorName = _errorNames[errorID]; - TIXMLASSERT( errorName && errorName[0] ); - return errorName; -} - -const TCHAR* XMLDocument::ErrorStr() const -{ - return _errorStr.Empty() ? TINYXML2_STR("") : _errorStr.GetStr(); -} - - -void XMLDocument::PrintError() const -{ - printf(TINYXML2_STR("%s\n"), ErrorStr()); -} - -const TCHAR* XMLDocument::ErrorName() const -{ - return ErrorIDToName(_errorID); -} - -void XMLDocument::Parse() -{ - TIXMLASSERT( NoChildren() ); // Clear() must have been called previously - TIXMLASSERT( _charBuffer ); - _parseCurLineNum = 1; - _parseLineNum = 1; - TCHAR* p = _charBuffer; - p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); - p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); - if ( !*p ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return; - } - ParseDeep(p, 0, &_parseCurLineNum ); -} - -void XMLDocument::PushDepth() -{ - _parsingDepth++; - if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) { - SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, TINYXML2_STR("Element nesting is too deep.") ); - } -} - -void XMLDocument::PopDepth() -{ - TIXMLASSERT(_parsingDepth > 0); - --_parsingDepth; -} - -XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : - _elementJustOpened( false ), - _stack(), - _firstElement( true ), - _fp( file ), - _depth( depth ), - _textDepth( -1 ), - _processEntities( true ), - _compactMode( compact ), - _buffer() -{ - for( int i=0; i(entityValue); - TIXMLASSERT( flagIndex < ENTITY_RANGE ); - _entityFlag[flagIndex] = true; - } - _restrictedEntityFlag[static_cast('&')] = true; - _restrictedEntityFlag[static_cast('<')] = true; - _restrictedEntityFlag[static_cast('>')] = true; // not required, but consistency is nice - _buffer.Push( 0 ); -} - - -void XMLPrinter::Print( const TCHAR* format, ... ) -{ - va_list va; - va_start( va, format ); - - if ( _fp ) { - vfprintf( _fp, format, va ); - } - else { - const int len = TIXML_VSCPRINTF( format, va ); - // Close out and re-start the va-args - va_end( va ); - TIXMLASSERT( len >= 0 ); - va_start( va, format ); - TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); - TCHAR* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. - TIXML_VSNPRINTF( p, len+1, format, va ); - } - va_end( va ); -} - - -void XMLPrinter::Write( const TCHAR* data, size_t size ) -{ - if ( _fp ) { - fwrite ( data , sizeof(TCHAR), size, _fp); - } - else { - TCHAR* p = _buffer.PushArr( static_cast(size) ) - 1; // back up over the null terminator. - memcpy( p, data, size * sizeof(TCHAR) ); - p[size] = 0; - } -} - -void XMLPrinter::Write( const TCHAR* data) -{ - Write( data, strlen( data ) ); -} - - -void XMLPrinter::Putc( TCHAR ch ) -{ - if ( _fp ) { - fputc ( ch, _fp); - } - else { - TCHAR* p = _buffer.PushArr( sizeof(TCHAR) ) - 1; // back up over the null terminator. - p[0] = ch; - p[1] = 0; - } -} - - -void XMLPrinter::PrintSpace( int depth ) -{ - for( int i=0; i 0 && *q < ENTITY_RANGE ) { - // Check for entities. If one is found, flush - // the stream up until the entity, write the - // entity, and keep looking. - if ( flag[static_cast(*q)] ) { - while ( p < q ) { - const size_t delta = q - p; - const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast(delta); - Write( p, toPrint ); - p += toPrint; - } - bool entityPatternPrinted = false; - for( int i=0; i(delta); - Write( p, toPrint ); - } - } - else { - Write( p ); - } -} - - -void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) -{ - if ( writeBOM ) { - static const TUCHAR bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; - Write( reinterpret_cast< const TCHAR* >( bom ) ); - } - if ( writeDec ) { - PushDeclaration( TINYXML2_STR("xml version=\"1.0\"") ); - } -} - - -void XMLPrinter::OpenElement( const TCHAR* name, bool compactMode ) -{ - SealElementIfJustOpened(); - _stack.Push( name ); - - if ( _textDepth < 0 && !_firstElement && !compactMode ) { - Putc( '\n' ); - } - if ( !compactMode ) { - PrintSpace( _depth ); - } - - Write ( TINYXML2_STR("<") ); - Write ( name ); - - _elementJustOpened = true; - _firstElement = false; - ++_depth; -} - - -void XMLPrinter::PushAttribute( const TCHAR* name, const TCHAR* value ) -{ - TIXMLASSERT( _elementJustOpened ); - Putc ( ' ' ); - Write( name ); - Write( TINYXML2_STR("=\"") ); - PrintString( value, false ); - Putc ( '\"' ); -} - - -void XMLPrinter::PushAttribute( const TCHAR* name, int v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute( const TCHAR* name, unsigned v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute(const TCHAR* name, int64_t v) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - PushAttribute(name, buf); -} - - -void XMLPrinter::PushAttribute(const TCHAR* name, uint64_t v) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - PushAttribute(name, buf); -} - - -void XMLPrinter::PushAttribute( const TCHAR* name, bool v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute( const TCHAR* name, double v ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::CloseElement( bool compactMode ) -{ - --_depth; - const TCHAR* name = _stack.Pop(); - - if ( _elementJustOpened ) { - Write( TINYXML2_STR("/>") ); - } - else { - if ( _textDepth < 0 && !compactMode) { - Putc( '\n' ); - PrintSpace( _depth ); - } - Write ( TINYXML2_STR("") ); - } - - if ( _textDepth == _depth ) { - _textDepth = -1; - } - if ( _depth == 0 && !compactMode) { - Putc( '\n' ); - } - _elementJustOpened = false; -} - - -void XMLPrinter::SealElementIfJustOpened() -{ - if ( !_elementJustOpened ) { - return; - } - _elementJustOpened = false; - Putc( '>' ); -} - - -void XMLPrinter::PushText( const TCHAR* text, bool cdata ) -{ - _textDepth = _depth-1; - - SealElementIfJustOpened(); - if ( cdata ) { - Write( TINYXML2_STR("") ); - } - else { - PrintString( text, true ); - } -} - - -void XMLPrinter::PushText( int64_t value ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( uint64_t value ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr(value, buf, BUF_SIZE); - PushText(buf, false); -} - - -void XMLPrinter::PushText( int value ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( unsigned value ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( bool value ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( float value ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( double value ) -{ - TCHAR buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushComment( const TCHAR* comment ) -{ - SealElementIfJustOpened(); - if ( _textDepth < 0 && !_firstElement && !_compactMode) { - Putc( '\n' ); - PrintSpace( _depth ); - } - _firstElement = false; - - Write( TINYXML2_STR("") ); -} - - -void XMLPrinter::PushDeclaration( const TCHAR* value ) -{ - SealElementIfJustOpened(); - if ( _textDepth < 0 && !_firstElement && !_compactMode) { - Putc( '\n' ); - PrintSpace( _depth ); - } - _firstElement = false; - - Write( TINYXML2_STR("") ); -} - - -void XMLPrinter::PushUnknown( const TCHAR* value ) -{ - SealElementIfJustOpened(); - if ( _textDepth < 0 && !_firstElement && !_compactMode) { - Putc( '\n' ); - PrintSpace( _depth ); - } - _firstElement = false; - - Write( TINYXML2_STR("' ); -} - - -bool XMLPrinter::VisitEnter( const XMLDocument& doc ) -{ - _processEntities = doc.ProcessEntities(); - if ( doc.HasBOM() ) { - PushHeader( true, false ); - } - return true; -} - - -bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) -{ - const XMLElement* parentElem = 0; - if ( element.Parent() ) { - parentElem = element.Parent()->ToElement(); - } - const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; - OpenElement( element.Name(), compactMode ); - while ( attribute ) { - PushAttribute( attribute->Name(), attribute->Value() ); - attribute = attribute->Next(); - } - return true; -} - - -bool XMLPrinter::VisitExit( const XMLElement& element ) -{ - CloseElement( CompactMode(element) ); - return true; -} - - -bool XMLPrinter::Visit( const XMLText& text ) -{ - PushText( text.Value(), text.CData() ); - return true; -} - - -bool XMLPrinter::Visit( const XMLComment& comment ) -{ - PushComment( comment.Value() ); - return true; -} - -bool XMLPrinter::Visit( const XMLDeclaration& declaration ) -{ - PushDeclaration( declaration.Value() ); - return true; -} - - -bool XMLPrinter::Visit( const XMLUnknown& unknown ) -{ - PushUnknown( unknown.Value() ); - return true; -} - -} // namespace tinyxml2 diff --git a/src/3rd-party/tinyxml2/tinyxml2.h b/src/3rd-party/tinyxml2/tinyxml2.h deleted file mode 100644 index 80675fcb..00000000 --- a/src/3rd-party/tinyxml2/tinyxml2.h +++ /dev/null @@ -1,2379 +0,0 @@ -/* -Original code by Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#ifndef TINYXML2_INCLUDED -#define TINYXML2_INCLUDED - -#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) -# include -# include -# include -# include -# include -# if defined(__PS3__) -# include -# endif -#else -# include -# include -# include -# include -# include -#endif -#include - -/* - TODO: intern strings instead of allocation. -*/ -/* - gcc: - g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe - - Formatting, Artistic Style: - AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h -*/ - -#if defined( _DEBUG ) || defined (__DEBUG__) -# ifndef TINYXML2_DEBUG -# define TINYXML2_DEBUG -# endif -#endif - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4251) -#endif - -#ifdef _WIN32 -# ifdef TINYXML2_EXPORT -# define TINYXML2_LIB __declspec(dllexport) -# elif defined(TINYXML2_IMPORT) -# define TINYXML2_LIB __declspec(dllimport) -# else -# define TINYXML2_LIB -# endif -#elif __GNUC__ >= 4 -# define TINYXML2_LIB __attribute__((visibility("default"))) -#else -# define TINYXML2_LIB -#endif - - -#if defined(TINYXML2_DEBUG) -# if defined(_MSC_VER) -# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like -# define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } -# elif defined (ANDROID_NDK) -# include -# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } -# else -# include -# define TIXMLASSERT assert -# endif -#else -# define TIXMLASSERT( x ) {} -#endif - - -/* Versioning, past 1.0.14: - http://semver.org/ -*/ -static const int TIXML2_MAJOR_VERSION = 7; -static const int TIXML2_MINOR_VERSION = 1; -static const int TIXML2_PATCH_VERSION = 0; - -#define TINYXML2_MAJOR_VERSION 7 -#define TINYXML2_MINOR_VERSION 1 -#define TINYXML2_PATCH_VERSION 0 - -/* - Add by Nomango 2019.10.12 -*/ -#define TINYXML2_STR(STR) (L##STR) - -// A fixed element depth limit is problematic. There needs to be a -// limit to avoid a stack overflow. However, that limit varies per -// system, and the capacity of the stack. On the other hand, it's a trivial -// attack that can result from ill, malicious, or even correctly formed XML, -// so there needs to be a limit in place. -static const int TINYXML2_MAX_ELEMENT_DEPTH = 100; - -namespace tinyxml2 -{ - -/* - Add by Nomango 2019.10.12 -*/ -typedef wchar_t TCHAR; -typedef wchar_t TUCHAR; - -class XMLDocument; -class XMLElement; -class XMLAttribute; -class XMLComment; -class XMLText; -class XMLDeclaration; -class XMLUnknown; -class XMLPrinter; - -/* - A class that wraps strings. Normally stores the start and end - pointers into the XML file itself, and will apply normalization - and entity translation if actually read. Can also store (and memory - manage) a traditional char[] - - Isn't clear why TINYXML2_LIB is needed; but seems to fix #719 -*/ -class TINYXML2_LIB StrPair -{ -public: - enum { - NEEDS_ENTITY_PROCESSING = 0x01, - NEEDS_NEWLINE_NORMALIZATION = 0x02, - NEEDS_WHITESPACE_COLLAPSING = 0x04, - - TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_NAME = 0, - ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - COMMENT = NEEDS_NEWLINE_NORMALIZATION - }; - - StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} - ~StrPair(); - - void Set( TCHAR* start, TCHAR* end, int flags ) { - TIXMLASSERT( start ); - TIXMLASSERT( end ); - Reset(); - _start = start; - _end = end; - _flags = flags | NEEDS_FLUSH; - } - - const TCHAR* GetStr(); - - bool Empty() const { - return _start == _end; - } - - void SetInternedStr( const TCHAR* str ) { - Reset(); - _start = const_cast(str); - } - - void SetStr( const TCHAR* str, int flags=0 ); - - TCHAR* ParseText( TCHAR* in, const TCHAR* endTag, int strFlags, int* curLineNumPtr ); - TCHAR* ParseName( TCHAR* in ); - - void TransferTo( StrPair* other ); - void Reset(); - -private: - void CollapseWhitespace(); - - enum { - NEEDS_FLUSH = 0x100, - NEEDS_DELETE = 0x200 - }; - - int _flags; - TCHAR* _start; - TCHAR* _end; - - StrPair( const StrPair& other ); // not supported - void operator=( const StrPair& other ); // not supported, use TransferTo() -}; - - -/* - A dynamic array of Plain Old Data. Doesn't support constructors, etc. - Has a small initial memory pool, so that low or no usage will not - cause a call to new/delete -*/ -template -class DynArray -{ -public: - DynArray() : - _mem( _pool ), - _allocated( INITIAL_SIZE ), - _size( 0 ) - { - } - - ~DynArray() { - if ( _mem != _pool ) { - delete [] _mem; - } - } - - void Clear() { - _size = 0; - } - - void Push( T t ) { - TIXMLASSERT( _size < INT_MAX ); - EnsureCapacity( _size+1 ); - _mem[_size] = t; - ++_size; - } - - T* PushArr( int count ) { - TIXMLASSERT( count >= 0 ); - TIXMLASSERT( _size <= INT_MAX - count ); - EnsureCapacity( _size+count ); - T* ret = &_mem[_size]; - _size += count; - return ret; - } - - T Pop() { - TIXMLASSERT( _size > 0 ); - --_size; - return _mem[_size]; - } - - void PopArr( int count ) { - TIXMLASSERT( _size >= count ); - _size -= count; - } - - bool Empty() const { - return _size == 0; - } - - T& operator[](int i) { - TIXMLASSERT( i>= 0 && i < _size ); - return _mem[i]; - } - - const T& operator[](int i) const { - TIXMLASSERT( i>= 0 && i < _size ); - return _mem[i]; - } - - const T& PeekTop() const { - TIXMLASSERT( _size > 0 ); - return _mem[ _size - 1]; - } - - int Size() const { - TIXMLASSERT( _size >= 0 ); - return _size; - } - - int Capacity() const { - TIXMLASSERT( _allocated >= INITIAL_SIZE ); - return _allocated; - } - - void SwapRemove(int i) { - TIXMLASSERT(i >= 0 && i < _size); - TIXMLASSERT(_size > 0); - _mem[i] = _mem[_size - 1]; - --_size; - } - - const T* Mem() const { - TIXMLASSERT( _mem ); - return _mem; - } - - T* Mem() { - TIXMLASSERT( _mem ); - return _mem; - } - -private: - DynArray( const DynArray& ); // not supported - void operator=( const DynArray& ); // not supported - - void EnsureCapacity( int cap ) { - TIXMLASSERT( cap > 0 ); - if ( cap > _allocated ) { - TIXMLASSERT( cap <= INT_MAX / 2 ); - const int newAllocated = cap * 2; - T* newMem = new T[newAllocated]; - TIXMLASSERT( newAllocated >= _size ); - memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs - if ( _mem != _pool ) { - delete [] _mem; - } - _mem = newMem; - _allocated = newAllocated; - } - } - - T* _mem; - T _pool[INITIAL_SIZE]; - int _allocated; // objects allocated - int _size; // number objects in use -}; - - -/* - Parent virtual class of a pool for fast allocation - and deallocation of objects. -*/ -class MemPool -{ -public: - MemPool() {} - virtual ~MemPool() {} - - virtual int ItemSize() const = 0; - virtual void* Alloc() = 0; - virtual void Free( void* ) = 0; - virtual void SetTracked() = 0; -}; - - -/* - Template child class to create pools of the correct type. -*/ -template< int ITEM_SIZE > -class MemPoolT : public MemPool -{ -public: - MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} - ~MemPoolT() { - MemPoolT< ITEM_SIZE >::Clear(); - } - - void Clear() { - // Delete the blocks. - while( !_blockPtrs.Empty()) { - Block* lastBlock = _blockPtrs.Pop(); - delete lastBlock; - } - _root = 0; - _currentAllocs = 0; - _nAllocs = 0; - _maxAllocs = 0; - _nUntracked = 0; - } - - virtual int ItemSize() const { - return ITEM_SIZE; - } - int CurrentAllocs() const { - return _currentAllocs; - } - - virtual void* Alloc() { - if ( !_root ) { - // Need a new block. - Block* block = new Block(); - _blockPtrs.Push( block ); - - Item* blockItems = block->items; - for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { - blockItems[i].next = &(blockItems[i + 1]); - } - blockItems[ITEMS_PER_BLOCK - 1].next = 0; - _root = blockItems; - } - Item* const result = _root; - TIXMLASSERT( result != 0 ); - _root = _root->next; - - ++_currentAllocs; - if ( _currentAllocs > _maxAllocs ) { - _maxAllocs = _currentAllocs; - } - ++_nAllocs; - ++_nUntracked; - return result; - } - - virtual void Free( void* mem ) { - if ( !mem ) { - return; - } - --_currentAllocs; - Item* item = static_cast( mem ); -#ifdef TINYXML2_DEBUG - memset( item, 0xfe, sizeof( *item ) ); -#endif - item->next = _root; - _root = item; - } - - void Trace( const char* name ) { - printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", - name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, - ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); - } - - void Trace( const wchar_t* name ) { - wprintf( L"Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", - name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, - ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); - } - - void SetTracked() { - --_nUntracked; - } - - int Untracked() const { - return _nUntracked; - } - - // This number is perf sensitive. 4k seems like a good tradeoff on my machine. - // The test file is large, 170k. - // Release: VS2010 gcc(no opt) - // 1k: 4000 - // 2k: 4000 - // 4k: 3900 21000 - // 16k: 5200 - // 32k: 4300 - // 64k: 4000 21000 - // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK - // in private part if ITEMS_PER_BLOCK is private - enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; - -private: - MemPoolT( const MemPoolT& ); // not supported - void operator=( const MemPoolT& ); // not supported - - union Item { - Item* next; - TCHAR itemData[ITEM_SIZE]; - }; - struct Block { - Item items[ITEMS_PER_BLOCK]; - }; - DynArray< Block*, 10 > _blockPtrs; - Item* _root; - - int _currentAllocs; - int _nAllocs; - int _maxAllocs; - int _nUntracked; -}; - - - -/** - Implements the interface to the "Visitor pattern" (see the Accept() method.) - If you call the Accept() method, it requires being passed a XMLVisitor - class to handle callbacks. For nodes that contain other nodes (Document, Element) - you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs - are simply called with Visit(). - - If you return 'true' from a Visit method, recursive parsing will continue. If you return - false, no children of this node or its siblings will be visited. - - All flavors of Visit methods have a default implementation that returns 'true' (continue - visiting). You need to only override methods that are interesting to you. - - Generally Accept() is called on the XMLDocument, although all nodes support visiting. - - You should never change the document from a callback. - - @sa XMLNode::Accept() -*/ -class TINYXML2_LIB XMLVisitor -{ -public: - virtual ~XMLVisitor() {} - - /// Visit a document. - virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { - return true; - } - /// Visit a document. - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { - return true; - } - - /// Visit an element. - virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { - return true; - } - /// Visit an element. - virtual bool VisitExit( const XMLElement& /*element*/ ) { - return true; - } - - /// Visit a declaration. - virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { - return true; - } - /// Visit a text node. - virtual bool Visit( const XMLText& /*text*/ ) { - return true; - } - /// Visit a comment node. - virtual bool Visit( const XMLComment& /*comment*/ ) { - return true; - } - /// Visit an unknown node. - virtual bool Visit( const XMLUnknown& /*unknown*/ ) { - return true; - } -}; - -// WARNING: must match XMLDocument::_errorNames[] -enum XMLError { - XML_SUCCESS = 0, - XML_NO_ATTRIBUTE, - XML_WRONG_ATTRIBUTE_TYPE, - XML_ERROR_FILE_NOT_FOUND, - XML_ERROR_FILE_COULD_NOT_BE_OPENED, - XML_ERROR_FILE_READ_ERROR, - XML_ERROR_PARSING_ELEMENT, - XML_ERROR_PARSING_ATTRIBUTE, - XML_ERROR_PARSING_TEXT, - XML_ERROR_PARSING_CDATA, - XML_ERROR_PARSING_COMMENT, - XML_ERROR_PARSING_DECLARATION, - XML_ERROR_PARSING_UNKNOWN, - XML_ERROR_EMPTY_DOCUMENT, - XML_ERROR_MISMATCHED_ELEMENT, - XML_ERROR_PARSING, - XML_CAN_NOT_CONVERT_TEXT, - XML_NO_TEXT_NODE, - XML_ELEMENT_DEPTH_EXCEEDED, - - XML_ERROR_COUNT -}; - - -/* - Utility functionality. -*/ -class TINYXML2_LIB XMLUtil -{ -public: - static const TCHAR* SkipWhiteSpace( const TCHAR* p, int* curLineNumPtr ) { - TIXMLASSERT( p ); - - while( IsWhiteSpace(*p) ) { - if (curLineNumPtr && *p == '\n') { - ++(*curLineNumPtr); - } - ++p; - } - TIXMLASSERT( p ); - return p; - } - static TCHAR* SkipWhiteSpace( TCHAR* p, int* curLineNumPtr ) { - return const_cast( SkipWhiteSpace( const_cast(p), curLineNumPtr ) ); - } - - // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't - // correct, but simple, and usually works. - static bool IsWhiteSpace( TCHAR p ) { - return !IsUTF8Continuation(p) && isspace( static_cast(p) ); - } - - inline static bool IsNameStartChar( TUCHAR ch ) { - if ( ch >= 128 ) { - // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() - return true; - } - if ( isalpha( ch ) ) { - return true; - } - return ch == ':' || ch == '_'; - } - - inline static bool IsNameChar( TUCHAR ch ) { - return IsNameStartChar( ch ) - || isdigit( ch ) - || ch == '.' - || ch == '-'; - } - - inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { - if ( p == q ) { - return true; - } - TIXMLASSERT( p ); - TIXMLASSERT( q ); - TIXMLASSERT( nChar >= 0 ); - return strncmp( p, q, nChar ) == 0; - } - - inline static bool StringEqual( const wchar_t* p, const wchar_t* q, int nChar=INT_MAX ) { - if ( p == q ) { - return true; - } - TIXMLASSERT( p ); - TIXMLASSERT( q ); - TIXMLASSERT( nChar >= 0 ); - return wcsncmp( p, q, nChar ) == 0; - } - - inline static bool IsUTF8Continuation( TCHAR p ) { - return ( p & 0x80 ) != 0; - } - - static const TCHAR* ReadBOM( const TCHAR* p, bool* hasBOM ); - // p is the starting location, - // the UTF-8 value of the entity will be placed in value, and length filled in. - static const TCHAR* GetCharacterRef( const TCHAR* p, TCHAR* value, int* length ); - static void ConvertUTF32ToUTF8( unsigned long input, TCHAR* output, int* length ); - - // converts primitive types to strings - static void ToStr( int v, TCHAR* buffer, int bufferSize ); - static void ToStr( unsigned v, TCHAR* buffer, int bufferSize ); - static void ToStr( bool v, TCHAR* buffer, int bufferSize ); - static void ToStr( float v, TCHAR* buffer, int bufferSize ); - static void ToStr( double v, TCHAR* buffer, int bufferSize ); - static void ToStr( int64_t v, TCHAR* buffer, int bufferSize ); - static void ToStr( uint64_t v, TCHAR* buffer, int bufferSize ); - - // converts strings to primitive types - static bool ToInt( const TCHAR* str, int* value ); - static bool ToUnsigned( const TCHAR* str, unsigned* value ); - static bool ToBool( const TCHAR* str, bool* value ); - static bool ToFloat( const TCHAR* str, float* value ); - static bool ToDouble( const TCHAR* str, double* value ); - static bool ToInt64(const TCHAR* str, int64_t* value); - static bool ToUnsigned64(const TCHAR* str, uint64_t* value); - // Changes what is serialized for a boolean value. - // Default to "true" and "false". Shouldn't be changed - // unless you have a special testing or compatibility need. - // Be careful: static, global, & not thread safe. - // Be sure to set static const memory as parameters. - static void SetBoolSerialization(const TCHAR* writeTrue, const TCHAR* writeFalse); - -private: - static const TCHAR* writeBoolTrue; - static const TCHAR* writeBoolFalse; -}; - - -/** XMLNode is a base class for every object that is in the - XML Document Object Model (DOM), except XMLAttributes. - Nodes have siblings, a parent, and children which can - be navigated. A node is always in a XMLDocument. - The type of a XMLNode can be queried, and it can - be cast to its more defined type. - - A XMLDocument allocates memory for all its Nodes. - When the XMLDocument gets deleted, all its Nodes - will also be deleted. - - @verbatim - A Document can contain: Element (container or leaf) - Comment (leaf) - Unknown (leaf) - Declaration( leaf ) - - An Element can contain: Element (container or leaf) - Text (leaf) - Attributes (not on tree) - Comment (leaf) - Unknown (leaf) - - @endverbatim -*/ -class TINYXML2_LIB XMLNode -{ - friend class XMLDocument; - friend class XMLElement; -public: - - /// Get the XMLDocument that owns this XMLNode. - const XMLDocument* GetDocument() const { - TIXMLASSERT( _document ); - return _document; - } - /// Get the XMLDocument that owns this XMLNode. - XMLDocument* GetDocument() { - TIXMLASSERT( _document ); - return _document; - } - - /// Safely cast to an Element, or null. - virtual XMLElement* ToElement() { - return 0; - } - /// Safely cast to Text, or null. - virtual XMLText* ToText() { - return 0; - } - /// Safely cast to a Comment, or null. - virtual XMLComment* ToComment() { - return 0; - } - /// Safely cast to a Document, or null. - virtual XMLDocument* ToDocument() { - return 0; - } - /// Safely cast to a Declaration, or null. - virtual XMLDeclaration* ToDeclaration() { - return 0; - } - /// Safely cast to an Unknown, or null. - virtual XMLUnknown* ToUnknown() { - return 0; - } - - virtual const XMLElement* ToElement() const { - return 0; - } - virtual const XMLText* ToText() const { - return 0; - } - virtual const XMLComment* ToComment() const { - return 0; - } - virtual const XMLDocument* ToDocument() const { - return 0; - } - virtual const XMLDeclaration* ToDeclaration() const { - return 0; - } - virtual const XMLUnknown* ToUnknown() const { - return 0; - } - - /** The meaning of 'value' changes for the specific type. - @verbatim - Document: empty (NULL is returned, not an empty string) - Element: name of the element - Comment: the comment text - Unknown: the tag contents - Text: the text string - @endverbatim - */ - const TCHAR* Value() const; - - /** Set the Value of an XML node. - @sa Value() - */ - void SetValue( const TCHAR* val, bool staticMem=false ); - - /// Gets the line number the node is in, if the document was parsed from a file. - int GetLineNum() const { return _parseLineNum; } - - /// Get the parent of this node on the DOM. - const XMLNode* Parent() const { - return _parent; - } - - XMLNode* Parent() { - return _parent; - } - - /// Returns true if this node has no children. - bool NoChildren() const { - return !_firstChild; - } - - /// Get the first child node, or null if none exists. - const XMLNode* FirstChild() const { - return _firstChild; - } - - XMLNode* FirstChild() { - return _firstChild; - } - - /** Get the first child element, or optionally the first child - element with the specified name. - */ - const XMLElement* FirstChildElement( const TCHAR* name = 0 ) const; - - XMLElement* FirstChildElement( const TCHAR* name = 0 ) { - return const_cast(const_cast(this)->FirstChildElement( name )); - } - - /// Get the last child node, or null if none exists. - const XMLNode* LastChild() const { - return _lastChild; - } - - XMLNode* LastChild() { - return _lastChild; - } - - /** Get the last child element or optionally the last child - element with the specified name. - */ - const XMLElement* LastChildElement( const TCHAR* name = 0 ) const; - - XMLElement* LastChildElement( const TCHAR* name = 0 ) { - return const_cast(const_cast(this)->LastChildElement(name) ); - } - - /// Get the previous (left) sibling node of this node. - const XMLNode* PreviousSibling() const { - return _prev; - } - - XMLNode* PreviousSibling() { - return _prev; - } - - /// Get the previous (left) sibling element of this node, with an optionally supplied name. - const XMLElement* PreviousSiblingElement( const TCHAR* name = 0 ) const ; - - XMLElement* PreviousSiblingElement( const TCHAR* name = 0 ) { - return const_cast(const_cast(this)->PreviousSiblingElement( name ) ); - } - - /// Get the next (right) sibling node of this node. - const XMLNode* NextSibling() const { - return _next; - } - - XMLNode* NextSibling() { - return _next; - } - - /// Get the next (right) sibling element of this node, with an optionally supplied name. - const XMLElement* NextSiblingElement( const TCHAR* name = 0 ) const; - - XMLElement* NextSiblingElement( const TCHAR* name = 0 ) { - return const_cast(const_cast(this)->NextSiblingElement( name ) ); - } - - /** - Add a child node as the last (right) child. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the node does not - belong to the same document. - */ - XMLNode* InsertEndChild( XMLNode* addThis ); - - XMLNode* LinkEndChild( XMLNode* addThis ) { - return InsertEndChild( addThis ); - } - /** - Add a child node as the first (left) child. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the node does not - belong to the same document. - */ - XMLNode* InsertFirstChild( XMLNode* addThis ); - /** - Add a node after the specified child node. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the afterThis node - is not a child of this node, or if the node does not - belong to the same document. - */ - XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); - - /** - Delete all the children of this node. - */ - void DeleteChildren(); - - /** - Delete a child of this node. - */ - void DeleteChild( XMLNode* node ); - - /** - Make a copy of this node, but not its children. - You may pass in a Document pointer that will be - the owner of the new Node. If the 'document' is - null, then the node returned will be allocated - from the current Document. (this->GetDocument()) - - Note: if called on a XMLDocument, this will return null. - */ - virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; - - /** - Make a copy of this node and all its children. - - If the 'target' is null, then the nodes will - be allocated in the current document. If 'target' - is specified, the memory will be allocated is the - specified XMLDocument. - - NOTE: This is probably not the correct tool to - copy a document, since XMLDocuments can have multiple - top level XMLNodes. You probably want to use - XMLDocument::DeepCopy() - */ - XMLNode* DeepClone( XMLDocument* target ) const; - - /** - Test if 2 nodes are the same, but don't test children. - The 2 nodes do not need to be in the same Document. - - Note: if called on a XMLDocument, this will return false. - */ - virtual bool ShallowEqual( const XMLNode* compare ) const = 0; - - /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the - XML tree will be conditionally visited and the host will be called back - via the XMLVisitor interface. - - This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse - the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this - interface versus any other.) - - The interface has been based on ideas from: - - - http://www.saxproject.org/ - - http://c2.com/cgi/wiki?HierarchicalVisitorPattern - - Which are both good references for "visiting". - - An example of using Accept(): - @verbatim - XMLPrinter printer; - tinyxmlDoc.Accept( &printer ); - const char* xmlcstr = printer.CStr(); - @endverbatim - */ - virtual bool Accept( XMLVisitor* visitor ) const = 0; - - /** - Set user data into the XMLNode. TinyXML-2 in - no way processes or interprets user data. - It is initially 0. - */ - void SetUserData(void* userData) { _userData = userData; } - - /** - Get user data set into the XMLNode. TinyXML-2 in - no way processes or interprets user data. - It is initially 0. - */ - void* GetUserData() const { return _userData; } - -protected: - explicit XMLNode( XMLDocument* ); - virtual ~XMLNode(); - - virtual TCHAR* ParseDeep( TCHAR* p, StrPair* parentEndTag, int* curLineNumPtr); - - XMLDocument* _document; - XMLNode* _parent; - mutable StrPair _value; - int _parseLineNum; - - XMLNode* _firstChild; - XMLNode* _lastChild; - - XMLNode* _prev; - XMLNode* _next; - - void* _userData; - -private: - MemPool* _memPool; - void Unlink( XMLNode* child ); - static void DeleteNode( XMLNode* node ); - void InsertChildPreamble( XMLNode* insertThis ) const; - const XMLElement* ToElementWithName( const TCHAR* name ) const; - - XMLNode( const XMLNode& ); // not supported - XMLNode& operator=( const XMLNode& ); // not supported -}; - - -/** XML text. - - Note that a text node can have child element nodes, for example: - @verbatim - This is bold - @endverbatim - - A text node can have 2 ways to output the next. "normal" output - and CDATA. It will default to the mode it was parsed from the XML file and - you generally want to leave it alone, but you can change the output mode with - SetCData() and query it with CData(). -*/ -class TINYXML2_LIB XMLText : public XMLNode -{ - friend class XMLDocument; -public: - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLText* ToText() { - return this; - } - virtual const XMLText* ToText() const { - return this; - } - - /// Declare whether this should be CDATA or standard text. - void SetCData( bool isCData ) { - _isCData = isCData; - } - /// Returns true if this is a CDATA text element. - bool CData() const { - return _isCData; - } - - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - explicit XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} - virtual ~XMLText() {} - - TCHAR* ParseDeep( TCHAR* p, StrPair* parentEndTag, int* curLineNumPtr ); - -private: - bool _isCData; - - XMLText( const XMLText& ); // not supported - XMLText& operator=( const XMLText& ); // not supported -}; - - -/** An XML Comment. */ -class TINYXML2_LIB XMLComment : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLComment* ToComment() { - return this; - } - virtual const XMLComment* ToComment() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - explicit XMLComment( XMLDocument* doc ); - virtual ~XMLComment(); - - TCHAR* ParseDeep( TCHAR* p, StrPair* parentEndTag, int* curLineNumPtr); - -private: - XMLComment( const XMLComment& ); // not supported - XMLComment& operator=( const XMLComment& ); // not supported -}; - - -/** In correct XML the declaration is the first entry in the file. - @verbatim - - @endverbatim - - TinyXML-2 will happily read or write files without a declaration, - however. - - The text of the declaration isn't interpreted. It is parsed - and written as a string. -*/ -class TINYXML2_LIB XMLDeclaration : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLDeclaration* ToDeclaration() { - return this; - } - virtual const XMLDeclaration* ToDeclaration() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - explicit XMLDeclaration( XMLDocument* doc ); - virtual ~XMLDeclaration(); - - TCHAR* ParseDeep( TCHAR* p, StrPair* parentEndTag, int* curLineNumPtr ); - -private: - XMLDeclaration( const XMLDeclaration& ); // not supported - XMLDeclaration& operator=( const XMLDeclaration& ); // not supported -}; - - -/** Any tag that TinyXML-2 doesn't recognize is saved as an - unknown. It is a tag of text, but should not be modified. - It will be written back to the XML, unchanged, when the file - is saved. - - DTD tags get thrown into XMLUnknowns. -*/ -class TINYXML2_LIB XMLUnknown : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLUnknown* ToUnknown() { - return this; - } - virtual const XMLUnknown* ToUnknown() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - explicit XMLUnknown( XMLDocument* doc ); - virtual ~XMLUnknown(); - - TCHAR* ParseDeep( TCHAR* p, StrPair* parentEndTag, int* curLineNumPtr ); - -private: - XMLUnknown( const XMLUnknown& ); // not supported - XMLUnknown& operator=( const XMLUnknown& ); // not supported -}; - - - -/** An attribute is a name-value pair. Elements have an arbitrary - number of attributes, each with a unique name. - - @note The attributes are not XMLNodes. You may only query the - Next() attribute in a list. -*/ -class TINYXML2_LIB XMLAttribute -{ - friend class XMLElement; -public: - /// The name of the attribute. - const TCHAR* Name() const; - - /// The value of the attribute. - const TCHAR* Value() const; - - /// Gets the line number the attribute is in, if the document was parsed from a file. - int GetLineNum() const { return _parseLineNum; } - - /// The next attribute in the list. - const XMLAttribute* Next() const { - return _next; - } - - /** IntValue interprets the attribute as an integer, and returns the value. - If the value isn't an integer, 0 will be returned. There is no error checking; - use QueryIntValue() if you need error checking. - */ - int IntValue() const { - int i = 0; - QueryIntValue(&i); - return i; - } - - int64_t Int64Value() const { - int64_t i = 0; - QueryInt64Value(&i); - return i; - } - - uint64_t Unsigned64Value() const { - uint64_t i = 0; - QueryUnsigned64Value(&i); - return i; - } - - /// Query as an unsigned integer. See IntValue() - unsigned UnsignedValue() const { - unsigned i=0; - QueryUnsignedValue( &i ); - return i; - } - /// Query as a boolean. See IntValue() - bool BoolValue() const { - bool b=false; - QueryBoolValue( &b ); - return b; - } - /// Query as a double. See IntValue() - double DoubleValue() const { - double d=0; - QueryDoubleValue( &d ); - return d; - } - /// Query as a float. See IntValue() - float FloatValue() const { - float f=0; - QueryFloatValue( &f ); - return f; - } - - /** QueryIntValue interprets the attribute as an integer, and returns the value - in the provided parameter. The function will return XML_SUCCESS on success, - and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. - */ - XMLError QueryIntValue( int* value ) const; - /// See QueryIntValue - XMLError QueryUnsignedValue( unsigned int* value ) const; - /// See QueryIntValue - XMLError QueryInt64Value( int64_t* value ) const; - /// See QueryIntValue - XMLError QueryUnsigned64Value( uint64_t* value ) const; - /// See QueryIntValue - XMLError QueryBoolValue( bool* value ) const; - /// See QueryIntValue - XMLError QueryDoubleValue( double* value ) const; - /// See QueryIntValue - XMLError QueryFloatValue( float* value ) const; - - /// Set the attribute to a string value. - void SetAttribute( const TCHAR* value ); - /// Set the attribute to value. - void SetAttribute( int value ); - /// Set the attribute to value. - void SetAttribute( unsigned value ); - /// Set the attribute to value. - void SetAttribute( int64_t value ); - /// Set the attribute to value. - void SetAttribute( uint64_t value ); - /// Set the attribute to value. - void SetAttribute( bool value ); - /// Set the attribute to value. - void SetAttribute( double value ); - /// Set the attribute to value. - void SetAttribute( float value ); - -private: - enum { BUF_SIZE = 200 }; - - XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {} - virtual ~XMLAttribute() {} - - XMLAttribute( const XMLAttribute& ); // not supported - void operator=( const XMLAttribute& ); // not supported - void SetName( const TCHAR* name ); - - TCHAR* ParseDeep( TCHAR* p, bool processEntities, int* curLineNumPtr ); - - mutable StrPair _name; - mutable StrPair _value; - int _parseLineNum; - XMLAttribute* _next; - MemPool* _memPool; -}; - - -/** The element is a container class. It has a value, the element name, - and can contain other elements, text, comments, and unknowns. - Elements also contain an arbitrary number of attributes. -*/ -class TINYXML2_LIB XMLElement : public XMLNode -{ - friend class XMLDocument; -public: - /// Get the name of an element (which is the Value() of the node.) - const TCHAR* Name() const { - return Value(); - } - /// Set the name of the element. - void SetName( const TCHAR* str, bool staticMem=false ) { - SetValue( str, staticMem ); - } - - virtual XMLElement* ToElement() { - return this; - } - virtual const XMLElement* ToElement() const { - return this; - } - virtual bool Accept( XMLVisitor* visitor ) const; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none - exists. For example: - - @verbatim - const char* value = ele->Attribute( "foo" ); - @endverbatim - - The 'value' parameter is normally null. However, if specified, - the attribute will only be returned if the 'name' and 'value' - match. This allow you to write code: - - @verbatim - if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); - @endverbatim - - rather than: - @verbatim - if ( ele->Attribute( "foo" ) ) { - if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); - } - @endverbatim - */ - const TCHAR* Attribute( const TCHAR* name, const TCHAR* value=0 ) const; - - /** Given an attribute name, IntAttribute() returns the value - of the attribute interpreted as an integer. The default - value will be returned if the attribute isn't present, - or if there is an error. (For a method with error - checking, see QueryIntAttribute()). - */ - int IntAttribute(const TCHAR* name, int defaultValue = 0) const; - /// See IntAttribute() - unsigned UnsignedAttribute(const TCHAR* name, unsigned defaultValue = 0) const; - /// See IntAttribute() - int64_t Int64Attribute(const TCHAR* name, int64_t defaultValue = 0) const; - /// See IntAttribute() - uint64_t Unsigned64Attribute(const TCHAR* name, uint64_t defaultValue = 0) const; - /// See IntAttribute() - bool BoolAttribute(const TCHAR* name, bool defaultValue = false) const; - /// See IntAttribute() - double DoubleAttribute(const TCHAR* name, double defaultValue = 0) const; - /// See IntAttribute() - float FloatAttribute(const TCHAR* name, float defaultValue = 0) const; - - /** Given an attribute name, QueryIntAttribute() returns - XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - XMLError QueryIntAttribute( const TCHAR* name, int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryIntValue( value ); - } - - /// See QueryIntAttribute() - XMLError QueryUnsignedAttribute( const TCHAR* name, unsigned int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryUnsignedValue( value ); - } - - /// See QueryIntAttribute() - XMLError QueryInt64Attribute(const TCHAR* name, int64_t* value) const { - const XMLAttribute* a = FindAttribute(name); - if (!a) { - return XML_NO_ATTRIBUTE; - } - return a->QueryInt64Value(value); - } - - /// See QueryIntAttribute() - XMLError QueryUnsigned64Attribute(const TCHAR* name, uint64_t* value) const { - const XMLAttribute* a = FindAttribute(name); - if(!a) { - return XML_NO_ATTRIBUTE; - } - return a->QueryUnsigned64Value(value); - } - - /// See QueryIntAttribute() - XMLError QueryBoolAttribute( const TCHAR* name, bool* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryBoolValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryDoubleAttribute( const TCHAR* name, double* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryDoubleValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryFloatAttribute( const TCHAR* name, float* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryFloatValue( value ); - } - - /// See QueryIntAttribute() - XMLError QueryStringAttribute(const TCHAR* name, const TCHAR** value) const { - const XMLAttribute* a = FindAttribute(name); - if (!a) { - return XML_NO_ATTRIBUTE; - } - *value = a->Value(); - return XML_SUCCESS; - } - - - - /** Given an attribute name, QueryAttribute() returns - XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. It is overloaded for the primitive types, - and is a generally more convenient replacement of - QueryIntAttribute() and related functions. - - If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - XMLError QueryAttribute( const TCHAR* name, int* value ) const { - return QueryIntAttribute( name, value ); - } - - XMLError QueryAttribute( const TCHAR* name, unsigned int* value ) const { - return QueryUnsignedAttribute( name, value ); - } - - XMLError QueryAttribute(const TCHAR* name, int64_t* value) const { - return QueryInt64Attribute(name, value); - } - - XMLError QueryAttribute(const TCHAR* name, uint64_t* value) const { - return QueryUnsigned64Attribute(name, value); - } - - XMLError QueryAttribute( const TCHAR* name, bool* value ) const { - return QueryBoolAttribute( name, value ); - } - - XMLError QueryAttribute( const TCHAR* name, double* value ) const { - return QueryDoubleAttribute( name, value ); - } - - XMLError QueryAttribute( const TCHAR* name, float* value ) const { - return QueryFloatAttribute( name, value ); - } - - /// Sets the named attribute to value. - void SetAttribute( const TCHAR* name, const TCHAR* value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const TCHAR* name, int value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const TCHAR* name, unsigned value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - - /// Sets the named attribute to value. - void SetAttribute( const TCHAR* name, int64_t value) { - XMLAttribute* a = FindOrCreateAttribute(name); - a->SetAttribute(value); - } - - /// Sets the named attribute to value. - void SetAttribute( const TCHAR* name, uint64_t value) { - XMLAttribute* a = FindOrCreateAttribute(name); - a->SetAttribute(value); - } - - /// Sets the named attribute to value. - void SetAttribute( const TCHAR* name, bool value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const TCHAR* name, double value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const TCHAR* name, float value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - - /** - Delete an attribute. - */ - void DeleteAttribute( const TCHAR* name ); - - /// Return the first attribute in the list. - const XMLAttribute* FirstAttribute() const { - return _rootAttribute; - } - /// Query a specific attribute in the list. - const XMLAttribute* FindAttribute( const TCHAR* name ) const; - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, GetText() is limited compared to getting the XMLText child - and accessing it directly. - - If the first child of 'this' is a XMLText, the GetText() - returns the character string of the Text node, else null is returned. - - This is a convenient method for getting the text of simple contained text: - @verbatim - This is text - const char* str = fooElement->GetText(); - @endverbatim - - 'str' will be a pointer to "This is text". - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - This is text - @endverbatim - - then the value of str would be null. The first child node isn't a text node, it is - another element. From this XML: - @verbatim - This is text - @endverbatim - GetText() will return "This is ". - */ - const TCHAR* GetText() const; - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, SetText() is limited compared to creating an XMLText child - and mutating it directly. - - If the first child of 'this' is a XMLText, SetText() sets its value to - the given string, otherwise it will create a first child that is an XMLText. - - This is a convenient method for setting the text of simple contained text: - @verbatim - This is text - fooElement->SetText( "Hullaballoo!" ); - Hullaballoo! - @endverbatim - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - This is text - @endverbatim - - then it will not change "This is text", but rather prefix it with a text element: - @verbatim - Hullaballoo!This is text - @endverbatim - - For this XML: - @verbatim - - @endverbatim - SetText() will generate - @verbatim - Hullaballoo! - @endverbatim - */ - void SetText( const TCHAR* inText ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( int value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( unsigned value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( int64_t value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( uint64_t value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( bool value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( double value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( float value ); - - /** - Convenience method to query the value of a child text node. This is probably best - shown by example. Given you have a document is this form: - @verbatim - - 1 - 1.4 - - @endverbatim - - The QueryIntText() and similar functions provide a safe and easier way to get to the - "value" of x and y. - - @verbatim - int x = 0; - float y = 0; // types of x and y are contrived for example - const XMLElement* xElement = pointElement->FirstChildElement( "x" ); - const XMLElement* yElement = pointElement->FirstChildElement( "y" ); - xElement->QueryIntText( &x ); - yElement->QueryFloatText( &y ); - @endverbatim - - @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted - to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. - - */ - XMLError QueryIntText( int* ival ) const; - /// See QueryIntText() - XMLError QueryUnsignedText( unsigned* uval ) const; - /// See QueryIntText() - XMLError QueryInt64Text( int64_t* uval ) const; - /// See QueryIntText() - XMLError QueryUnsigned64Text( uint64_t* uval ) const; - /// See QueryIntText() - XMLError QueryBoolText( bool* bval ) const; - /// See QueryIntText() - XMLError QueryDoubleText( double* dval ) const; - /// See QueryIntText() - XMLError QueryFloatText( float* fval ) const; - - int IntText(int defaultValue = 0) const; - - /// See QueryIntText() - unsigned UnsignedText(unsigned defaultValue = 0) const; - /// See QueryIntText() - int64_t Int64Text(int64_t defaultValue = 0) const; - /// See QueryIntText() - uint64_t Unsigned64Text(uint64_t defaultValue = 0) const; - /// See QueryIntText() - bool BoolText(bool defaultValue = false) const; - /// See QueryIntText() - double DoubleText(double defaultValue = 0) const; - /// See QueryIntText() - float FloatText(float defaultValue = 0) const; - - // internal: - enum ElementClosingType { - OPEN, // - CLOSED, // - CLOSING // - }; - ElementClosingType ClosingType() const { - return _closingType; - } - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - TCHAR* ParseDeep( TCHAR* p, StrPair* parentEndTag, int* curLineNumPtr ); - -private: - XMLElement( XMLDocument* doc ); - virtual ~XMLElement(); - XMLElement( const XMLElement& ); // not supported - void operator=( const XMLElement& ); // not supported - - XMLAttribute* FindOrCreateAttribute( const TCHAR* name ); - TCHAR* ParseAttributes( TCHAR* p, int* curLineNumPtr ); - static void DeleteAttribute( XMLAttribute* attribute ); - XMLAttribute* CreateAttribute(); - - enum { BUF_SIZE = 200 }; - ElementClosingType _closingType; - // The attribute list is ordered; there is no 'lastAttribute' - // because the list needs to be scanned for dupes before adding - // a new attribute. - XMLAttribute* _rootAttribute; -}; - - -enum Whitespace { - PRESERVE_WHITESPACE, - COLLAPSE_WHITESPACE -}; - - -/** A Document binds together all the functionality. - It can be saved, loaded, and printed to the screen. - All Nodes are connected and allocated to a Document. - If the Document is deleted, all its Nodes are also deleted. -*/ -class TINYXML2_LIB XMLDocument : public XMLNode -{ - friend class XMLElement; - // Gives access to SetError and Push/PopDepth, but over-access for everything else. - // Wishing C++ had "internal" scope. - friend class XMLNode; - friend class XMLText; - friend class XMLComment; - friend class XMLDeclaration; - friend class XMLUnknown; -public: - /// constructor - XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE ); - ~XMLDocument(); - - virtual XMLDocument* ToDocument() { - TIXMLASSERT( this == _document ); - return this; - } - virtual const XMLDocument* ToDocument() const { - TIXMLASSERT( this == _document ); - return this; - } - - /** - Parse an XML file from a character string. - Returns XML_SUCCESS (0) on success, or - an errorID. - - You may optionally pass in the 'nBytes', which is - the number of bytes which will be parsed. If not - specified, TinyXML-2 will assume 'xml' points to a - null terminated string. - */ - XMLError Parse( const TCHAR* xml, size_t nBytes=static_cast(-1) ); - - /** - Load an XML file from disk. - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError LoadFile( const TCHAR* filename ); - - /** - Load an XML file from disk. You are responsible - for providing and closing the FILE*. - - NOTE: The file should be opened as binary ("rb") - not text in order for TinyXML-2 to correctly - do newline normalization. - - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError LoadFile( FILE* ); - - /** - Save the XML file to disk. - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError SaveFile( const TCHAR* filename, bool compact = false ); - - /** - Save the XML file to disk. You are responsible - for providing and closing the FILE*. - - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError SaveFile( FILE* fp, bool compact = false ); - - bool ProcessEntities() const { - return _processEntities; - } - Whitespace WhitespaceMode() const { - return _whitespaceMode; - } - - /** - Returns true if this document has a leading Byte Order Mark of UTF8. - */ - bool HasBOM() const { - return _writeBOM; - } - /** Sets whether to write the BOM when writing the file. - */ - void SetBOM( bool useBOM ) { - _writeBOM = useBOM; - } - - /** Return the root element of DOM. Equivalent to FirstChildElement(). - To get the first node, use FirstChild(). - */ - XMLElement* RootElement() { - return FirstChildElement(); - } - const XMLElement* RootElement() const { - return FirstChildElement(); - } - - /** Print the Document. If the Printer is not provided, it will - print to stdout. If you provide Printer, this can print to a file: - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Or you can use a printer to print to memory: - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - // printer.CStr() has a const char* to the XML - @endverbatim - */ - void Print( XMLPrinter* streamer=0 ) const; - virtual bool Accept( XMLVisitor* visitor ) const; - - /** - Create a new Element associated with - this Document. The memory for the Element - is managed by the Document. - */ - XMLElement* NewElement( const TCHAR* name ); - /** - Create a new Comment associated with - this Document. The memory for the Comment - is managed by the Document. - */ - XMLComment* NewComment( const TCHAR* comment ); - /** - Create a new Text associated with - this Document. The memory for the Text - is managed by the Document. - */ - XMLText* NewText( const TCHAR* text ); - /** - Create a new Declaration associated with - this Document. The memory for the object - is managed by the Document. - - If the 'text' param is null, the standard - declaration is used.: - @verbatim - - @endverbatim - */ - XMLDeclaration* NewDeclaration( const TCHAR* text=0 ); - /** - Create a new Unknown associated with - this Document. The memory for the object - is managed by the Document. - */ - XMLUnknown* NewUnknown( const TCHAR* text ); - - /** - Delete a node associated with this document. - It will be unlinked from the DOM. - */ - void DeleteNode( XMLNode* node ); - - void ClearError() { - SetError(XML_SUCCESS, 0, 0); - } - - /// Return true if there was an error parsing the document. - bool Error() const { - return _errorID != XML_SUCCESS; - } - /// Return the errorID. - XMLError ErrorID() const { - return _errorID; - } - const TCHAR* ErrorName() const; - static const TCHAR* ErrorIDToName(XMLError errorID); - - /** Returns a "long form" error description. A hopefully helpful - diagnostic with location, line number, and/or additional info. - */ - const TCHAR* ErrorStr() const; - - /// A (trivial) utility function that prints the ErrorStr() to stdout. - void PrintError() const; - - /// Return the line where the error occurred, or zero if unknown. - int ErrorLineNum() const - { - return _errorLineNum; - } - - /// Clear the document, resetting it to the initial state. - void Clear(); - - /** - Copies this document to a target document. - The target will be completely cleared before the copy. - If you want to copy a sub-tree, see XMLNode::DeepClone(). - - NOTE: that the 'target' must be non-null. - */ - void DeepCopy(XMLDocument* target) const; - - // internal - TCHAR* Identify( TCHAR* p, XMLNode** node ); - - // internal - void MarkInUse(XMLNode*); - - virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { - return 0; - } - virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { - return false; - } - -private: - XMLDocument( const XMLDocument& ); // not supported - void operator=( const XMLDocument& ); // not supported - - bool _writeBOM; - bool _processEntities; - XMLError _errorID; - Whitespace _whitespaceMode; - mutable StrPair _errorStr; - int _errorLineNum; - TCHAR* _charBuffer; - int _parseCurLineNum; - int _parsingDepth; - // Memory tracking does add some overhead. - // However, the code assumes that you don't - // have a bunch of unlinked nodes around. - // Therefore it takes less memory to track - // in the document vs. a linked list in the XMLNode, - // and the performance is the same. - DynArray _unlinked; - - MemPoolT< sizeof(XMLElement) > _elementPool; - MemPoolT< sizeof(XMLAttribute) > _attributePool; - MemPoolT< sizeof(XMLText) > _textPool; - MemPoolT< sizeof(XMLComment) > _commentPool; - - static const TCHAR* _errorNames[XML_ERROR_COUNT]; - - void Parse(); - - void SetError( XMLError error, int lineNum, const TCHAR* format, ... ); - - // Something of an obvious security hole, once it was discovered. - // Either an ill-formed XML or an excessively deep one can overflow - // the stack. Track stack depth, and error out if needed. - class DepthTracker { - public: - explicit DepthTracker(XMLDocument * document) { - this->_document = document; - document->PushDepth(); - } - ~DepthTracker() { - _document->PopDepth(); - } - private: - XMLDocument * _document; - }; - void PushDepth(); - void PopDepth(); - - template - NodeType* CreateUnlinkedNode( MemPoolT& pool ); -}; - -template -inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT& pool ) -{ - TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); - TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() ); - NodeType* returnNode = new (pool.Alloc()) NodeType( this ); - TIXMLASSERT( returnNode ); - returnNode->_memPool = &pool; - - _unlinked.Push(returnNode); - return returnNode; -} - -/** - A XMLHandle is a class that wraps a node pointer with null checks; this is - an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 - DOM structure. It is a separate utility class. - - Take an example: - @verbatim - - - - - - - @endverbatim - - Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very - easy to write a *lot* of code that looks like: - - @verbatim - XMLElement* root = document.FirstChildElement( "Document" ); - if ( root ) - { - XMLElement* element = root->FirstChildElement( "Element" ); - if ( element ) - { - XMLElement* child = element->FirstChildElement( "Child" ); - if ( child ) - { - XMLElement* child2 = child->NextSiblingElement( "Child" ); - if ( child2 ) - { - // Finally do something useful. - @endverbatim - - And that doesn't even cover "else" cases. XMLHandle addresses the verbosity - of such code. A XMLHandle checks for null pointers so it is perfectly safe - and correct to use: - - @verbatim - XMLHandle docHandle( &document ); - XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); - if ( child2 ) - { - // do something useful - @endverbatim - - Which is MUCH more concise and useful. - - It is also safe to copy handles - internally they are nothing more than node pointers. - @verbatim - XMLHandle handleCopy = handle; - @endverbatim - - See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. -*/ -class TINYXML2_LIB XMLHandle -{ -public: - /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. - explicit XMLHandle( XMLNode* node ) : _node( node ) { - } - /// Create a handle from a node. - explicit XMLHandle( XMLNode& node ) : _node( &node ) { - } - /// Copy constructor - XMLHandle( const XMLHandle& ref ) : _node( ref._node ) { - } - /// Assignment - XMLHandle& operator=( const XMLHandle& ref ) { - _node = ref._node; - return *this; - } - - /// Get the first child of this handle. - XMLHandle FirstChild() { - return XMLHandle( _node ? _node->FirstChild() : 0 ); - } - /// Get the first child element of this handle. - XMLHandle FirstChildElement( const TCHAR* name = 0 ) { - return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); - } - /// Get the last child of this handle. - XMLHandle LastChild() { - return XMLHandle( _node ? _node->LastChild() : 0 ); - } - /// Get the last child element of this handle. - XMLHandle LastChildElement( const TCHAR* name = 0 ) { - return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); - } - /// Get the previous sibling of this handle. - XMLHandle PreviousSibling() { - return XMLHandle( _node ? _node->PreviousSibling() : 0 ); - } - /// Get the previous sibling element of this handle. - XMLHandle PreviousSiblingElement( const TCHAR* name = 0 ) { - return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); - } - /// Get the next sibling of this handle. - XMLHandle NextSibling() { - return XMLHandle( _node ? _node->NextSibling() : 0 ); - } - /// Get the next sibling element of this handle. - XMLHandle NextSiblingElement( const TCHAR* name = 0 ) { - return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); - } - - /// Safe cast to XMLNode. This can return null. - XMLNode* ToNode() { - return _node; - } - /// Safe cast to XMLElement. This can return null. - XMLElement* ToElement() { - return ( _node ? _node->ToElement() : 0 ); - } - /// Safe cast to XMLText. This can return null. - XMLText* ToText() { - return ( _node ? _node->ToText() : 0 ); - } - /// Safe cast to XMLUnknown. This can return null. - XMLUnknown* ToUnknown() { - return ( _node ? _node->ToUnknown() : 0 ); - } - /// Safe cast to XMLDeclaration. This can return null. - XMLDeclaration* ToDeclaration() { - return ( _node ? _node->ToDeclaration() : 0 ); - } - -private: - XMLNode* _node; -}; - - -/** - A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the - same in all regards, except for the 'const' qualifiers. See XMLHandle for API. -*/ -class TINYXML2_LIB XMLConstHandle -{ -public: - explicit XMLConstHandle( const XMLNode* node ) : _node( node ) { - } - explicit XMLConstHandle( const XMLNode& node ) : _node( &node ) { - } - XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) { - } - - XMLConstHandle& operator=( const XMLConstHandle& ref ) { - _node = ref._node; - return *this; - } - - const XMLConstHandle FirstChild() const { - return XMLConstHandle( _node ? _node->FirstChild() : 0 ); - } - const XMLConstHandle FirstChildElement( const TCHAR* name = 0 ) const { - return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); - } - const XMLConstHandle LastChild() const { - return XMLConstHandle( _node ? _node->LastChild() : 0 ); - } - const XMLConstHandle LastChildElement( const TCHAR* name = 0 ) const { - return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); - } - const XMLConstHandle PreviousSibling() const { - return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); - } - const XMLConstHandle PreviousSiblingElement( const TCHAR* name = 0 ) const { - return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); - } - const XMLConstHandle NextSibling() const { - return XMLConstHandle( _node ? _node->NextSibling() : 0 ); - } - const XMLConstHandle NextSiblingElement( const TCHAR* name = 0 ) const { - return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); - } - - - const XMLNode* ToNode() const { - return _node; - } - const XMLElement* ToElement() const { - return ( _node ? _node->ToElement() : 0 ); - } - const XMLText* ToText() const { - return ( _node ? _node->ToText() : 0 ); - } - const XMLUnknown* ToUnknown() const { - return ( _node ? _node->ToUnknown() : 0 ); - } - const XMLDeclaration* ToDeclaration() const { - return ( _node ? _node->ToDeclaration() : 0 ); - } - -private: - const XMLNode* _node; -}; - - -/** - Printing functionality. The XMLPrinter gives you more - options than the XMLDocument::Print() method. - - It can: - -# Print to memory. - -# Print to a file you provide. - -# Print XML without a XMLDocument. - - Print to Memory - - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - SomeFunction( printer.CStr() ); - @endverbatim - - Print to a File - - You provide the file pointer. - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Print without a XMLDocument - - When loading, an XML parser is very useful. However, sometimes - when saving, it just gets in the way. The code is often set up - for streaming, and constructing the DOM is just overhead. - - The Printer supports the streaming case. The following code - prints out a trivially simple XML file without ever creating - an XML document. - - @verbatim - XMLPrinter printer( fp ); - printer.OpenElement( "foo" ); - printer.PushAttribute( "foo", "bar" ); - printer.CloseElement(); - @endverbatim -*/ -class TINYXML2_LIB XMLPrinter : public XMLVisitor -{ -public: - /** Construct the printer. If the FILE* is specified, - this will print to the FILE. Else it will print - to memory, and the result is available in CStr(). - If 'compact' is set to true, then output is created - with only required whitespace and newlines. - */ - XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); - virtual ~XMLPrinter() {} - - /** If streaming, write the BOM and declaration. */ - void PushHeader( bool writeBOM, bool writeDeclaration ); - /** If streaming, start writing an element. - The element must be closed with CloseElement() - */ - void OpenElement( const TCHAR* name, bool compactMode=false ); - /// If streaming, add an attribute to an open element. - void PushAttribute( const TCHAR* name, const TCHAR* value ); - void PushAttribute( const TCHAR* name, int value ); - void PushAttribute( const TCHAR* name, unsigned value ); - void PushAttribute( const TCHAR* name, int64_t value ); - void PushAttribute( const TCHAR* name, uint64_t value ); - void PushAttribute( const TCHAR* name, bool value ); - void PushAttribute( const TCHAR* name, double value ); - /// If streaming, close the Element. - virtual void CloseElement( bool compactMode=false ); - - /// Add a text node. - void PushText( const TCHAR* text, bool cdata=false ); - /// Add a text node from an integer. - void PushText( int value ); - /// Add a text node from an unsigned. - void PushText( unsigned value ); - /// Add a text node from a signed 64bit integer. - void PushText( int64_t value ); - /// Add a text node from an unsigned 64bit integer. - void PushText( uint64_t value ); - /// Add a text node from a bool. - void PushText( bool value ); - /// Add a text node from a float. - void PushText( float value ); - /// Add a text node from a double. - void PushText( double value ); - - /// Add a comment - void PushComment( const TCHAR* comment ); - - void PushDeclaration( const TCHAR* value ); - void PushUnknown( const TCHAR* value ); - - virtual bool VisitEnter( const XMLDocument& /*doc*/ ); - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { - return true; - } - - virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); - virtual bool VisitExit( const XMLElement& element ); - - virtual bool Visit( const XMLText& text ); - virtual bool Visit( const XMLComment& comment ); - virtual bool Visit( const XMLDeclaration& declaration ); - virtual bool Visit( const XMLUnknown& unknown ); - - /** - If in print to memory mode, return a pointer to - the XML file in memory. - */ - const TCHAR* CStr() const { - return _buffer.Mem(); - } - /** - If in print to memory mode, return the size - of the XML file in memory. (Note the size returned - includes the terminating null.) - */ - int CStrSize() const { - return _buffer.Size(); - } - /** - If in print to memory mode, reset the buffer to the - beginning. - */ - void ClearBuffer( bool resetToFirstElement = true ) { - _buffer.Clear(); - _buffer.Push(0); - _firstElement = resetToFirstElement; - } - -protected: - virtual bool CompactMode( const XMLElement& ) { return _compactMode; } - - /** Prints out the space before an element. You may override to change - the space and tabs used. A PrintSpace() override should call Print(). - */ - virtual void PrintSpace( int depth ); - void Print( const TCHAR* format, ... ); - void Write( const TCHAR* data, size_t size ); - void Write( const TCHAR* data ); - void Putc( TCHAR ch ); - - void SealElementIfJustOpened(); - bool _elementJustOpened; - DynArray< const TCHAR*, 10 > _stack; - -private: - void PrintString( const TCHAR*, bool restrictedEntitySet ); // prints out, after detecting entities. - - bool _firstElement; - FILE* _fp; - int _depth; - int _textDepth; - bool _processEntities; - bool _compactMode; - - enum { - ENTITY_RANGE = 64, - BUF_SIZE = 200 - }; - bool _entityFlag[ENTITY_RANGE]; - bool _restrictedEntityFlag[ENTITY_RANGE]; - - DynArray< TCHAR, 20 > _buffer; - - // Prohibit cloning, intentionally not implemented - XMLPrinter( const XMLPrinter& ); - XMLPrinter& operator=( const XMLPrinter& ); -}; - - -} // tinyxml2 - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif - -#endif // TINYXML2_INCLUDED diff --git a/src/kiwano-audio/AudioEngine.cpp b/src/kiwano-audio/AudioEngine.cpp index d060e31f..8e238f66 100644 --- a/src/kiwano-audio/AudioEngine.cpp +++ b/src/kiwano-audio/AudioEngine.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -18,108 +18,106 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include // win32::ThrowIfFailed -#include -#include #include +#include +#include +#include // win32::ThrowIfFailed namespace kiwano { - namespace audio - { - AudioEngine::AudioEngine() - : x_audio2_(nullptr) - , mastering_voice_(nullptr) - { - } - - AudioEngine::~AudioEngine() - { - } - - void AudioEngine::SetupComponent() - { - KGE_SYS_LOG(L"Creating audio resources"); - - HRESULT hr = dlls::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL); - - if (SUCCEEDED(hr)) - { - hr = dlls::XAudio2::Get().XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR); - } - - if (SUCCEEDED(hr)) - { - hr = x_audio2_->CreateMasteringVoice(&mastering_voice_); - } - - win32::ThrowIfFailed(hr); - } - - void AudioEngine::DestroyComponent() - { - KGE_SYS_LOG(L"Destroying audio resources"); - - if (mastering_voice_) - { - mastering_voice_->DestroyVoice(); - mastering_voice_ = nullptr; - } - - if (x_audio2_) - { - x_audio2_->Release(); - x_audio2_ = nullptr; - } - - dlls::MediaFoundation::Get().MFShutdown(); - } - - bool AudioEngine::CreateSound(Sound& sound, const Transcoder::Buffer& buffer) - { - KGE_ASSERT(x_audio2_ && "AudioEngine hasn't been initialized!"); - - HRESULT hr = S_OK; - - if (buffer.format == nullptr) - hr = E_INVALIDARG; - - if (SUCCEEDED(hr)) - { - IXAudio2SourceVoice* voice = nullptr; - hr = x_audio2_->CreateSourceVoice(&voice, buffer.format, 0, XAUDIO2_DEFAULT_FREQ_RATIO); - - if (SUCCEEDED(hr)) - { - IXAudio2SourceVoice* old = sound.GetXAudio2Voice(); - if (old) - { - old->DestroyVoice(); - old = nullptr; - } - - sound.SetXAudio2Voice(voice); - } - } - - win32::WarnIfFailed(hr); - return SUCCEEDED(hr); - } - - void AudioEngine::Open() - { - KGE_ASSERT(x_audio2_ && "AudioEngine hasn't been initialized!"); - - if (x_audio2_) - x_audio2_->StartEngine(); - } - - void AudioEngine::Close() - { - KGE_ASSERT(x_audio2_ && "AudioEngine hasn't been initialized!"); - - if (x_audio2_) - x_audio2_->StopEngine(); - } - } +namespace audio +{ +AudioEngine::AudioEngine() + : x_audio2_(nullptr) + , mastering_voice_(nullptr) +{ } + +AudioEngine::~AudioEngine() {} + +void AudioEngine::SetupComponent() +{ + KGE_SYS_LOG(L"Creating audio resources"); + + HRESULT hr = dlls::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL); + + if (SUCCEEDED(hr)) + { + hr = dlls::XAudio2::Get().XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR); + } + + if (SUCCEEDED(hr)) + { + hr = x_audio2_->CreateMasteringVoice(&mastering_voice_); + } + + win32::ThrowIfFailed(hr); +} + +void AudioEngine::DestroyComponent() +{ + KGE_SYS_LOG(L"Destroying audio resources"); + + if (mastering_voice_) + { + mastering_voice_->DestroyVoice(); + mastering_voice_ = nullptr; + } + + if (x_audio2_) + { + x_audio2_->Release(); + x_audio2_ = nullptr; + } + + dlls::MediaFoundation::Get().MFShutdown(); +} + +bool AudioEngine::CreateSound(Sound& sound, const Transcoder::Buffer& buffer) +{ + KGE_ASSERT(x_audio2_ && "AudioEngine hasn't been initialized!"); + + HRESULT hr = S_OK; + + if (buffer.format == nullptr) + hr = E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + IXAudio2SourceVoice* voice = nullptr; + hr = x_audio2_->CreateSourceVoice(&voice, buffer.format, 0, XAUDIO2_DEFAULT_FREQ_RATIO); + + if (SUCCEEDED(hr)) + { + IXAudio2SourceVoice* old = sound.GetXAudio2Voice(); + if (old) + { + old->DestroyVoice(); + old = nullptr; + } + + sound.SetXAudio2Voice(voice); + } + } + + win32::WarnIfFailed(hr); + return SUCCEEDED(hr); +} + +void AudioEngine::Open() +{ + KGE_ASSERT(x_audio2_ && "AudioEngine hasn't been initialized!"); + + if (x_audio2_) + x_audio2_->StartEngine(); +} + +void AudioEngine::Close() +{ + KGE_ASSERT(x_audio2_ && "AudioEngine hasn't been initialized!"); + + if (x_audio2_) + x_audio2_->StopEngine(); +} +} // namespace audio +} // namespace kiwano diff --git a/src/kiwano-audio/AudioEngine.h b/src/kiwano-audio/AudioEngine.h index 4321ac3a..fd877360 100644 --- a/src/kiwano-audio/AudioEngine.h +++ b/src/kiwano-audio/AudioEngine.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -19,64 +19,66 @@ // THE SOFTWARE. #pragma once -#include -#include -#include #include +#include +#include +#include #include namespace kiwano { - namespace audio - { - /** - * \~chinese - * \defgroup Audio Ƶ - */ +namespace audio +{ - /** - * \addtogroup Audio - * @{ - */ +/** + * \~chinese + * \defgroup Audio Ƶ + */ - /** - * \~chinese - * @brief Ƶ - */ - class KGE_API AudioEngine - : public Singleton - , public ComponentBase - { - friend Singleton; +/** + * \addtogroup Audio + * @{ + */ - public: - /// \~chinese - /// @brief Ƶ豸 - void Open(); +/** + * \~chinese + * @brief Ƶ + */ +class KGE_API AudioEngine + : public Singleton + , public ComponentBase +{ + friend Singleton; - /// \~chinese - /// @brief رƵ豸 - void Close(); +public: + /// \~chinese + /// @brief Ƶ豸 + void Open(); - /// \~chinese - /// @brief ӽݻдƵ - bool CreateSound(Sound& sound, const Transcoder::Buffer& buffer); + /// \~chinese + /// @brief رƵ豸 + void Close(); - public: - void SetupComponent() override; + /// \~chinese + /// @brief ӽݻдƵ + bool CreateSound(Sound& sound, const Transcoder::Buffer& buffer); - void DestroyComponent() override; +public: + void SetupComponent() override; - private: - AudioEngine(); + void DestroyComponent() override; - ~AudioEngine(); +private: + AudioEngine(); - private: - IXAudio2* x_audio2_; - IXAudio2MasteringVoice* mastering_voice_; - }; + ~AudioEngine(); - /** @} */ - } -} +private: + IXAudio2* x_audio2_; + IXAudio2MasteringVoice* mastering_voice_; +}; + +/** @} */ + +} // namespace audio +} // namespace kiwano diff --git a/src/kiwano-audio/Sound.cpp b/src/kiwano-audio/Sound.cpp index be9bcdc2..39158d6b 100644 --- a/src/kiwano-audio/Sound.cpp +++ b/src/kiwano-audio/Sound.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -18,210 +18,210 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include #include -#include -#include namespace kiwano { - namespace audio - { +namespace audio +{ - Sound::Sound() - : opened_(false) - , playing_(false) - , voice_(nullptr) - { - } - - Sound::~Sound() - { - Close(); - } - - bool Sound::Load(String const& file_path) - { - if (!FileSystem::instance().IsFileExists(file_path)) - { - KGE_WARN(L"Media file '%s' not found", file_path.c_str()); - return false; - } - - if (opened_) - { - Close(); - } - - String full_path = FileSystem::instance().GetFullPathForFile(file_path); - - HRESULT hr = transcoder_.LoadMediaFile(full_path); - if (FAILED(hr)) - { - KGE_ERROR(L"Load media file failed with HRESULT of %08X", hr); - return false; - } - - if (!AudioEngine::instance().CreateSound(*this, transcoder_.GetBuffer())) - { - Close(); - return false; - } - - opened_ = true; - return true; - } - - bool Sound::Load(Resource const& res) - { - if (opened_) - { - Close(); - } - - HRESULT hr = transcoder_.LoadMediaResource(res); - if (FAILED(hr)) - { - KGE_ERROR(L"Load media resource failed with HRESULT of %08X", hr); - return false; - } - - if (!AudioEngine::instance().CreateSound(*this, transcoder_.GetBuffer())) - { - Close(); - return false; - } - - opened_ = true; - return true; - } - - bool Sound::IsValid() const - { - return voice_ != nullptr; - } - - void Sound::Play(int loop_count) - { - if (!opened_) - { - KGE_ERROR(L"Sound must be opened first!"); - return; - } - - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - // if sound stream is not empty, stop() will clear it - XAUDIO2_VOICE_STATE state; - voice_->GetState(&state); - if (state.BuffersQueued) - Stop(); - - // clamp loop count - loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1); - - auto wave_buffer = transcoder_.GetBuffer(); - - XAUDIO2_BUFFER buffer = { 0 }; - buffer.pAudioData = wave_buffer.data; - buffer.Flags = XAUDIO2_END_OF_STREAM; - buffer.AudioBytes = wave_buffer.size; - buffer.LoopCount = static_cast(loop_count); - - HRESULT hr = voice_->SubmitSourceBuffer(&buffer); - if (SUCCEEDED(hr)) - { - hr = voice_->Start(); - } - - if (FAILED(hr)) - { - KGE_ERROR(L"Submitting source buffer failed with HRESULT of %08X", hr); - } - - playing_ = SUCCEEDED(hr); - } - - void Sound::Pause() - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - if (SUCCEEDED(voice_->Stop())) - playing_ = false; - } - - void Sound::Resume() - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - if (SUCCEEDED(voice_->Start())) - playing_ = true; - } - - void Sound::Stop() - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - HRESULT hr = voice_->Stop(); - - if (SUCCEEDED(hr)) - hr = voice_->ExitLoop(); - - if (SUCCEEDED(hr)) - hr = voice_->FlushSourceBuffers(); - - if (SUCCEEDED(hr)) - playing_ = false; - } - - void Sound::Close() - { - if (voice_) - { - voice_->Stop(); - voice_->FlushSourceBuffers(); - voice_->DestroyVoice(); - voice_ = nullptr; - } - - transcoder_.ClearBuffer(); - - opened_ = false; - playing_ = false; - } - - bool Sound::IsPlaying() const - { - if (opened_) - { - if (!voice_) - return false; - - XAUDIO2_VOICE_STATE state; - voice_->GetState(&state); - uint32_t buffers_queued = state.BuffersQueued; - - if (buffers_queued && playing_) - return true; - } - return false; - } - - float Sound::GetVolume() const - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - float volume = 0.0f; - voice_->GetVolume(&volume); - return volume; - } - - void Sound::SetVolume(float volume) - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - volume = std::min(std::max(volume, -224.f), 224.f); - voice_->SetVolume(volume); - } - } +Sound::Sound() + : opened_(false) + , playing_(false) + , voice_(nullptr) +{ } + +Sound::~Sound() +{ + Close(); +} + +bool Sound::Load(String const& file_path) +{ + if (!FileSystem::Instance().IsFileExists(file_path)) + { + KGE_WARN(L"Media file '%s' not found", file_path.c_str()); + return false; + } + + if (opened_) + { + Close(); + } + + String full_path = FileSystem::Instance().GetFullPathForFile(file_path); + + HRESULT hr = transcoder_.LoadMediaFile(full_path); + if (FAILED(hr)) + { + KGE_ERROR(L"Load media file failed with HRESULT of %08X", hr); + return false; + } + + if (!AudioEngine::Instance().CreateSound(*this, transcoder_.GetBuffer())) + { + Close(); + return false; + } + + opened_ = true; + return true; +} + +bool Sound::Load(Resource const& res) +{ + if (opened_) + { + Close(); + } + + HRESULT hr = transcoder_.LoadMediaResource(res); + if (FAILED(hr)) + { + KGE_ERROR(L"Load media resource failed with HRESULT of %08X", hr); + return false; + } + + if (!AudioEngine::Instance().CreateSound(*this, transcoder_.GetBuffer())) + { + Close(); + return false; + } + + opened_ = true; + return true; +} + +bool Sound::IsValid() const +{ + return voice_ != nullptr; +} + +void Sound::Play(int loop_count) +{ + if (!opened_) + { + KGE_ERROR(L"Sound must be opened first!"); + return; + } + + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + // if sound stream is not empty, stop() will clear it + XAUDIO2_VOICE_STATE state; + voice_->GetState(&state); + if (state.BuffersQueued) + Stop(); + + // clamp loop count + loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1); + + auto wave_buffer = transcoder_.GetBuffer(); + + XAUDIO2_BUFFER buffer = { 0 }; + buffer.pAudioData = wave_buffer.data; + buffer.Flags = XAUDIO2_END_OF_STREAM; + buffer.AudioBytes = wave_buffer.size; + buffer.LoopCount = static_cast(loop_count); + + HRESULT hr = voice_->SubmitSourceBuffer(&buffer); + if (SUCCEEDED(hr)) + { + hr = voice_->Start(); + } + + if (FAILED(hr)) + { + KGE_ERROR(L"Submitting source buffer failed with HRESULT of %08X", hr); + } + + playing_ = SUCCEEDED(hr); +} + +void Sound::Pause() +{ + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + if (SUCCEEDED(voice_->Stop())) + playing_ = false; +} + +void Sound::Resume() +{ + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + if (SUCCEEDED(voice_->Start())) + playing_ = true; +} + +void Sound::Stop() +{ + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + HRESULT hr = voice_->Stop(); + + if (SUCCEEDED(hr)) + hr = voice_->ExitLoop(); + + if (SUCCEEDED(hr)) + hr = voice_->FlushSourceBuffers(); + + if (SUCCEEDED(hr)) + playing_ = false; +} + +void Sound::Close() +{ + if (voice_) + { + voice_->Stop(); + voice_->FlushSourceBuffers(); + voice_->DestroyVoice(); + voice_ = nullptr; + } + + transcoder_.ClearBuffer(); + + opened_ = false; + playing_ = false; +} + +bool Sound::IsPlaying() const +{ + if (opened_) + { + if (!voice_) + return false; + + XAUDIO2_VOICE_STATE state; + voice_->GetState(&state); + uint32_t buffers_queued = state.BuffersQueued; + + if (buffers_queued && playing_) + return true; + } + return false; +} + +float Sound::GetVolume() const +{ + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + float volume = 0.0f; + voice_->GetVolume(&volume); + return volume; +} + +void Sound::SetVolume(float volume) +{ + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + volume = std::min(std::max(volume, -224.f), 224.f); + voice_->SetVolume(volume); +} +} // namespace audio +} // namespace kiwano diff --git a/src/kiwano-audio/Sound.h b/src/kiwano-audio/Sound.h index c1a1a1ac..14987b15 100644 --- a/src/kiwano-audio/Sound.h +++ b/src/kiwano-audio/Sound.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -19,110 +19,108 @@ // THE SOFTWARE. #pragma once +#include #include #include #include -#include #include namespace kiwano { - namespace audio - { - class AudioEngine; +namespace audio +{ +class AudioEngine; - KGE_DECLARE_SMART_PTR(Sound); +KGE_DECLARE_SMART_PTR(Sound); - /** - * \addtogroup Audio - * @{ - */ +/** + * \addtogroup Audio + * @{ + */ - /** - * \~chinese - * @brief Ƶ - */ - class KGE_API Sound - : public ObjectBase - { - friend class AudioEngine; +/** + * \~chinese + * @brief Ƶ + */ +class KGE_API Sound : public virtual ObjectBase +{ + friend class AudioEngine; - public: - Sound(); +public: + Sound(); - virtual ~Sound(); + virtual ~Sound(); - /// \~chinese - /// @brief 򿪱Ƶļ - /// @param res Ƶļ· - bool Load(String const& file_path); + /// \~chinese + /// @brief 򿪱Ƶļ + /// @param res Ƶļ· + bool Load(String const& file_path); - /// \~chinese - /// @brief ƵԴ - /// @param res ƵԴ - bool Load(Resource const& res); + /// \~chinese + /// @brief ƵԴ + /// @param res ƵԴ + bool Load(Resource const& res); - /// \~chinese - /// @brief ǷЧ - bool IsValid() const; + /// \~chinese + /// @brief ǷЧ + bool IsValid() const; - /// \~chinese - /// @brief - /// @param loop_count ѭ -1 Ϊѭ - void Play(int loop_count = 0); + /// \~chinese + /// @brief + /// @param loop_count ѭ -1 Ϊѭ + void Play(int loop_count = 0); - /// \~chinese - /// @brief ͣ - void Pause(); + /// \~chinese + /// @brief ͣ + void Pause(); - /// \~chinese - /// @brief - void Resume(); + /// \~chinese + /// @brief + void Resume(); - /// \~chinese - /// @brief ֹͣ - void Stop(); + /// \~chinese + /// @brief ֹͣ + void Stop(); - /// \~chinese - /// @brief رղԴ - void Close(); + /// \~chinese + /// @brief رղԴ + void Close(); - /// \~chinese - /// @brief Ƿڲ - bool IsPlaying() const; + /// \~chinese + /// @brief Ƿڲ + bool IsPlaying() const; - /// \~chinese - /// @brief ȡ - float GetVolume() const; + /// \~chinese + /// @brief ȡ + float GetVolume() const; - /// \~chinese - /// @brief - /// @param volume С1.0 Ϊԭʼ, 1 ΪŴ, 0 ΪС - void SetVolume(float volume); + /// \~chinese + /// @brief + /// @param volume С1.0 Ϊԭʼ, 1 ΪŴ, 0 ΪС + void SetVolume(float volume); - private: - IXAudio2SourceVoice* GetXAudio2Voice() const; +private: + IXAudio2SourceVoice* GetXAudio2Voice() const; - void SetXAudio2Voice(IXAudio2SourceVoice* voice); + void SetXAudio2Voice(IXAudio2SourceVoice* voice); - private: - bool opened_; - bool playing_; - Transcoder transcoder_; - IXAudio2SourceVoice* voice_; - }; +private: + bool opened_; + bool playing_; + Transcoder transcoder_; + IXAudio2SourceVoice* voice_; +}; - /** @} */ +/** @} */ - - inline IXAudio2SourceVoice* Sound::GetXAudio2Voice() const - { - return voice_; - } - - inline void Sound::SetXAudio2Voice(IXAudio2SourceVoice* voice) - { - voice_ = voice; - } - } +inline IXAudio2SourceVoice* Sound::GetXAudio2Voice() const +{ + return voice_; } + +inline void Sound::SetXAudio2Voice(IXAudio2SourceVoice* voice) +{ + voice_ = voice; +} +} // namespace audio +} // namespace kiwano diff --git a/src/kiwano-audio/SoundPlayer.cpp b/src/kiwano-audio/SoundPlayer.cpp index 5146b885..442a3e2a 100644 --- a/src/kiwano-audio/SoundPlayer.cpp +++ b/src/kiwano-audio/SoundPlayer.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -22,135 +22,135 @@ namespace kiwano { - namespace audio - { - SoundPlayer::SoundPlayer() - : volume_(1.f) - { - } - - SoundPlayer::~SoundPlayer() - { - ClearCache(); - } - - size_t SoundPlayer::Load(String const& file_path) - { - int hash_code = static_cast(file_path.hash()); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - return hash_code; - - SoundPtr sound = new (std::nothrow) Sound; - - if (sound) - { - if (sound->Load(file_path)) - { - sound->SetVolume(volume_); - sound_cache_.insert(std::make_pair(hash_code, sound)); - return hash_code; - } - } - return 0; - } - - size_t SoundPlayer::Load(Resource const& res) - { - size_t hash_code = static_cast(res.GetId()); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - return hash_code; - - SoundPtr sound = new (std::nothrow) Sound; - - if (sound) - { - if (sound->Load(res)) - { - sound->SetVolume(volume_); - sound_cache_.insert(std::make_pair(hash_code, sound)); - return hash_code; - } - } - return 0; - } - - void SoundPlayer::Play(size_t id, int loop_count) - { - auto iter = sound_cache_.find(id); - if (sound_cache_.end() != iter) - iter->second->Play(loop_count); - } - - void SoundPlayer::Pause(size_t id) - { - auto iter = sound_cache_.find(id); - if (sound_cache_.end() != iter) - iter->second->Pause(); - } - - void SoundPlayer::Resume(size_t id) - { - auto iter = sound_cache_.find(id); - if (sound_cache_.end() != iter) - iter->second->Resume(); - } - - void SoundPlayer::Stop(size_t id) - { - auto iter = sound_cache_.find(id); - if (sound_cache_.end() != iter) - iter->second->Stop(); - } - - bool SoundPlayer::IsPlaying(size_t id) - { - auto iter = sound_cache_.find(id); - if (sound_cache_.end() != iter) - return iter->second->IsPlaying(); - return false; - } - - float SoundPlayer::GetVolume() const - { - return volume_; - } - - void SoundPlayer::SetVolume(float volume) - { - volume_ = std::min(std::max(volume, -224.f), 224.f); - for (auto& pair : sound_cache_) - { - pair.second->SetVolume(volume_); - } - } - - void SoundPlayer::PauseAll() - { - for (auto& pair : sound_cache_) - { - pair.second->Pause(); - } - } - - void SoundPlayer::ResumeAll() - { - for (auto& pair : sound_cache_) - { - pair.second->Resume(); - } - } - - void SoundPlayer::StopAll() - { - for (auto& pair : sound_cache_) - { - pair.second->Stop(); - } - } - - void SoundPlayer::ClearCache() - { - sound_cache_.clear(); - } - } +namespace audio +{ +SoundPlayer::SoundPlayer() + : volume_(1.f) +{ } + +SoundPlayer::~SoundPlayer() +{ + ClearCache(); +} + +size_t SoundPlayer::Load(String const& file_path) +{ + int hash_code = static_cast(file_path.hash()); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + return hash_code; + + SoundPtr sound = new (std::nothrow) Sound; + + if (sound) + { + if (sound->Load(file_path)) + { + sound->SetVolume(volume_); + sound_cache_.insert(std::make_pair(hash_code, sound)); + return hash_code; + } + } + return 0; +} + +size_t SoundPlayer::Load(Resource const& res) +{ + size_t hash_code = static_cast(res.GetId()); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + return hash_code; + + SoundPtr sound = new (std::nothrow) Sound; + + if (sound) + { + if (sound->Load(res)) + { + sound->SetVolume(volume_); + sound_cache_.insert(std::make_pair(hash_code, sound)); + return hash_code; + } + } + return 0; +} + +void SoundPlayer::Play(size_t id, int loop_count) +{ + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + iter->second->Play(loop_count); +} + +void SoundPlayer::Pause(size_t id) +{ + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + iter->second->Pause(); +} + +void SoundPlayer::Resume(size_t id) +{ + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + iter->second->Resume(); +} + +void SoundPlayer::Stop(size_t id) +{ + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + iter->second->Stop(); +} + +bool SoundPlayer::IsPlaying(size_t id) +{ + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + return iter->second->IsPlaying(); + return false; +} + +float SoundPlayer::GetVolume() const +{ + return volume_; +} + +void SoundPlayer::SetVolume(float volume) +{ + volume_ = std::min(std::max(volume, -224.f), 224.f); + for (auto& pair : sound_cache_) + { + pair.second->SetVolume(volume_); + } +} + +void SoundPlayer::PauseAll() +{ + for (auto& pair : sound_cache_) + { + pair.second->Pause(); + } +} + +void SoundPlayer::ResumeAll() +{ + for (auto& pair : sound_cache_) + { + pair.second->Resume(); + } +} + +void SoundPlayer::StopAll() +{ + for (auto& pair : sound_cache_) + { + pair.second->Stop(); + } +} + +void SoundPlayer::ClearCache() +{ + sound_cache_.clear(); +} +} // namespace audio +} // namespace kiwano diff --git a/src/kiwano-audio/SoundPlayer.h b/src/kiwano-audio/SoundPlayer.h index f3bd7809..df200860 100644 --- a/src/kiwano-audio/SoundPlayer.h +++ b/src/kiwano-audio/SoundPlayer.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -19,102 +19,101 @@ // THE SOFTWARE. #pragma once -#include #include +#include namespace kiwano { - namespace audio - { - KGE_DECLARE_SMART_PTR(SoundPlayer); +namespace audio +{ +KGE_DECLARE_SMART_PTR(SoundPlayer); - /** - * \addtogroup Audio - * @{ - */ +/** + * \addtogroup Audio + * @{ + */ - /** - * \~chinese - * @brief Ƶ - */ - class KGE_API SoundPlayer - : public ObjectBase - { - public: - SoundPlayer(); +/** + * \~chinese + * @brief Ƶ + */ +class KGE_API SoundPlayer : public virtual ObjectBase +{ +public: + SoundPlayer(); - ~SoundPlayer(); + ~SoundPlayer(); - /// \~chinese - /// @brief رƵļ - /// @param file_path Ƶļ· - /// @return Ƶʶ - size_t Load(String const& file_path); + /// \~chinese + /// @brief رƵļ + /// @param file_path Ƶļ· + /// @return Ƶʶ + size_t Load(String const& file_path); - /// \~chinese - /// @brief ƵԴ - /// @param res ƵԴ - /// @return Ƶʶ - size_t Load(Resource const& res); + /// \~chinese + /// @brief ƵԴ + /// @param res ƵԴ + /// @return Ƶʶ + size_t Load(Resource const& res); - /// \~chinese - /// @brief Ƶ - /// @param id Ƶʶ - /// @param loop_count ѭ -1 Ϊѭ - void Play(size_t id, int loop_count = 0); + /// \~chinese + /// @brief Ƶ + /// @param id Ƶʶ + /// @param loop_count ѭ -1 Ϊѭ + void Play(size_t id, int loop_count = 0); - /// \~chinese - /// @brief ͣƵ - /// @param id Ƶʶ - void Pause(size_t id); + /// \~chinese + /// @brief ͣƵ + /// @param id Ƶʶ + void Pause(size_t id); - /// \~chinese - /// @brief Ƶ - /// @param id Ƶʶ - void Resume(size_t id); + /// \~chinese + /// @brief Ƶ + /// @param id Ƶʶ + void Resume(size_t id); - /// \~chinese - /// @brief ֹͣƵ - /// @param id Ƶʶ - void Stop(size_t id); + /// \~chinese + /// @brief ֹͣƵ + /// @param id Ƶʶ + void Stop(size_t id); - /// \~chinese - /// @brief ȡƵ״̬ - /// @param id Ƶʶ - bool IsPlaying(size_t id); + /// \~chinese + /// @brief ȡƵ״̬ + /// @param id Ƶʶ + bool IsPlaying(size_t id); - /// \~chinese - /// @brief ȡ - float GetVolume() const; + /// \~chinese + /// @brief ȡ + float GetVolume() const; - /// \~chinese - /// @brief - /// @param volume С1.0 Ϊԭʼ, 1 ΪŴ, 0 ΪС - void SetVolume(float volume); + /// \~chinese + /// @brief + /// @param volume С1.0 Ϊԭʼ, 1 ΪŴ, 0 ΪС + void SetVolume(float volume); - /// \~chinese - /// @brief ͣƵ - void PauseAll(); + /// \~chinese + /// @brief ͣƵ + void PauseAll(); - /// \~chinese - /// @brief Ƶ - void ResumeAll(); + /// \~chinese + /// @brief Ƶ + void ResumeAll(); - /// \~chinese - /// @brief ֹͣƵ - void StopAll(); + /// \~chinese + /// @brief ֹͣƵ + void StopAll(); - /// \~chinese - /// @brief - void ClearCache(); + /// \~chinese + /// @brief + void ClearCache(); - private: - float volume_; +private: + float volume_; - using SoundMap = Map; - SoundMap sound_cache_; - }; + using SoundMap = Map; + SoundMap sound_cache_; +}; - /** @} */ - } -} +/** @} */ +} // namespace audio +} // namespace kiwano diff --git a/src/kiwano-audio/Transcoder.cpp b/src/kiwano-audio/Transcoder.cpp index 08ff08e6..bef635c4 100644 --- a/src/kiwano-audio/Transcoder.cpp +++ b/src/kiwano-audio/Transcoder.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -19,280 +19,252 @@ // THE SOFTWARE. #ifndef INITGUID -# define INITGUID // MFAudioFormat_PCM, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MFMediaType_Audio +#define INITGUID // MFAudioFormat_PCM, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MFMediaType_Audio #endif -#include -#include -#include +#include +#include +#include #include +#include +#include #include #include -#include -#include namespace kiwano { - namespace audio - { +namespace audio +{ - Transcoder::Transcoder() - : wave_format_(nullptr) - , wave_data_(nullptr) - , wave_size_(0) - { - } - - Transcoder::~Transcoder() - { - ClearBuffer(); - } - - Transcoder::Buffer Transcoder::GetBuffer() const - { - return Buffer{ wave_data_, wave_size_, wave_format_ }; - } - - void Transcoder::ClearBuffer() - { - if (wave_format_) - { - ::CoTaskMemFree(wave_format_); - wave_format_ = nullptr; - } - - if (wave_data_) - { - delete[] wave_data_; - wave_data_ = nullptr; - } - - wave_size_ = 0; - } - - HRESULT Transcoder::LoadMediaFile(String const& file_path) - { - HRESULT hr = S_OK; - - ComPtr reader; - - hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromURL( - file_path.c_str(), - nullptr, - &reader - ); - - if (SUCCEEDED(hr)) - { - hr = ReadSource(reader.get()); - } - - return hr; - } - - HRESULT Transcoder::LoadMediaResource(Resource const& res) - { - HRESULT hr = S_OK; - - ComPtr stream; - ComPtr byte_stream; - ComPtr reader; - - Resource::Data data = res.GetData(); - if (!data) { return E_FAIL; } - - stream = win32::dlls::Shlwapi::Get().SHCreateMemStream( - static_cast(data.buffer), - static_cast(data.size) - ); - - if (stream == nullptr) - { - KGE_ERROR(L"SHCreateMemStream failed"); - return E_OUTOFMEMORY; - } - - if (SUCCEEDED(hr)) - { - hr = dlls::MediaFoundation::Get().MFCreateMFByteStreamOnStream(stream.get(), &byte_stream); - } - - if (SUCCEEDED(hr)) - { - hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromByteStream( - byte_stream.get(), - nullptr, - &reader - ); - } - - if (SUCCEEDED(hr)) - { - hr = ReadSource(reader.get()); - } - - return hr; - } - - HRESULT Transcoder::ReadSource(IMFSourceReader* reader) - { - HRESULT hr = S_OK; - DWORD max_stream_size = 0; - - ComPtr partial_type; - ComPtr uncompressed_type; - - hr = dlls::MediaFoundation::Get().MFCreateMediaType(&partial_type); - - if (SUCCEEDED(hr)) - { - hr = partial_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); - } - - if (SUCCEEDED(hr)) - { - hr = partial_type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); - } - - // source reader ýͣʹúʵĽȥƵ - if (SUCCEEDED(hr)) - { - hr = reader->SetCurrentMediaType( - (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, - 0, - partial_type.get() - ); - } - - // IMFMediaType лȡ WAVEFORMAT ṹ - if (SUCCEEDED(hr)) - { - hr = reader->GetCurrentMediaType( - (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, - &uncompressed_type - ); - } - - // ָƵ - if (SUCCEEDED(hr)) - { - hr = reader->SetStreamSelection( - (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, - true - ); - } - - // ȡ WAVEFORMAT - if (SUCCEEDED(hr)) - { - uint32_t size = 0; - hr = dlls::MediaFoundation::Get().MFCreateWaveFormatExFromMFMediaType( - uncompressed_type.get(), - &wave_format_, - &size, - (DWORD)MFWaveFormatExConvertFlag_Normal - ); - } - - // ƵС - if (SUCCEEDED(hr)) - { - PROPVARIANT prop; - PropVariantInit(&prop); - - hr = reader->GetPresentationAttribute( - (DWORD)MF_SOURCE_READER_MEDIASOURCE, - MF_PD_DURATION, - &prop - ); - - LONGLONG duration = prop.uhVal.QuadPart; - max_stream_size = static_cast( - (duration * wave_format_->nAvgBytesPerSec) / 10000000 + 1 - ); - PropVariantClear(&prop); - } - - // ȡƵ - if (SUCCEEDED(hr)) - { - DWORD flags = 0; - DWORD position = 0; - BYTE* data = new (std::nothrow) BYTE[max_stream_size]; - - ComPtr sample; - ComPtr buffer; - - if (data == nullptr) - { - KGE_ERROR(L"Low memory"); - hr = E_OUTOFMEMORY; - } - else - { - while (true) - { - hr = reader->ReadSample( - (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, - 0, - nullptr, - &flags, - nullptr, - &sample - ); - - if (flags & MF_SOURCE_READERF_ENDOFSTREAM) { break; } - - if (sample == nullptr) { continue; } - - if (SUCCEEDED(hr)) - { - hr = sample->ConvertToContiguousBuffer(&buffer); - - if (SUCCEEDED(hr)) - { - BYTE* audio_data = nullptr; - DWORD sample_buffer_length = 0; - - hr = buffer->Lock( - &audio_data, - nullptr, - &sample_buffer_length - ); - - if (position + sample_buffer_length >= max_stream_size) - { - hr = E_FAIL; - } - - if (SUCCEEDED(hr)) - { - ::memcpy(data + position, audio_data, sample_buffer_length); - position += sample_buffer_length; - hr = buffer->Unlock(); - } - } - buffer = nullptr; - } - sample = nullptr; - - if (FAILED(hr)) { break; } - } - - if (SUCCEEDED(hr)) - { - wave_data_ = data; - wave_size_ = position; - } - else - { - delete[] data; - data = nullptr; - } - } - } - - return hr; - } - } +Transcoder::Transcoder() + : wave_format_(nullptr) + , wave_data_(nullptr) + , wave_size_(0) +{ } + +Transcoder::~Transcoder() +{ + ClearBuffer(); +} + +Transcoder::Buffer Transcoder::GetBuffer() const +{ + return Buffer{ wave_data_, wave_size_, wave_format_ }; +} + +void Transcoder::ClearBuffer() +{ + if (wave_format_) + { + ::CoTaskMemFree(wave_format_); + wave_format_ = nullptr; + } + + if (wave_data_) + { + delete[] wave_data_; + wave_data_ = nullptr; + } + + wave_size_ = 0; +} + +HRESULT Transcoder::LoadMediaFile(String const& file_path) +{ + HRESULT hr = S_OK; + + ComPtr reader; + + hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromURL(file_path.c_str(), nullptr, &reader); + + if (SUCCEEDED(hr)) + { + hr = ReadSource(reader.get()); + } + + return hr; +} + +HRESULT Transcoder::LoadMediaResource(Resource const& res) +{ + HRESULT hr = S_OK; + + ComPtr stream; + ComPtr byte_stream; + ComPtr reader; + + Resource::Data data = res.GetData(); + if (!data) + { + return E_FAIL; + } + + stream = win32::dlls::Shlwapi::Get().SHCreateMemStream(static_cast(data.buffer), + static_cast(data.size)); + + if (stream == nullptr) + { + KGE_ERROR(L"SHCreateMemStream failed"); + return E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr)) + { + hr = dlls::MediaFoundation::Get().MFCreateMFByteStreamOnStream(stream.get(), &byte_stream); + } + + if (SUCCEEDED(hr)) + { + hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromByteStream(byte_stream.get(), nullptr, &reader); + } + + if (SUCCEEDED(hr)) + { + hr = ReadSource(reader.get()); + } + + return hr; +} + +HRESULT Transcoder::ReadSource(IMFSourceReader* reader) +{ + HRESULT hr = S_OK; + DWORD max_stream_size = 0; + + ComPtr partial_type; + ComPtr uncompressed_type; + + hr = dlls::MediaFoundation::Get().MFCreateMediaType(&partial_type); + + if (SUCCEEDED(hr)) + { + hr = partial_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); + } + + if (SUCCEEDED(hr)) + { + hr = partial_type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); + } + + // source reader ýͣʹúʵĽȥƵ + if (SUCCEEDED(hr)) + { + hr = reader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, partial_type.get()); + } + + // IMFMediaType лȡ WAVEFORMAT ṹ + if (SUCCEEDED(hr)) + { + hr = reader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, &uncompressed_type); + } + + // ָƵ + if (SUCCEEDED(hr)) + { + hr = reader->SetStreamSelection((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, true); + } + + // ȡ WAVEFORMAT + if (SUCCEEDED(hr)) + { + uint32_t size = 0; + hr = dlls::MediaFoundation::Get().MFCreateWaveFormatExFromMFMediaType( + uncompressed_type.get(), &wave_format_, &size, (DWORD)MFWaveFormatExConvertFlag_Normal); + } + + // ƵС + if (SUCCEEDED(hr)) + { + PROPVARIANT prop; + PropVariantInit(&prop); + + hr = reader->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &prop); + + LONGLONG duration = prop.uhVal.QuadPart; + max_stream_size = static_cast((duration * wave_format_->nAvgBytesPerSec) / 10000000 + 1); + PropVariantClear(&prop); + } + + // ȡƵ + if (SUCCEEDED(hr)) + { + DWORD flags = 0; + DWORD position = 0; + BYTE* data = new (std::nothrow) BYTE[max_stream_size]; + + ComPtr sample; + ComPtr buffer; + + if (data == nullptr) + { + KGE_ERROR(L"Low memory"); + hr = E_OUTOFMEMORY; + } + else + { + while (true) + { + hr = reader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &flags, nullptr, + &sample); + + if (flags & MF_SOURCE_READERF_ENDOFSTREAM) + { + break; + } + + if (sample == nullptr) + { + continue; + } + + if (SUCCEEDED(hr)) + { + hr = sample->ConvertToContiguousBuffer(&buffer); + + if (SUCCEEDED(hr)) + { + BYTE* audio_data = nullptr; + DWORD sample_buffer_length = 0; + + hr = buffer->Lock(&audio_data, nullptr, &sample_buffer_length); + + if (position + sample_buffer_length >= max_stream_size) + { + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + { + ::memcpy(data + position, audio_data, sample_buffer_length); + position += sample_buffer_length; + hr = buffer->Unlock(); + } + } + buffer = nullptr; + } + sample = nullptr; + + if (FAILED(hr)) + { + break; + } + } + + if (SUCCEEDED(hr)) + { + wave_data_ = data; + wave_size_ = position; + } + else + { + delete[] data; + data = nullptr; + } + } + } + + return hr; +} +} // namespace audio +} // namespace kiwano diff --git a/src/kiwano-audio/Transcoder.h b/src/kiwano-audio/Transcoder.h index 50afd1c0..7b69cdb8 100644 --- a/src/kiwano-audio/Transcoder.h +++ b/src/kiwano-audio/Transcoder.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -26,66 +26,66 @@ namespace kiwano { - namespace audio - { - class Sound; +namespace audio +{ +class Sound; - /** - * \addtogroup Audio - * @{ - */ +/** + * \addtogroup Audio + * @{ + */ - /** - * \~chinese - * @brief Ƶ - */ - class KGE_API Transcoder - { - friend class Sound; +/** + * \~chinese + * @brief Ƶ + */ +class KGE_API Transcoder +{ + friend class Sound; - public: - /** - * \~chinese - * @brief Ƶݻ - */ - struct Buffer - { - BYTE* data; ///< Ƶ - uint32_t size; ///< ƵݴС - const WAVEFORMATEX* format; ///< Ƶݸʽ - }; +public: + /** + * \~chinese + * @brief Ƶݻ + */ + struct Buffer + { + BYTE* data; ///< Ƶ + uint32_t size; ///< ƵݴС + const WAVEFORMATEX* format; ///< Ƶݸʽ + }; - Transcoder(); + Transcoder(); - ~Transcoder(); + ~Transcoder(); - /// \~chinese - /// @brief ȡݻ - Buffer GetBuffer() const; + /// \~chinese + /// @brief ȡݻ + Buffer GetBuffer() const; - /// \~chinese - /// @brief ݻ - void ClearBuffer(); + /// \~chinese + /// @brief ݻ + void ClearBuffer(); - private: - /// \~chinese - /// @brief 뱾Ƶļ - HRESULT LoadMediaFile(String const& file_path); +private: + /// \~chinese + /// @brief 뱾Ƶļ + HRESULT LoadMediaFile(String const& file_path); - /// \~chinese - /// @brief ƵԴ - HRESULT LoadMediaResource(Resource const& res); + /// \~chinese + /// @brief ƵԴ + HRESULT LoadMediaResource(Resource const& res); - /// \~chinese - /// @brief ȡƵԴ - HRESULT ReadSource(IMFSourceReader* reader); + /// \~chinese + /// @brief ȡƵԴ + HRESULT ReadSource(IMFSourceReader* reader); - private: - BYTE* wave_data_; - uint32_t wave_size_; - WAVEFORMATEX* wave_format_; - }; +private: + BYTE* wave_data_; + uint32_t wave_size_; + WAVEFORMATEX* wave_format_; +}; - /** @} */ - } -} +/** @} */ +} // namespace audio +} // namespace kiwano diff --git a/src/kiwano-audio/kiwano-audio.h b/src/kiwano-audio/kiwano-audio.h index 041b7aa7..975d92e1 100644 --- a/src/kiwano-audio/kiwano-audio.h +++ b/src/kiwano-audio/kiwano-audio.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 diff --git a/src/kiwano-audio/libraries.cpp b/src/kiwano-audio/libraries.cpp index 5239d216..2b060608 100644 --- a/src/kiwano-audio/libraries.cpp +++ b/src/kiwano-audio/libraries.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -18,81 +18,84 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include #include +#include namespace kiwano { - namespace audio - { - namespace dlls - { - XAudio2::XAudio2() - : xaudio2() - , XAudio2Create(nullptr) - { - const auto xaudio2_dll_names = - { - "xaudio2_9.dll", // for Windows 10 - "xaudio2_8.dll", // for Windows 8 - "xaudio2_7.dll" // for DirectX SDK - }; +namespace audio +{ +namespace dlls +{ +XAudio2::XAudio2() + : xaudio2() + , XAudio2Create(nullptr) +{ + const auto xaudio2_dll_names = { + "xaudio2_9.dll", // for Windows 10 + "xaudio2_8.dll", // for Windows 8 + "xaudio2_7.dll" // for DirectX SDK + }; - for (const auto& name : xaudio2_dll_names) - { - if (xaudio2.Load(name)) - { - break; - } - } + for (const auto& name : xaudio2_dll_names) + { + if (xaudio2.Load(name)) + { + break; + } + } - if (xaudio2.IsValid()) - { - XAudio2Create = xaudio2.GetProcess("XAudio2Create"); - } - else - { - KGE_ERROR(L"Load xaudio2.dll failed"); - throw std::runtime_error("Load xaudio2.dll failed"); - } - } - - MediaFoundation::MediaFoundation() - : mfplat() - , mfreadwrite() - , MFStartup(nullptr) - , MFShutdown(nullptr) - , MFCreateMediaType(nullptr) - , MFCreateWaveFormatExFromMFMediaType(nullptr) - , MFCreateSourceReaderFromURL(nullptr) - , MFCreateSourceReaderFromByteStream(nullptr) - , MFCreateMFByteStreamOnStream(nullptr) - { - if (mfplat.Load("Mfplat.dll")) - { - MFStartup = mfplat.GetProcess("MFStartup"); - MFShutdown = mfplat.GetProcess("MFShutdown"); - MFCreateMediaType = mfplat.GetProcess("MFCreateMediaType"); - MFCreateWaveFormatExFromMFMediaType = mfplat.GetProcess("MFCreateWaveFormatExFromMFMediaType"); - MFCreateMFByteStreamOnStream = mfplat.GetProcess("MFCreateMFByteStreamOnStream"); - } - else - { - KGE_ERROR(L"Load Mfplat.dll failed"); - throw std::runtime_error("Load Mfplat.dll failed"); - } - - if (mfreadwrite.Load("Mfreadwrite.dll")) - { - MFCreateSourceReaderFromURL = mfreadwrite.GetProcess("MFCreateSourceReaderFromURL"); - MFCreateSourceReaderFromByteStream = mfreadwrite.GetProcess("MFCreateSourceReaderFromByteStream"); - } - else - { - KGE_ERROR(L"Load Mfreadwrite.dll failed"); - throw std::runtime_error("Load Mfreadwrite.dll failed"); - } - } - } - } + if (xaudio2.IsValid()) + { + XAudio2Create = xaudio2.GetProcess("XAudio2Create"); + } + else + { + KGE_ERROR(L"Load xaudio2.dll failed"); + throw std::runtime_error("Load xaudio2.dll failed"); + } } + +MediaFoundation::MediaFoundation() + : mfplat() + , mfreadwrite() + , MFStartup(nullptr) + , MFShutdown(nullptr) + , MFCreateMediaType(nullptr) + , MFCreateWaveFormatExFromMFMediaType(nullptr) + , MFCreateSourceReaderFromURL(nullptr) + , MFCreateSourceReaderFromByteStream(nullptr) + , MFCreateMFByteStreamOnStream(nullptr) +{ + if (mfplat.Load("Mfplat.dll")) + { + MFStartup = mfplat.GetProcess("MFStartup"); + MFShutdown = mfplat.GetProcess("MFShutdown"); + MFCreateMediaType = mfplat.GetProcess("MFCreateMediaType"); + MFCreateWaveFormatExFromMFMediaType = + mfplat.GetProcess("MFCreateWaveFormatExFromMFMediaType"); + MFCreateMFByteStreamOnStream = + mfplat.GetProcess("MFCreateMFByteStreamOnStream"); + } + else + { + KGE_ERROR(L"Load Mfplat.dll failed"); + throw std::runtime_error("Load Mfplat.dll failed"); + } + + if (mfreadwrite.Load("Mfreadwrite.dll")) + { + MFCreateSourceReaderFromURL = + mfreadwrite.GetProcess("MFCreateSourceReaderFromURL"); + MFCreateSourceReaderFromByteStream = + mfreadwrite.GetProcess("MFCreateSourceReaderFromByteStream"); + } + else + { + KGE_ERROR(L"Load Mfreadwrite.dll failed"); + throw std::runtime_error("Load Mfreadwrite.dll failed"); + } +} +} // namespace dlls +} // namespace audio +} // namespace kiwano diff --git a/src/kiwano-audio/libraries.h b/src/kiwano-audio/libraries.h index 44d26bf7..35324db0 100644 --- a/src/kiwano-audio/libraries.h +++ b/src/kiwano-audio/libraries.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -20,80 +20,79 @@ #pragma once #include -#include #include #include #include +#include #ifndef KGE_DOXYGEN_DO_NOT_INCLUDE namespace kiwano { - namespace audio - { - namespace dlls - { - class KGE_API XAudio2 - { - public: - static inline XAudio2& Get() - { - static XAudio2 instance; - return instance; - } +namespace audio +{ +namespace dlls +{ +class KGE_API XAudio2 +{ +public: + static inline XAudio2& Get() + { + static XAudio2 instance; + return instance; + } - // XAudio2 functions - typedef HRESULT(WINAPI* PFN_XAudio2Create)(IXAudio2**, UINT32, XAUDIO2_PROCESSOR); + // XAudio2 functions + typedef HRESULT(WINAPI* PFN_XAudio2Create)(IXAudio2**, UINT32, XAUDIO2_PROCESSOR); - PFN_XAudio2Create XAudio2Create; + PFN_XAudio2Create XAudio2Create; - private: - XAudio2(); +private: + XAudio2(); - XAudio2(const XAudio2&) = delete; - XAudio2& operator=(const XAudio2&) = delete; + XAudio2(const XAudio2&) = delete; + XAudio2& operator=(const XAudio2&) = delete; - Library xaudio2; - }; + Library xaudio2; +}; +class KGE_API MediaFoundation +{ +public: + static inline MediaFoundation& Get() + { + static MediaFoundation instance; + return instance; + } - class KGE_API MediaFoundation - { - public: - static inline MediaFoundation& Get() - { - static MediaFoundation instance; - return instance; - } + // MediaFoundation functions + typedef HRESULT(WINAPI* PFN_MFStartup)(ULONG, DWORD); + typedef HRESULT(WINAPI* PFN_MFShutdown)(); + typedef HRESULT(WINAPI* PFN_MFCreateMediaType)(IMFMediaType**); + typedef HRESULT(WINAPI* PFN_MFCreateWaveFormatExFromMFMediaType)(IMFMediaType*, WAVEFORMATEX**, UINT32*, UINT32); + typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromURL)(LPCWSTR, IMFAttributes*, IMFSourceReader**); + typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromByteStream)(IMFByteStream*, IMFAttributes*, IMFSourceReader**); + typedef HRESULT(WINAPI* PFN_MFCreateMFByteStreamOnStream)(IStream*, IMFByteStream**); - // MediaFoundation functions - typedef HRESULT(WINAPI* PFN_MFStartup)(ULONG, DWORD); - typedef HRESULT(WINAPI* PFN_MFShutdown)(); - typedef HRESULT(WINAPI* PFN_MFCreateMediaType)(IMFMediaType**); - typedef HRESULT(WINAPI* PFN_MFCreateWaveFormatExFromMFMediaType)(IMFMediaType*, WAVEFORMATEX**, UINT32*, UINT32); - typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromURL)(LPCWSTR, IMFAttributes*, IMFSourceReader**); - typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromByteStream)(IMFByteStream*, IMFAttributes*, IMFSourceReader**); - typedef HRESULT(WINAPI* PFN_MFCreateMFByteStreamOnStream)(IStream*, IMFByteStream**); + PFN_MFStartup MFStartup; + PFN_MFShutdown MFShutdown; + PFN_MFCreateMediaType MFCreateMediaType; + PFN_MFCreateWaveFormatExFromMFMediaType MFCreateWaveFormatExFromMFMediaType; + PFN_MFCreateSourceReaderFromURL MFCreateSourceReaderFromURL; + PFN_MFCreateSourceReaderFromByteStream MFCreateSourceReaderFromByteStream; + PFN_MFCreateMFByteStreamOnStream MFCreateMFByteStreamOnStream; - PFN_MFStartup MFStartup; - PFN_MFShutdown MFShutdown; - PFN_MFCreateMediaType MFCreateMediaType; - PFN_MFCreateWaveFormatExFromMFMediaType MFCreateWaveFormatExFromMFMediaType; - PFN_MFCreateSourceReaderFromURL MFCreateSourceReaderFromURL; - PFN_MFCreateSourceReaderFromByteStream MFCreateSourceReaderFromByteStream; - PFN_MFCreateMFByteStreamOnStream MFCreateMFByteStreamOnStream; +private: + MediaFoundation(); - private: - MediaFoundation(); + MediaFoundation(const MediaFoundation&) = delete; + MediaFoundation& operator=(const MediaFoundation&) = delete; - MediaFoundation(const MediaFoundation&) = delete; - MediaFoundation& operator=(const MediaFoundation&) = delete; - - Library mfplat; - Library mfreadwrite; - }; - } - } -} + Library mfplat; + Library mfreadwrite; +}; +} // namespace dlls +} // namespace audio +} // namespace kiwano #endif diff --git a/src/kiwano-imgui/ImGuiLayer.cpp b/src/kiwano-imgui/ImGuiLayer.cpp index 94890048..94054b4d 100644 --- a/src/kiwano-imgui/ImGuiLayer.cpp +++ b/src/kiwano-imgui/ImGuiLayer.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -22,43 +22,45 @@ namespace kiwano { - namespace imgui - { - ImGuiLayer::ImGuiLayer() - { - SetSwallowEvents(true); - } - - ImGuiLayer::~ImGuiLayer() - { - } - - void ImGuiLayer::OnRender(RenderTarget* rt) - { - PrepareToRender(rt); - for (const auto& pipeline : pipelines_) - { - pipeline.second(); - } - } - - void ImGuiLayer::AddItem(ImGuiPipeline const& item, String const& name) - { - pipelines_.insert(std::make_pair(name, item)); - } - - void ImGuiLayer::RemoveItem(String const& name) - { - auto iter = pipelines_.find(name); - if (iter != pipelines_.end()) - { - pipelines_.erase(iter); - } - } - - void ImGuiLayer::RemoveAllItems() - { - pipelines_.clear(); - } - } +namespace imgui +{ +ImGuiLayer::ImGuiLayer() +{ + SetSwallowEvents(true); } + +ImGuiLayer::~ImGuiLayer() {} + +void ImGuiLayer::OnRender(RenderContext& ctx) +{ + for (const auto& pipeline : pipelines_) + { + pipeline.second(); + } +} + +bool ImGuiLayer::CheckVisibility(RenderContext& ctx) const +{ + return true; +} + +void ImGuiLayer::AddItem(String const& name, ImGuiPipeline const& item) +{ + pipelines_.insert(std::make_pair(name, item)); +} + +void ImGuiLayer::RemoveItem(String const& name) +{ + auto iter = pipelines_.find(name); + if (iter != pipelines_.end()) + { + pipelines_.erase(iter); + } +} + +void ImGuiLayer::RemoveAllItems() +{ + pipelines_.clear(); +} +} // namespace imgui +} // namespace kiwano diff --git a/src/kiwano-imgui/ImGuiLayer.h b/src/kiwano-imgui/ImGuiLayer.h index 8f4246b2..a377805e 100644 --- a/src/kiwano-imgui/ImGuiLayer.h +++ b/src/kiwano-imgui/ImGuiLayer.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -23,47 +23,48 @@ namespace kiwano { - namespace imgui - { - KGE_DECLARE_SMART_PTR(ImGuiLayer); +namespace imgui +{ +KGE_DECLARE_SMART_PTR(ImGuiLayer); - /// \~chinese - /// @brief ImGuiܵ - using ImGuiPipeline = Function; +/// \~chinese +/// @brief ImGuiܵ +using ImGuiPipeline = Function; - /** - * \~chinese - * @brief ImGuiͼ - */ - class ImGuiLayer - : public Layer - { - public: - ImGuiLayer(); +/** + * \~chinese + * @brief ImGuiͼ + */ +class ImGuiLayer : public Layer +{ +public: + ImGuiLayer(); - virtual ~ImGuiLayer(); + virtual ~ImGuiLayer(); - /// \~chinese - /// @brief ImGui Ԫ - /// @param item ܵ - /// @param name Ԫ - void AddItem(ImGuiPipeline const& item, String const& name); + /// \~chinese + /// @brief ImGui Ԫ + /// @param name Ԫ + /// @param item ܵ + void AddItem(String const& name, ImGuiPipeline const& item); - /// \~chinese - /// @brief Ƴ ImGui Ԫ - /// @param name Ԫ - void RemoveItem(String const& name); + /// \~chinese + /// @brief Ƴ ImGui Ԫ + /// @param name Ԫ + void RemoveItem(String const& name); - // ƳԪ - /// \~chinese - /// @brief ƳԪ - void RemoveAllItems(); + // ƳԪ + /// \~chinese + /// @brief ƳԪ + void RemoveAllItems(); - public: - void OnRender(RenderTarget* rt) override; +public: + void OnRender(RenderContext& ctx) override; - private: - Map pipelines_; - }; - } -} + bool CheckVisibility(RenderContext& ctx) const override; + +private: + Map pipelines_; +}; +} // namespace imgui +} // namespace kiwano diff --git a/src/kiwano-imgui/ImGuiModule.cpp b/src/kiwano-imgui/ImGuiModule.cpp index d0fa9a0f..2cb8fcdf 100644 --- a/src/kiwano-imgui/ImGuiModule.cpp +++ b/src/kiwano-imgui/ImGuiModule.cpp @@ -1,309 +1,236 @@ // Copyright (C) 2019 Nomango -#include -#include + +#include +#include +#include #include -#include +#include +#include #include #include -#include -#pragma comment(lib, "xinput") - -// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. -#ifndef WM_MOUSEHWHEEL -# define WM_MOUSEHWHEEL 0x020E -#endif - -#ifndef DBT_DEVNODES_CHANGED -# define DBT_DEVNODES_CHANGED 0x0007 -#endif - namespace kiwano { - namespace imgui - { - ImGuiModule::ImGuiModule() - : has_gamepad_(false) - , want_update_has_gamepad_(false) - , target_window_(nullptr) - { - } - - void ImGuiModule::SetupComponent() - { - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - //ImGui::StyleColorsClassic(); - - // Setup Platform/Renderer bindings - Init(Window::instance().GetHandle()); - - target_window_ = Renderer::instance().GetTargetWindow(); - } - - void ImGuiModule::DestroyComponent() - { - ImGui_Impl_Shutdown(); - ImGui::DestroyContext(); - } - - void ImGuiModule::OnUpdate(Duration dt) - { - ImGuiIO& io = ImGui::GetIO(); - - // Setup time step - io.DeltaTime = dt.Seconds(); - - // Read keyboard modifiers inputs - io.KeyCtrl = Input::instance().IsDown(KeyCode::Ctrl); - io.KeyShift = Input::instance().IsDown(KeyCode::Shift); - io.KeyAlt = Input::instance().IsDown(KeyCode::Alt); - io.KeySuper = false; - // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. - - // Update OS mouse position - UpdateMousePos(); - - // Update OS mouse cursor with the cursor requested by imgui - UpdateMouseCursor(); - - // Update game controllers (if enabled and available) - UpdateGamepads(); - } - - void ImGuiModule::Init(HWND hwnd) - { - ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - io.BackendPlatformName = "imgui_impl_win32"; - io.ImeWindowHandle = hwnd; - - // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. - io.KeyMap[ImGuiKey_Tab] = KeyCode::Tab; - io.KeyMap[ImGuiKey_LeftArrow] = KeyCode::Left; - io.KeyMap[ImGuiKey_RightArrow] = KeyCode::Right; - io.KeyMap[ImGuiKey_UpArrow] = KeyCode::Up; - io.KeyMap[ImGuiKey_DownArrow] = KeyCode::Down; - io.KeyMap[ImGuiKey_Delete] = KeyCode::Delete; - io.KeyMap[ImGuiKey_Backspace] = KeyCode::Back; - io.KeyMap[ImGuiKey_Space] = KeyCode::Space; - io.KeyMap[ImGuiKey_Enter] = KeyCode::Enter; - io.KeyMap[ImGuiKey_Escape] = KeyCode::Esc; - io.KeyMap[ImGuiKey_A] = KeyCode::A; - io.KeyMap[ImGuiKey_C] = KeyCode::C; - io.KeyMap[ImGuiKey_V] = KeyCode::V; - io.KeyMap[ImGuiKey_X] = KeyCode::X; - io.KeyMap[ImGuiKey_Y] = KeyCode::Y; - io.KeyMap[ImGuiKey_Z] = KeyCode::Z; - - ImGui_Impl_Init(&Renderer::instance()); - } - - void ImGuiModule::BeforeRender() - { - NewFrame(); - } - - void ImGuiModule::AfterRender() - { - Render(); - } - - void ImGuiModule::HandleMessage(HWND hwnd, UINT32 msg, WPARAM wparam, LPARAM lparam) - { - if (ImGui::GetCurrentContext() == NULL) - return; - - ImGuiIO& io = ImGui::GetIO(); - switch (msg) - { - case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: - case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: - { - int button = 0; - if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } - if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } - if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; } - if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wparam) == XBUTTON1) ? 3 : 4; } - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) - ::SetCapture(hwnd); - - io.MouseDown[button] = true; - break; - } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - case WM_XBUTTONUP: - { - int button = 0; - if (msg == WM_LBUTTONUP) { button = 0; } - if (msg == WM_RBUTTONUP) { button = 1; } - if (msg == WM_MBUTTONUP) { button = 2; } - if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wparam) == XBUTTON1) ? 3 : 4; } - io.MouseDown[button] = false; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) - ::ReleaseCapture(); - break; - } - case WM_MOUSEWHEEL: - { - io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wparam) / (float)WHEEL_DELTA; - break; - } - case WM_MOUSEHWHEEL: - { - io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wparam) / (float)WHEEL_DELTA; - break; - } - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - { - if (wparam < 256) - io.KeysDown[wparam] = 1; - break; - } - case WM_KEYUP: - case WM_SYSKEYUP: - { - if (wparam < 256) - io.KeysDown[wparam] = 0; - break; - } - case WM_CHAR: - { - // You can also use ToAscii()+GetKeyboardState() to retrieve characters. - io.AddInputCharacter((uint32_t)wparam); - break; - } - case WM_SETCURSOR: - { - if (LOWORD(lparam) == HTCLIENT) - { - UpdateMouseCursor(); - } - break; - } - case WM_DEVICECHANGE: - { - if ((uint32_t)wparam == DBT_DEVNODES_CHANGED) - want_update_has_gamepad_ = true; - break; - } - } - } - - void ImGuiModule::NewFrame() - { - ImGui_Impl_NewFrame(); - - ImGuiIO& io = ImGui::GetIO(); - KGE_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built!"); - - // Setup display size (every frame to accommodate for window resizing) - Size display_size = Renderer::instance().GetOutputSize(); - io.DisplaySize = ImVec2(display_size.x, display_size.y); - - ImGui::NewFrame(); - } - - void ImGuiModule::Render() - { - ImGui::Render(); - - ImGui_Impl_RenderDrawData(ImGui::GetDrawData()); - } - - void ImGuiModule::UpdateMousePos() - { - ImGuiIO& io = ImGui::GetIO(); - - // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - ::ClientToScreen(target_window_, &pos); - ::SetCursorPos(pos.x, pos.y); - } - - Point pos = Input::instance().GetMousePos(); - io.MousePos = ImVec2(pos.x, pos.y); - } - - void ImGuiModule::UpdateMouseCursor() - { - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) - return; - - CursorType cursor = CursorType::Arrow; - switch (ImGui::GetMouseCursor()) - { - case ImGuiMouseCursor_Arrow: cursor = CursorType::Arrow; break; - case ImGuiMouseCursor_TextInput: cursor = CursorType::TextInput; break; - case ImGuiMouseCursor_ResizeAll: cursor = CursorType::SizeAll; break; - case ImGuiMouseCursor_ResizeEW: cursor = CursorType::SizeWE; break; - case ImGuiMouseCursor_ResizeNS: cursor = CursorType::SizeNS; break; - case ImGuiMouseCursor_ResizeNESW: cursor = CursorType::SizeNESW; break; - case ImGuiMouseCursor_ResizeNWSE: cursor = CursorType::SizeNWSE; break; - case ImGuiMouseCursor_Hand: cursor = CursorType::Hand; break; - } - - Window::instance().SetCursor(cursor); - } - void ImGuiModule::UpdateGamepads() - { - ImGuiIO& io = ImGui::GetIO(); - memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) - return; - - // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. - // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. - if (want_update_has_gamepad_) - { - XINPUT_CAPABILITIES caps; - has_gamepad_ = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); - want_update_has_gamepad_ = false; - } - - XINPUT_STATE xinput_state; - io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; - if (has_gamepad_ && XInputGetState(0, &xinput_state) == ERROR_SUCCESS) - { - const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; - io.BackendFlags |= ImGuiBackendFlags_HasGamepad; - -#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; } -#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } - MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A - MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B - MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X - MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB - MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB - MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB - MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB - MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); - MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767); -#undef MAP_BUTTON -#undef MAP_ANALOG - } - } - } +namespace imgui +{ +ImGuiModule::ImGuiModule() + : target_window_(nullptr) +{ } + +void ImGuiModule::SetupComponent() +{ + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + (void)io; + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + + // Setup Platform/Renderer bindings + target_window_ = Renderer::Instance().GetTargetWindow(); + + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= + ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendPlatformName = "imgui_impl_win32"; + io.ImeWindowHandle = target_window_; + + // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during + // the application lifetime. + io.KeyMap[ImGuiKey_Tab] = (int)KeyCode::Tab; + io.KeyMap[ImGuiKey_LeftArrow] = (int)KeyCode::Left; + io.KeyMap[ImGuiKey_RightArrow] = (int)KeyCode::Right; + io.KeyMap[ImGuiKey_UpArrow] = (int)KeyCode::Up; + io.KeyMap[ImGuiKey_DownArrow] = (int)KeyCode::Down; + io.KeyMap[ImGuiKey_Delete] = (int)KeyCode::Delete; + io.KeyMap[ImGuiKey_Backspace] = (int)KeyCode::Back; + io.KeyMap[ImGuiKey_Space] = (int)KeyCode::Space; + io.KeyMap[ImGuiKey_Enter] = (int)KeyCode::Enter; + io.KeyMap[ImGuiKey_Escape] = (int)KeyCode::Esc; + io.KeyMap[ImGuiKey_A] = (int)KeyCode::A; + io.KeyMap[ImGuiKey_C] = (int)KeyCode::C; + io.KeyMap[ImGuiKey_V] = (int)KeyCode::V; + io.KeyMap[ImGuiKey_X] = (int)KeyCode::X; + io.KeyMap[ImGuiKey_Y] = (int)KeyCode::Y; + io.KeyMap[ImGuiKey_Z] = (int)KeyCode::Z; + + ImGui_Impl_Init(Renderer::Instance()); +} + +void ImGuiModule::DestroyComponent() +{ + ImGui_Impl_Shutdown(); + ImGui::DestroyContext(); +} + +void ImGuiModule::OnUpdate(Duration dt) +{ + ImGuiIO& io = ImGui::GetIO(); + + // Setup time step + io.DeltaTime = dt.Seconds(); + + // Read keyboard modifiers inputs + io.KeyCtrl = Input::Instance().IsDown(KeyCode::Ctrl); + io.KeyShift = Input::Instance().IsDown(KeyCode::Shift); + io.KeyAlt = Input::Instance().IsDown(KeyCode::Alt); + io.KeySuper = Input::Instance().IsDown(KeyCode::Super); + // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the HandleEvent function below. + + // Update OS mouse position + UpdateMousePos(); + + // Update OS mouse cursor with the cursor requested by imgui + UpdateMouseCursor(); +} + +void ImGuiModule::BeforeRender() +{ + NewFrame(); +} + +void ImGuiModule::AfterRender() +{ + Render(); +} + +void ImGuiModule::HandleEvent(Event* evt) +{ + if (ImGui::GetCurrentContext() == NULL) + return; + + ImGuiIO& io = ImGui::GetIO(); + if (evt->IsType()) + { + if (evt->IsType()) + { + MouseButton button = dynamic_cast(evt)->button; + int index = 0; + if (button == MouseButton::Left) + index = 0; + else if (button == MouseButton::Right) + index = 1; + else if (button == MouseButton::Middle) + index = 2; + io.MouseDown[index] = true; + } + else if (evt->IsType()) + { + MouseButton button = dynamic_cast(evt)->button; + int index = 0; + if (button == MouseButton::Left) + index = 0; + else if (button == MouseButton::Right) + index = 1; + else if (button == MouseButton::Middle) + index = 2; + io.MouseDown[index] = false; + } + else if (evt->IsType()) + { + float wheel = dynamic_cast(evt)->wheel; + io.MouseWheel += wheel; + } + } + else if (evt->IsType()) + { + if (evt->IsType()) + { + KeyCode key = dynamic_cast(evt)->code; + io.KeysDown[(int)key] = true; + } + else if (evt->IsType()) + { + KeyCode key = dynamic_cast(evt)->code; + io.KeysDown[(int)key] = false; + } + else if (evt->IsType()) + { + // You can also use ToAscii()+GetKeyboardState() to retrieve characters. + char ch = dynamic_cast(evt)->value; + io.AddInputCharacter(static_cast(ch)); + } + } +} + +void ImGuiModule::NewFrame() +{ + ImGui_Impl_NewFrame(); + + ImGuiIO& io = ImGui::GetIO(); + KGE_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built!"); + + // Setup display size (every frame to accommodate for window resizing) + Size display_size = Renderer::Instance().GetOutputSize(); + io.DisplaySize = ImVec2(display_size.x, display_size.y); + + ImGui::NewFrame(); +} + +void ImGuiModule::Render() +{ + ImGui::Render(); + + ImGui_Impl_RenderDrawData(ImGui::GetDrawData()); +} + +void ImGuiModule::UpdateMousePos() +{ + ImGuiIO& io = ImGui::GetIO(); + + // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by + // user) + if (io.WantSetMousePos) + { + POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; + ::ClientToScreen(target_window_, &pos); + ::SetCursorPos(pos.x, pos.y); + } + + Point pos = Input::Instance().GetMousePos(); + io.MousePos = ImVec2(pos.x, pos.y); +} + +void ImGuiModule::UpdateMouseCursor() +{ + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) + return; + + CursorType cursor = CursorType::Arrow; + switch (ImGui::GetMouseCursor()) + { + case ImGuiMouseCursor_Arrow: + cursor = CursorType::Arrow; + break; + case ImGuiMouseCursor_TextInput: + cursor = CursorType::TextInput; + break; + case ImGuiMouseCursor_ResizeAll: + cursor = CursorType::SizeAll; + break; + case ImGuiMouseCursor_ResizeEW: + cursor = CursorType::SizeWE; + break; + case ImGuiMouseCursor_ResizeNS: + cursor = CursorType::SizeNS; + break; + case ImGuiMouseCursor_ResizeNESW: + cursor = CursorType::SizeNESW; + break; + case ImGuiMouseCursor_ResizeNWSE: + cursor = CursorType::SizeNWSE; + break; + case ImGuiMouseCursor_Hand: + cursor = CursorType::Hand; + break; + } + + Window::Instance().SetCursor(cursor); +} + +} // namespace imgui +} // namespace kiwano diff --git a/src/kiwano-imgui/ImGuiModule.h b/src/kiwano-imgui/ImGuiModule.h index 50e50115..33a2a2f5 100644 --- a/src/kiwano-imgui/ImGuiModule.h +++ b/src/kiwano-imgui/ImGuiModule.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -19,57 +19,51 @@ // THE SOFTWARE. #pragma once -#include +#include #include namespace kiwano { - namespace imgui - { - /** - * \~chinese - * @brief ImGuiģ - */ - class ImGuiModule - : public Singleton - , public RenderComponent - , public UpdateComponent - , public EventComponent - { - friend Singleton; +namespace imgui +{ +/** + * \~chinese + * @brief ImGuiģ + */ +class ImGuiModule + : public Singleton + , public RenderComponent + , public UpdateComponent + , public EventComponent +{ + friend Singleton; - public: - ImGuiModule(); +public: + ImGuiModule(); - void SetupComponent() override; + void SetupComponent() override; - void DestroyComponent() override; + void DestroyComponent() override; - void BeforeRender() override; + void BeforeRender() override; - void AfterRender() override; + void AfterRender() override; - void HandleMessage(HWND hwnd, UINT32 msg, WPARAM wparam, LPARAM lparam) override; + void HandleEvent(Event* evt) override; - void OnUpdate(Duration dt) override; + void OnUpdate(Duration dt) override; - private: - void Init(HWND hwnd); +private: + void NewFrame(); - void NewFrame(); + void Render(); - void Render(); + void UpdateMousePos(); - void UpdateMousePos(); + void UpdateMouseCursor(); - void UpdateMouseCursor(); - - void UpdateGamepads(); - - private: - bool has_gamepad_; - bool want_update_has_gamepad_; - HWND target_window_; - }; - } -} +private: + WindowHandle target_window_; +}; +} // namespace imgui +} // namespace kiwano diff --git a/src/kiwano-imgui/imgui_impl.h b/src/kiwano-imgui/imgui_impl.h index 372f26fe..0402bcd5 100644 --- a/src/kiwano-imgui/imgui_impl.h +++ b/src/kiwano-imgui/imgui_impl.h @@ -8,25 +8,62 @@ #include -inline bool ImGui_Impl_Init(::kiwano::Renderer* renderer) { return ImGui_ImplDX11_Init(renderer->GetD3DDeviceResources()->GetDevice(), renderer->GetD3DDeviceResources()->GetDeviceContext()); } -inline void ImGui_Impl_Shutdown() { ImGui_ImplDX11_Shutdown(); } -inline void ImGui_Impl_NewFrame() { ImGui_ImplDX11_NewFrame(); } -inline void ImGui_Impl_RenderDrawData(ImDrawData* draw_data) { ImGui_ImplDX11_RenderDrawData(draw_data); } +inline bool ImGui_Impl_Init(::kiwano::Renderer& renderer) +{ + return ImGui_ImplDX11_Init(renderer.GetD3DDeviceResources()->GetDevice(), + renderer.GetD3DDeviceResources()->GetDeviceContext()); +} +inline void ImGui_Impl_Shutdown() +{ + ImGui_ImplDX11_Shutdown(); +} +inline void ImGui_Impl_NewFrame() +{ + ImGui_ImplDX11_NewFrame(); +} +inline void ImGui_Impl_RenderDrawData(ImDrawData* draw_data) +{ + ImGui_ImplDX11_RenderDrawData(draw_data); +} -inline void ImGui_Impl_InvalidateDeviceObjects() { ImGui_ImplDX11_InvalidateDeviceObjects(); } -inline bool ImGui_Impl_CreateDeviceObjects() { return ImGui_ImplDX11_CreateDeviceObjects(); } +inline void ImGui_Impl_InvalidateDeviceObjects() +{ + ImGui_ImplDX11_InvalidateDeviceObjects(); +} +inline bool ImGui_Impl_CreateDeviceObjects() +{ + return ImGui_ImplDX11_CreateDeviceObjects(); +} #else #include -inline bool ImGui_Impl_Init(::kiwano::Renderer* renderer) { return ImGui_ImplDX10_Init(renderer->GetD3DDeviceResources()->GetDevice()); } -inline void ImGui_Impl_Shutdown() { ImGui_ImplDX10_Shutdown(); } -inline void ImGui_Impl_NewFrame() { ImGui_ImplDX10_NewFrame(); } -inline void ImGui_Impl_RenderDrawData(ImDrawData* draw_data) { ImGui_ImplDX10_RenderDrawData(draw_data); } +inline bool ImGui_Impl_Init(::kiwano::Renderer& renderer) +{ + return ImGui_ImplDX10_Init(renderer.GetD3DDeviceResources()->GetDevice()); +} +inline void ImGui_Impl_Shutdown() +{ + ImGui_ImplDX10_Shutdown(); +} +inline void ImGui_Impl_NewFrame() +{ + ImGui_ImplDX10_NewFrame(); +} +inline void ImGui_Impl_RenderDrawData(ImDrawData* draw_data) +{ + ImGui_ImplDX10_RenderDrawData(draw_data); +} -inline void ImGui_Impl_InvalidateDeviceObjects() { ImGui_ImplDX10_InvalidateDeviceObjects(); } -inline bool ImGui_Impl_CreateDeviceObjects() { return ImGui_ImplDX10_CreateDeviceObjects(); } +inline void ImGui_Impl_InvalidateDeviceObjects() +{ + ImGui_ImplDX10_InvalidateDeviceObjects(); +} +inline bool ImGui_Impl_CreateDeviceObjects() +{ + return ImGui_ImplDX10_CreateDeviceObjects(); +} #endif diff --git a/src/kiwano-imgui/imgui_impl_dx10.cpp b/src/kiwano-imgui/imgui_impl_dx10.cpp index 8c0221be..8bd81ed6 100644 --- a/src/kiwano-imgui/imgui_impl_dx10.cpp +++ b/src/kiwano-imgui/imgui_impl_dx10.cpp @@ -3,40 +3,41 @@ #include // DirectX -#include #include #include #include +#include #ifdef _MSC_VER -#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. +#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif // DirectX data -static ID3D10Device* g_pd3dDevice = NULL; -static IDXGIFactory* g_pFactory = NULL; -static ID3D10Buffer* g_pVB = NULL; -static ID3D10Buffer* g_pIB = NULL; -static ID3D10Blob* g_pVertexShaderBlob = NULL; -static ID3D10VertexShader* g_pVertexShader = NULL; -static ID3D10InputLayout* g_pInputLayout = NULL; -static ID3D10Buffer* g_pVertexConstantBuffer = NULL; -static ID3D10Blob* g_pPixelShaderBlob = NULL; -static ID3D10PixelShader* g_pPixelShader = NULL; -static ID3D10SamplerState* g_pFontSampler = NULL; -static ID3D10ShaderResourceView*g_pFontTextureView = NULL; -static ID3D10RasterizerState* g_pRasterizerState = NULL; -static ID3D10BlendState* g_pBlendState = NULL; -static ID3D10DepthStencilState* g_pDepthStencilState = NULL; -static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; +static ID3D10Device* g_pd3dDevice = NULL; +static IDXGIFactory* g_pFactory = NULL; +static ID3D10Buffer* g_pVB = NULL; +static ID3D10Buffer* g_pIB = NULL; +static ID3D10Blob* g_pVertexShaderBlob = NULL; +static ID3D10VertexShader* g_pVertexShader = NULL; +static ID3D10InputLayout* g_pInputLayout = NULL; +static ID3D10Buffer* g_pVertexConstantBuffer = NULL; +static ID3D10Blob* g_pPixelShaderBlob = NULL; +static ID3D10PixelShader* g_pPixelShader = NULL; +static ID3D10SamplerState* g_pFontSampler = NULL; +static ID3D10ShaderResourceView* g_pFontTextureView = NULL; +static ID3D10RasterizerState* g_pRasterizerState = NULL; +static ID3D10BlendState* g_pBlendState = NULL; +static ID3D10DepthStencilState* g_pDepthStencilState = NULL; +static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; struct VERTEX_CONSTANT_BUFFER { - float mvp[4][4]; + float mvp[4][4]; }; // Render Function -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) +// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from +// your main loop) void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) { ID3D10Device* ctx = g_pd3dDevice; @@ -44,28 +45,36 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) // Create and grow vertex/index buffers if needed if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) { - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } + if (g_pVB) + { + g_pVB->Release(); + g_pVB = NULL; + } g_VertexBufferSize = draw_data->TotalVtxCount + 5000; D3D10_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D10_BUFFER_DESC)); - desc.Usage = D3D10_USAGE_DYNAMIC; - desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); - desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; + desc.Usage = D3D10_USAGE_DYNAMIC; + desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); + desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; - desc.MiscFlags = 0; + desc.MiscFlags = 0; if (ctx->CreateBuffer(&desc, NULL, &g_pVB) < 0) return; } if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) { - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } + if (g_pIB) + { + g_pIB->Release(); + g_pIB = NULL; + } g_IndexBufferSize = draw_data->TotalIdxCount + 10000; D3D10_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D10_BUFFER_DESC)); - desc.Usage = D3D10_USAGE_DYNAMIC; - desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); - desc.BindFlags = D3D10_BIND_INDEX_BUFFER; + desc.Usage = D3D10_USAGE_DYNAMIC; + desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); + desc.BindFlags = D3D10_BIND_INDEX_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; if (ctx->CreateBuffer(&desc, NULL, &g_pIB) < 0) return; @@ -73,7 +82,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) // Copy and convert all vertices into a single contiguous buffer ImDrawVert* vtx_dst = NULL; - ImDrawIdx* idx_dst = NULL; + ImDrawIdx* idx_dst = NULL; g_pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst); g_pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst); for (int n = 0; n < draw_data->CmdListsCount; n++) @@ -88,48 +97,49 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) g_pIB->Unmap(); // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). + // Our visible imgui space lies from draw_data->DisplayPos (top left) to + // draw_data->DisplayPos+data_data->DisplaySize (bottom right). { void* mapped_resource; if (g_pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource; - float L = draw_data->DisplayPos.x; - float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; - float T = draw_data->DisplayPos.y; - float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; - float mvp[4][4] = - { - { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.5f, 0.0f }, - { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + float mvp[4][4] = { + { 2.0f / (R - L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f / (T - B), 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.5f, 0.0f }, + { (R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f }, }; memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); g_pVertexConstantBuffer->Unmap(); } - // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) + // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and + // verbose. Close your eyes!) struct BACKUP_DX10_STATE { - UINT ScissorRectsCount, ViewportsCount; - D3D10_RECT ScissorRects[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; - D3D10_VIEWPORT Viewports[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; - ID3D10RasterizerState* RS; - ID3D10BlendState* BlendState; - FLOAT BlendFactor[4]; - UINT SampleMask; - UINT StencilRef; - ID3D10DepthStencilState* DepthStencilState; - ID3D10ShaderResourceView* PSShaderResource; - ID3D10SamplerState* PSSampler; - ID3D10PixelShader* PS; - ID3D10VertexShader* VS; - D3D10_PRIMITIVE_TOPOLOGY PrimitiveTopology; - ID3D10Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer; - UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset; - DXGI_FORMAT IndexBufferFormat; - ID3D10InputLayout* InputLayout; + UINT ScissorRectsCount, ViewportsCount; + D3D10_RECT ScissorRects[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; + D3D10_VIEWPORT Viewports[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; + ID3D10RasterizerState* RS; + ID3D10BlendState* BlendState; + FLOAT BlendFactor[4]; + UINT SampleMask; + UINT StencilRef; + ID3D10DepthStencilState* DepthStencilState; + ID3D10ShaderResourceView* PSShaderResource; + ID3D10SamplerState* PSSampler; + ID3D10PixelShader* PS; + ID3D10VertexShader* VS; + D3D10_PRIMITIVE_TOPOLOGY PrimitiveTopology; + ID3D10Buffer * IndexBuffer, *VertexBuffer, *VSConstantBuffer; + UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset; + DXGI_FORMAT IndexBufferFormat; + ID3D10InputLayout* InputLayout; }; BACKUP_DX10_STATE old; old.ScissorRectsCount = old.ViewportsCount = D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; @@ -151,8 +161,8 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) // Setup viewport D3D10_VIEWPORT vp; memset(&vp, 0, sizeof(D3D10_VIEWPORT)); - vp.Width = (UINT)draw_data->DisplaySize.x; - vp.Height = (UINT)draw_data->DisplaySize.y; + vp.Width = (UINT)draw_data->DisplaySize.x; + vp.Height = (UINT)draw_data->DisplaySize.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0; @@ -177,9 +187,9 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) ctx->RSSetState(g_pRasterizerState); // Render command lists - int vtx_offset = 0; - int idx_offset = 0; - ImVec2 pos = draw_data->DisplayPos; + int vtx_offset = 0; + int idx_offset = 0; + ImVec2 pos = draw_data->DisplayPos; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -194,7 +204,8 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) else { // Apply scissor/clipping rectangle - const D3D10_RECT r = { (LONG)(pcmd->ClipRect.x - pos.x), (LONG)(pcmd->ClipRect.y - pos.y), (LONG)(pcmd->ClipRect.z - pos.x), (LONG)(pcmd->ClipRect.w - pos.y)}; + const D3D10_RECT r = { (LONG)(pcmd->ClipRect.x - pos.x), (LONG)(pcmd->ClipRect.y - pos.y), + (LONG)(pcmd->ClipRect.z - pos.x), (LONG)(pcmd->ClipRect.w - pos.y) }; ctx->RSSetScissorRects(1, &r); // Bind texture, Draw @@ -210,55 +221,77 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) // Restore modified DX state ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); ctx->RSSetViewports(old.ViewportsCount, old.Viewports); - ctx->RSSetState(old.RS); if (old.RS) old.RS->Release(); - ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release(); - ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release(); - ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release(); - ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release(); - ctx->PSSetShader(old.PS); if (old.PS) old.PS->Release(); - ctx->VSSetShader(old.VS); if (old.VS) old.VS->Release(); - ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release(); + ctx->RSSetState(old.RS); + if (old.RS) + old.RS->Release(); + ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); + if (old.BlendState) + old.BlendState->Release(); + ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); + if (old.DepthStencilState) + old.DepthStencilState->Release(); + ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); + if (old.PSShaderResource) + old.PSShaderResource->Release(); + ctx->PSSetSamplers(0, 1, &old.PSSampler); + if (old.PSSampler) + old.PSSampler->Release(); + ctx->PSSetShader(old.PS); + if (old.PS) + old.PS->Release(); + ctx->VSSetShader(old.VS); + if (old.VS) + old.VS->Release(); + ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); + if (old.VSConstantBuffer) + old.VSConstantBuffer->Release(); ctx->IASetPrimitiveTopology(old.PrimitiveTopology); - ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release(); - ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release(); - ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); + ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); + if (old.IndexBuffer) + old.IndexBuffer->Release(); + ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); + if (old.VertexBuffer) + old.VertexBuffer->Release(); + ctx->IASetInputLayout(old.InputLayout); + if (old.InputLayout) + old.InputLayout->Release(); } static void ImGui_ImplDX10_CreateFontsTexture() { // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(); unsigned char* pixels; - int width, height; + int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Upload texture to graphics system { D3D10_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.Width = width; - desc.Height = height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; - desc.Usage = D3D10_USAGE_DEFAULT; - desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; - desc.CPUAccessFlags = 0; + desc.Usage = D3D10_USAGE_DEFAULT; + desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; - ID3D10Texture2D *pTexture = NULL; + ID3D10Texture2D* pTexture = NULL; D3D10_SUBRESOURCE_DATA subResource; - subResource.pSysMem = pixels; - subResource.SysMemPitch = desc.Width * 4; + subResource.pSysMem = pixels; + subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); // Create texture view D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc; ZeroMemory(&srv_desc, sizeof(srv_desc)); - srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; - srv_desc.Texture2D.MipLevels = desc.MipLevels; + srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; + srv_desc.Texture2D.MipLevels = desc.MipLevels; srv_desc.Texture2D.MostDetailedMip = 0; g_pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &g_pFontTextureView); pTexture->Release(); @@ -271,35 +304,36 @@ static void ImGui_ImplDX10_CreateFontsTexture() { D3D10_SAMPLER_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR; - desc.AddressU = D3D10_TEXTURE_ADDRESS_WRAP; - desc.AddressV = D3D10_TEXTURE_ADDRESS_WRAP; - desc.AddressW = D3D10_TEXTURE_ADDRESS_WRAP; - desc.MipLODBias = 0.f; + desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR; + desc.AddressU = D3D10_TEXTURE_ADDRESS_WRAP; + desc.AddressV = D3D10_TEXTURE_ADDRESS_WRAP; + desc.AddressW = D3D10_TEXTURE_ADDRESS_WRAP; + desc.MipLODBias = 0.f; desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS; - desc.MinLOD = 0.f; - desc.MaxLOD = 0.f; + desc.MinLOD = 0.f; + desc.MaxLOD = 0.f; g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); } } -bool ImGui_ImplDX10_CreateDeviceObjects() +bool ImGui_ImplDX10_CreateDeviceObjects() { if (!g_pd3dDevice) return false; if (g_pFontSampler) ImGui_ImplDX10_InvalidateDeviceObjects(); - // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) - // If you would like to use this DX10 sample code but remove this dependency you can: - // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] - // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. + // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of + // d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) If you would like to use this DX10 sample code but remove this + // dependency you can: + // 1) compile once, save the compiled shader blobs into a file or source code and pass them to + // CreateVertexShader()/CreatePixelShader() [preferred solution] 2) use code to detect any version of the DLL and + // grab a pointer to D3DCompile from the DLL. // See https://github.com/ocornut/imgui/pull/638 for sources and details. // Create the vertex shader { - static const char* vertexShader = - "cbuffer vertexBuffer : register(b0) \ + static const char* vertexShader = "cbuffer vertexBuffer : register(b0) \ {\ float4x4 ProjectionMatrix; \ };\ @@ -326,38 +360,46 @@ bool ImGui_ImplDX10_CreateDeviceObjects() return output;\ }"; - D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, NULL); - if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! + D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, + NULL); + if (g_pVertexShaderBlob + == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const + // char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! return false; - if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pVertexShader) != S_OK) + if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), + g_pVertexShaderBlob->GetBufferSize(), &g_pVertexShader) + != S_OK) return false; // Create the input layout - D3D10_INPUT_ELEMENT_DESC local_layout[] = - { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D10_INPUT_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D10_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D10_INPUT_PER_VERTEX_DATA, 0 }, + D3D10_INPUT_ELEMENT_DESC local_layout[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D10_INPUT_PER_VERTEX_DATA, + 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D10_INPUT_PER_VERTEX_DATA, + 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D10_INPUT_PER_VERTEX_DATA, + 0 }, }; - if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK) + if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), + g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) + != S_OK) return false; // Create the constant buffer { D3D10_BUFFER_DESC desc; - desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); - desc.Usage = D3D10_USAGE_DYNAMIC; - desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER; + desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); + desc.Usage = D3D10_USAGE_DYNAMIC; + desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; - desc.MiscFlags = 0; + desc.MiscFlags = 0; g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer); } } // Create the pixel shader { - static const char* pixelShader = - "struct PS_INPUT\ + static const char* pixelShader = "struct PS_INPUT\ {\ float4 pos : SV_POSITION;\ float4 col : COLOR0;\ @@ -372,10 +414,15 @@ bool ImGui_ImplDX10_CreateDeviceObjects() return out_col; \ }"; - D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, NULL); - if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! + D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, + NULL); + if (g_pPixelShaderBlob + == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const + // char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! return false; - if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), &g_pPixelShader) != S_OK) + if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), + g_pPixelShaderBlob->GetBufferSize(), &g_pPixelShader) + != S_OK) return false; } @@ -383,14 +430,14 @@ bool ImGui_ImplDX10_CreateDeviceObjects() { D3D10_BLEND_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.AlphaToCoverageEnable = false; - desc.BlendEnable[0] = true; - desc.SrcBlend = D3D10_BLEND_SRC_ALPHA; - desc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA; - desc.BlendOp = D3D10_BLEND_OP_ADD; - desc.SrcBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; - desc.DestBlendAlpha = D3D10_BLEND_ZERO; - desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.AlphaToCoverageEnable = false; + desc.BlendEnable[0] = true; + desc.SrcBlend = D3D10_BLEND_SRC_ALPHA; + desc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA; + desc.BlendOp = D3D10_BLEND_OP_ADD; + desc.SrcBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.DestBlendAlpha = D3D10_BLEND_ZERO; + desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); } @@ -399,9 +446,9 @@ bool ImGui_ImplDX10_CreateDeviceObjects() { D3D10_RASTERIZER_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.FillMode = D3D10_FILL_SOLID; - desc.CullMode = D3D10_CULL_NONE; - desc.ScissorEnable = true; + desc.FillMode = D3D10_FILL_SOLID; + desc.CullMode = D3D10_CULL_NONE; + desc.ScissorEnable = true; desc.DepthClipEnable = true; g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); } @@ -410,13 +457,14 @@ bool ImGui_ImplDX10_CreateDeviceObjects() { D3D10_DEPTH_STENCIL_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.DepthEnable = false; - desc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL; - desc.DepthFunc = D3D10_COMPARISON_ALWAYS; - desc.StencilEnable = false; - desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; + desc.DepthEnable = false; + desc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL; + desc.DepthFunc = D3D10_COMPARISON_ALWAYS; + desc.StencilEnable = false; + desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = + D3D10_STENCIL_OP_KEEP; desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS; - desc.BackFace = desc.FrontFace; + desc.BackFace = desc.FrontFace; g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); } @@ -425,46 +473,101 @@ bool ImGui_ImplDX10_CreateDeviceObjects() return true; } -void ImGui_ImplDX10_InvalidateDeviceObjects() +void ImGui_ImplDX10_InvalidateDeviceObjects() { if (!g_pd3dDevice) return; - if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } - if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } + if (g_pFontSampler) + { + g_pFontSampler->Release(); + g_pFontSampler = NULL; + } + if (g_pFontTextureView) + { + g_pFontTextureView->Release(); + g_pFontTextureView = NULL; + ImGui::GetIO().Fonts->TexID = NULL; + } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (g_pIB) + { + g_pIB->Release(); + g_pIB = NULL; + } + if (g_pVB) + { + g_pVB->Release(); + g_pVB = NULL; + } - if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } - if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } - if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } - if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } - if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; } - if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } - if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } - if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; } - if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; } + if (g_pBlendState) + { + g_pBlendState->Release(); + g_pBlendState = NULL; + } + if (g_pDepthStencilState) + { + g_pDepthStencilState->Release(); + g_pDepthStencilState = NULL; + } + if (g_pRasterizerState) + { + g_pRasterizerState->Release(); + g_pRasterizerState = NULL; + } + if (g_pPixelShader) + { + g_pPixelShader->Release(); + g_pPixelShader = NULL; + } + if (g_pPixelShaderBlob) + { + g_pPixelShaderBlob->Release(); + g_pPixelShaderBlob = NULL; + } + if (g_pVertexConstantBuffer) + { + g_pVertexConstantBuffer->Release(); + g_pVertexConstantBuffer = NULL; + } + if (g_pInputLayout) + { + g_pInputLayout->Release(); + g_pInputLayout = NULL; + } + if (g_pVertexShader) + { + g_pVertexShader->Release(); + g_pVertexShader = NULL; + } + if (g_pVertexShaderBlob) + { + g_pVertexShaderBlob->Release(); + g_pVertexShaderBlob = NULL; + } } -bool ImGui_ImplDX10_Init(ID3D10Device* device) +bool ImGui_ImplDX10_Init(ID3D10Device* device) { - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(); io.BackendRendererName = "imgui_impl_dx10"; // Get factory from device - IDXGIDevice* pDXGIDevice = NULL; + IDXGIDevice* pDXGIDevice = NULL; IDXGIAdapter* pDXGIAdapter = NULL; - IDXGIFactory* pFactory = NULL; + IDXGIFactory* pFactory = NULL; if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK) if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK) if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) { g_pd3dDevice = device; - g_pFactory = pFactory; + g_pFactory = pFactory; } - if (pDXGIDevice) pDXGIDevice->Release(); - if (pDXGIAdapter) pDXGIAdapter->Release(); + if (pDXGIDevice) + pDXGIDevice->Release(); + if (pDXGIAdapter) + pDXGIAdapter->Release(); return true; } @@ -472,7 +575,11 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) void ImGui_ImplDX10_Shutdown() { ImGui_ImplDX10_InvalidateDeviceObjects(); - if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; } + if (g_pFactory) + { + g_pFactory->Release(); + g_pFactory = NULL; + } g_pd3dDevice = NULL; } diff --git a/src/kiwano-imgui/imgui_impl_dx10.h b/src/kiwano-imgui/imgui_impl_dx10.h index 26fdb0c1..7fd26e53 100644 --- a/src/kiwano-imgui/imgui_impl_dx10.h +++ b/src/kiwano-imgui/imgui_impl_dx10.h @@ -7,13 +7,13 @@ struct ID3D10Device; -IMGUI_IMPL_API bool ImGui_ImplDX10_Init(ID3D10Device* device); -IMGUI_IMPL_API void ImGui_ImplDX10_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplDX10_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data); +IMGUI_IMPL_API bool ImGui_ImplDX10_Init(ID3D10Device* device); +IMGUI_IMPL_API void ImGui_ImplDX10_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplDX10_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing ImGui state. -IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects(); -IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects(); +IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects(); #endif diff --git a/src/kiwano-imgui/imgui_impl_dx11.cpp b/src/kiwano-imgui/imgui_impl_dx11.cpp index 470f914b..5fccb34d 100644 --- a/src/kiwano-imgui/imgui_impl_dx11.cpp +++ b/src/kiwano-imgui/imgui_impl_dx11.cpp @@ -3,40 +3,41 @@ #include // DirectX -#include #include #include +#include #ifdef _MSC_VER -#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. +#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif // DirectX data -static ID3D11Device* g_pd3dDevice = NULL; -static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; -static IDXGIFactory* g_pFactory = NULL; -static ID3D11Buffer* g_pVB = NULL; -static ID3D11Buffer* g_pIB = NULL; -static ID3D10Blob* g_pVertexShaderBlob = NULL; -static ID3D11VertexShader* g_pVertexShader = NULL; -static ID3D11InputLayout* g_pInputLayout = NULL; -static ID3D11Buffer* g_pVertexConstantBuffer = NULL; -static ID3D10Blob* g_pPixelShaderBlob = NULL; -static ID3D11PixelShader* g_pPixelShader = NULL; -static ID3D11SamplerState* g_pFontSampler = NULL; -static ID3D11ShaderResourceView*g_pFontTextureView = NULL; -static ID3D11RasterizerState* g_pRasterizerState = NULL; -static ID3D11BlendState* g_pBlendState = NULL; -static ID3D11DepthStencilState* g_pDepthStencilState = NULL; -static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; +static ID3D11Device* g_pd3dDevice = NULL; +static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; +static IDXGIFactory* g_pFactory = NULL; +static ID3D11Buffer* g_pVB = NULL; +static ID3D11Buffer* g_pIB = NULL; +static ID3D10Blob* g_pVertexShaderBlob = NULL; +static ID3D11VertexShader* g_pVertexShader = NULL; +static ID3D11InputLayout* g_pInputLayout = NULL; +static ID3D11Buffer* g_pVertexConstantBuffer = NULL; +static ID3D10Blob* g_pPixelShaderBlob = NULL; +static ID3D11PixelShader* g_pPixelShader = NULL; +static ID3D11SamplerState* g_pFontSampler = NULL; +static ID3D11ShaderResourceView* g_pFontTextureView = NULL; +static ID3D11RasterizerState* g_pRasterizerState = NULL; +static ID3D11BlendState* g_pBlendState = NULL; +static ID3D11DepthStencilState* g_pDepthStencilState = NULL; +static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; struct VERTEX_CONSTANT_BUFFER { - float mvp[4][4]; + float mvp[4][4]; }; // Render Function -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) +// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from +// your main loop) void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) { ID3D11DeviceContext* ctx = g_pd3dDeviceContext; @@ -44,27 +45,35 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) // Create and grow vertex/index buffers if needed if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) { - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } + if (g_pVB) + { + g_pVB->Release(); + g_pVB = NULL; + } g_VertexBufferSize = draw_data->TotalVtxCount + 5000; D3D11_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); - desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); - desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - desc.MiscFlags = 0; + desc.MiscFlags = 0; if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0) return; } if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) { - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } + if (g_pIB) + { + g_pIB->Release(); + g_pIB = NULL; + } g_IndexBufferSize = draw_data->TotalIdxCount + 10000; D3D11_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); - desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); - desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); + desc.BindFlags = D3D11_BIND_INDEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0) return; @@ -77,7 +86,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) return; ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; - ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; + ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -90,50 +99,51 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ctx->Unmap(g_pIB, 0); // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). + // Our visible imgui space lies from draw_data->DisplayPos (top left) to + // draw_data->DisplayPos+data_data->DisplaySize (bottom right). { D3D11_MAPPED_SUBRESOURCE mapped_resource; if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; - float L = draw_data->DisplayPos.x; - float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; - float T = draw_data->DisplayPos.y; - float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; - float mvp[4][4] = - { - { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.5f, 0.0f }, - { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + float mvp[4][4] = { + { 2.0f / (R - L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f / (T - B), 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.5f, 0.0f }, + { (R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f }, }; memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); ctx->Unmap(g_pVertexConstantBuffer, 0); } - // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) + // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and + // verbose. Close your eyes!) struct BACKUP_DX11_STATE { - UINT ScissorRectsCount, ViewportsCount; - D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; - D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; - ID3D11RasterizerState* RS; - ID3D11BlendState* BlendState; - FLOAT BlendFactor[4]; - UINT SampleMask; - UINT StencilRef; - ID3D11DepthStencilState* DepthStencilState; - ID3D11ShaderResourceView* PSShaderResource; - ID3D11SamplerState* PSSampler; - ID3D11PixelShader* PS; - ID3D11VertexShader* VS; - UINT PSInstancesCount, VSInstancesCount; - ID3D11ClassInstance* PSInstances[256], *VSInstances[256]; // 256 is max according to PSSetShader documentation - D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology; - ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer; - UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset; - DXGI_FORMAT IndexBufferFormat; - ID3D11InputLayout* InputLayout; + UINT ScissorRectsCount, ViewportsCount; + D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; + D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; + ID3D11RasterizerState* RS; + ID3D11BlendState* BlendState; + FLOAT BlendFactor[4]; + UINT SampleMask; + UINT StencilRef; + ID3D11DepthStencilState* DepthStencilState; + ID3D11ShaderResourceView* PSShaderResource; + ID3D11SamplerState* PSSampler; + ID3D11PixelShader* PS; + ID3D11VertexShader* VS; + UINT PSInstancesCount, VSInstancesCount; + ID3D11ClassInstance *PSInstances[256], *VSInstances[256]; // 256 is max according to PSSetShader documentation + D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology; + ID3D11Buffer * IndexBuffer, *VertexBuffer, *VSConstantBuffer; + UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset; + DXGI_FORMAT IndexBufferFormat; + ID3D11InputLayout* InputLayout; }; BACKUP_DX11_STATE old; old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; @@ -156,8 +166,8 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) // Setup viewport D3D11_VIEWPORT vp; memset(&vp, 0, sizeof(D3D11_VIEWPORT)); - vp.Width = draw_data->DisplaySize.x; - vp.Height = draw_data->DisplaySize.y; + vp.Width = draw_data->DisplaySize.x; + vp.Height = draw_data->DisplaySize.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0; @@ -182,9 +192,9 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ctx->RSSetState(g_pRasterizerState); // Render command lists - int vtx_offset = 0; - int idx_offset = 0; - ImVec2 pos = draw_data->DisplayPos; + int vtx_offset = 0; + int idx_offset = 0; + ImVec2 pos = draw_data->DisplayPos; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -199,7 +209,8 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) else { // Apply scissor/clipping rectangle - const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - pos.x), (LONG)(pcmd->ClipRect.y - pos.y), (LONG)(pcmd->ClipRect.z - pos.x), (LONG)(pcmd->ClipRect.w - pos.y) }; + const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - pos.x), (LONG)(pcmd->ClipRect.y - pos.y), + (LONG)(pcmd->ClipRect.z - pos.x), (LONG)(pcmd->ClipRect.w - pos.y) }; ctx->RSSetScissorRects(1, &r); // Bind texture, Draw @@ -215,61 +226,87 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) // Restore modified DX state ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); ctx->RSSetViewports(old.ViewportsCount, old.Viewports); - ctx->RSSetState(old.RS); if (old.RS) old.RS->Release(); - ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release(); - ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release(); - ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release(); - ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release(); - ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release(); - for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release(); - ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release(); - ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release(); - for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release(); + ctx->RSSetState(old.RS); + if (old.RS) + old.RS->Release(); + ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); + if (old.BlendState) + old.BlendState->Release(); + ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); + if (old.DepthStencilState) + old.DepthStencilState->Release(); + ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); + if (old.PSShaderResource) + old.PSShaderResource->Release(); + ctx->PSSetSamplers(0, 1, &old.PSSampler); + if (old.PSSampler) + old.PSSampler->Release(); + ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); + if (old.PS) + old.PS->Release(); + for (UINT i = 0; i < old.PSInstancesCount; i++) + if (old.PSInstances[i]) + old.PSInstances[i]->Release(); + ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); + if (old.VS) + old.VS->Release(); + ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); + if (old.VSConstantBuffer) + old.VSConstantBuffer->Release(); + for (UINT i = 0; i < old.VSInstancesCount; i++) + if (old.VSInstances[i]) + old.VSInstances[i]->Release(); ctx->IASetPrimitiveTopology(old.PrimitiveTopology); - ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release(); - ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release(); - ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); + ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); + if (old.IndexBuffer) + old.IndexBuffer->Release(); + ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); + if (old.VertexBuffer) + old.VertexBuffer->Release(); + ctx->IASetInputLayout(old.InputLayout); + if (old.InputLayout) + old.InputLayout->Release(); } static void ImGui_ImplDX11_CreateFontsTexture() { // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(); unsigned char* pixels; - int width, height; + int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Upload texture to graphics system { D3D11_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.Width = width; - desc.Height = height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - desc.CPUAccessFlags = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; - ID3D11Texture2D *pTexture = NULL; + ID3D11Texture2D* pTexture = NULL; D3D11_SUBRESOURCE_DATA subResource; - subResource.pSysMem = pixels; - subResource.SysMemPitch = desc.Width * 4; + subResource.pSysMem = pixels; + subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; - g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); + g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); if (pTexture) { // Create texture view D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; ZeroMemory(&srvDesc, sizeof(srvDesc)); - srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - srvDesc.Texture2D.MipLevels = desc.MipLevels; + srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; - g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView); + g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView); pTexture->Release(); } @@ -282,35 +319,36 @@ static void ImGui_ImplDX11_CreateFontsTexture() { D3D11_SAMPLER_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; - desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; - desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; - desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; - desc.MipLODBias = 0.f; + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + desc.MipLODBias = 0.f; desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; - desc.MinLOD = 0.f; - desc.MaxLOD = 0.f; + desc.MinLOD = 0.f; + desc.MaxLOD = 0.f; g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); } } -bool ImGui_ImplDX11_CreateDeviceObjects() +bool ImGui_ImplDX11_CreateDeviceObjects() { if (!g_pd3dDevice) return false; if (g_pFontSampler) ImGui_ImplDX11_InvalidateDeviceObjects(); - // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) - // If you would like to use this DX11 sample code but remove this dependency you can: - // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] - // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. + // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of + // d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) If you would like to use this DX11 sample code but remove this + // dependency you can: + // 1) compile once, save the compiled shader blobs into a file or source code and pass them to + // CreateVertexShader()/CreatePixelShader() [preferred solution] 2) use code to detect any version of the DLL and + // grab a pointer to D3DCompile from the DLL. // See https://github.com/ocornut/imgui/pull/638 for sources and details. // Create the vertex shader { - static const char* vertexShader = - "cbuffer vertexBuffer : register(b0) \ + static const char* vertexShader = "cbuffer vertexBuffer : register(b0) \ {\ float4x4 ProjectionMatrix; \ };\ @@ -337,38 +375,46 @@ bool ImGui_ImplDX11_CreateDeviceObjects() return output;\ }"; - D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, NULL); - if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! + D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, + NULL); + if (g_pVertexShaderBlob + == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const + // char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! return false; - if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK) + if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), + g_pVertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) + != S_OK) return false; // Create the input layout - D3D11_INPUT_ELEMENT_DESC local_layout[] = - { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + D3D11_INPUT_ELEMENT_DESC local_layout[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, + 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, + 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, + 0 }, }; - if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK) + if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), + g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) + != S_OK) return false; // Create the constant buffer { D3D11_BUFFER_DESC desc; - desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); - desc.Usage = D3D11_USAGE_DYNAMIC; - desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - desc.MiscFlags = 0; + desc.MiscFlags = 0; g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer); } } // Create the pixel shader { - static const char* pixelShader = - "struct PS_INPUT\ + static const char* pixelShader = "struct PS_INPUT\ {\ float4 pos : SV_POSITION;\ float4 col : COLOR0;\ @@ -383,10 +429,15 @@ bool ImGui_ImplDX11_CreateDeviceObjects() return out_col; \ }"; - D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, NULL); - if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! + D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, + NULL); + if (g_pPixelShaderBlob + == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const + // char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! return false; - if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK) + if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), + g_pPixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) + != S_OK) return false; } @@ -394,14 +445,14 @@ bool ImGui_ImplDX11_CreateDeviceObjects() { D3D11_BLEND_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.AlphaToCoverageEnable = false; - desc.RenderTarget[0].BlendEnable = true; - desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; - desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; - desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + desc.AlphaToCoverageEnable = false; + desc.RenderTarget[0].BlendEnable = true; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); } @@ -410,9 +461,9 @@ bool ImGui_ImplDX11_CreateDeviceObjects() { D3D11_RASTERIZER_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.FillMode = D3D11_FILL_SOLID; - desc.CullMode = D3D11_CULL_NONE; - desc.ScissorEnable = true; + desc.FillMode = D3D11_FILL_SOLID; + desc.CullMode = D3D11_CULL_NONE; + desc.ScissorEnable = true; desc.DepthClipEnable = true; g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); } @@ -421,13 +472,14 @@ bool ImGui_ImplDX11_CreateDeviceObjects() { D3D11_DEPTH_STENCIL_DESC desc; ZeroMemory(&desc, sizeof(desc)); - desc.DepthEnable = false; - desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; - desc.DepthFunc = D3D11_COMPARISON_ALWAYS; - desc.StencilEnable = false; - desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + desc.DepthEnable = false; + desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + desc.DepthFunc = D3D11_COMPARISON_ALWAYS; + desc.StencilEnable = false; + desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = + D3D11_STENCIL_OP_KEEP; desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; - desc.BackFace = desc.FrontFace; + desc.BackFace = desc.FrontFace; g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); } @@ -436,47 +488,102 @@ bool ImGui_ImplDX11_CreateDeviceObjects() return true; } -void ImGui_ImplDX11_InvalidateDeviceObjects() +void ImGui_ImplDX11_InvalidateDeviceObjects() { if (!g_pd3dDevice) return; - if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } - if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } + if (g_pFontSampler) + { + g_pFontSampler->Release(); + g_pFontSampler = NULL; + } + if (g_pFontTextureView) + { + g_pFontTextureView->Release(); + g_pFontTextureView = NULL; + ImGui::GetIO().Fonts->TexID = NULL; + } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (g_pIB) + { + g_pIB->Release(); + g_pIB = NULL; + } + if (g_pVB) + { + g_pVB->Release(); + g_pVB = NULL; + } - if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } - if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } - if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } - if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } - if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; } - if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } - if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } - if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; } - if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; } + if (g_pBlendState) + { + g_pBlendState->Release(); + g_pBlendState = NULL; + } + if (g_pDepthStencilState) + { + g_pDepthStencilState->Release(); + g_pDepthStencilState = NULL; + } + if (g_pRasterizerState) + { + g_pRasterizerState->Release(); + g_pRasterizerState = NULL; + } + if (g_pPixelShader) + { + g_pPixelShader->Release(); + g_pPixelShader = NULL; + } + if (g_pPixelShaderBlob) + { + g_pPixelShaderBlob->Release(); + g_pPixelShaderBlob = NULL; + } + if (g_pVertexConstantBuffer) + { + g_pVertexConstantBuffer->Release(); + g_pVertexConstantBuffer = NULL; + } + if (g_pInputLayout) + { + g_pInputLayout->Release(); + g_pInputLayout = NULL; + } + if (g_pVertexShader) + { + g_pVertexShader->Release(); + g_pVertexShader = NULL; + } + if (g_pVertexShaderBlob) + { + g_pVertexShaderBlob->Release(); + g_pVertexShaderBlob = NULL; + } } -bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) +bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) { - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(); io.BackendRendererName = "imgui_impl_dx11"; // Get factory from device - IDXGIDevice* pDXGIDevice = NULL; + IDXGIDevice* pDXGIDevice = NULL; IDXGIAdapter* pDXGIAdapter = NULL; - IDXGIFactory* pFactory = NULL; + IDXGIFactory* pFactory = NULL; if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK) if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK) if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) { - g_pd3dDevice = device; + g_pd3dDevice = device; g_pd3dDeviceContext = device_context; - g_pFactory = pFactory; + g_pFactory = pFactory; } - if (pDXGIDevice) pDXGIDevice->Release(); - if (pDXGIAdapter) pDXGIAdapter->Release(); + if (pDXGIDevice) + pDXGIDevice->Release(); + if (pDXGIAdapter) + pDXGIAdapter->Release(); return true; } @@ -484,8 +591,12 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co void ImGui_ImplDX11_Shutdown() { ImGui_ImplDX11_InvalidateDeviceObjects(); - if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; } - g_pd3dDevice = NULL; + if (g_pFactory) + { + g_pFactory->Release(); + g_pFactory = NULL; + } + g_pd3dDevice = NULL; g_pd3dDeviceContext = NULL; } diff --git a/src/kiwano-imgui/imgui_impl_dx11.h b/src/kiwano-imgui/imgui_impl_dx11.h index 4964c00c..e15f79d1 100644 --- a/src/kiwano-imgui/imgui_impl_dx11.h +++ b/src/kiwano-imgui/imgui_impl_dx11.h @@ -8,13 +8,13 @@ struct ID3D11Device; struct ID3D11DeviceContext; -IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); -IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); +IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); +IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing ImGui state. -IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); -IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); +IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); #endif diff --git a/src/kiwano-imgui/kiwano-imgui.h b/src/kiwano-imgui/kiwano-imgui.h index 453a1a0c..6f8a1ab2 100644 --- a/src/kiwano-imgui/kiwano-imgui.h +++ b/src/kiwano-imgui/kiwano-imgui.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 diff --git a/src/kiwano-network/HttpClient.cpp b/src/kiwano-network/HttpClient.cpp index 65b0305d..cbee5f69 100644 --- a/src/kiwano-network/HttpClient.cpp +++ b/src/kiwano-network/HttpClient.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -18,364 +18,338 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include #include - +#include +#include +#include #include #include #include - -#include -#include #include <3rd-party/curl/curl.h> // CURL namespace { - using namespace kiwano; - using namespace kiwano::network; +using namespace kiwano; +using namespace kiwano::network; - uint32_t write_data(void* buffer, uint32_t size, uint32_t nmemb, void* userp) - { - ByteString* recv_buffer = (ByteString*)userp; - uint32_t total = size * nmemb; +uint32_t write_data(void* buffer, uint32_t size, uint32_t nmemb, void* userp) +{ + ByteString* recv_buffer = (ByteString*)userp; + uint32_t total = size * nmemb; - // add data to the end of recv_buffer - // write data maybe called more than once in a single request - recv_buffer->append((char*)buffer, total); + // add data to the end of recv_buffer + // write data maybe called more than once in a single request + recv_buffer->append((char*)buffer, total); - return total; - } - - ByteString convert_to_utf8(String const& str) - { - std::wstring_convert> utf8_conv; - ByteString result; - - try - { - result = utf8_conv.to_bytes(str.c_str()); - } - catch (std::range_error&) - { - // bad conversion - result = WideToMultiByte(str); - } - return result; - } - - String convert_from_utf8(ByteString const& str) - { - oc::string_convert> utf8_conv; - String result; - - try - { - result = utf8_conv.from_bytes(str); - } - catch (std::range_error&) - { - // bad conversion - result = MultiByteToWide(str); - } - return result; - } - - class Curl - { - public: - Curl() - : curl_(curl_easy_init()) - , curl_headers_(nullptr) - { - } - - ~Curl() - { - if (curl_) - { - curl_easy_cleanup(curl_); - curl_ = nullptr; - } - - if (curl_headers_) - { - curl_slist_free_all(curl_headers_); - curl_headers_ = nullptr; - } - } - - bool Init(HttpClient* client, Vector const& headers, ByteString const& url, ByteString* response_data, ByteString* response_header, char* error_buffer) - { - if (!SetOption(CURLOPT_ERRORBUFFER, error_buffer)) - return false; - if (!SetOption(CURLOPT_TIMEOUT, client->GetTimeoutForRead())) - return false; - if (!SetOption(CURLOPT_CONNECTTIMEOUT, client->GetTimeoutForConnect())) - return false; - - const auto ssl_ca_file = wide_to_string(client->GetSSLVerification()); - if (ssl_ca_file.empty()) { - if (!SetOption(CURLOPT_SSL_VERIFYPEER, 0L)) - return false; - if (!SetOption(CURLOPT_SSL_VERIFYHOST, 0L)) - return false; - } - else { - if (!SetOption(CURLOPT_SSL_VERIFYPEER, 1L)) - return false; - if (!SetOption(CURLOPT_SSL_VERIFYHOST, 2L)) - return false; - if (!SetOption(CURLOPT_CAINFO, ssl_ca_file.c_str())) - return false; - } - - if (!SetOption(CURLOPT_NOSIGNAL, 1L)) - return false; - if (!SetOption(CURLOPT_ACCEPT_ENCODING, "")) - return false; - - // set request headers - if (!headers.empty()) - { - for (const auto& header : headers) - { - curl_headers_ = curl_slist_append(curl_headers_, header.c_str()); - } - if (!SetOption(CURLOPT_HTTPHEADER, curl_headers_)) - return false; - } - - return SetOption(CURLOPT_URL, url.c_str()) - && SetOption(CURLOPT_WRITEFUNCTION, write_data) - && SetOption(CURLOPT_WRITEDATA, response_data) - && SetOption(CURLOPT_HEADERFUNCTION, write_data) - && SetOption(CURLOPT_HEADERDATA, response_header); - } - - bool Perform(long* response_code) - { - if (CURLE_OK != curl_easy_perform(curl_)) - return false; - - CURLcode code = curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, response_code); - return code == CURLE_OK && (*response_code >= 200 && *response_code < 300); - } - - template - bool SetOption(CURLoption option, _Args&&... args) - { - return CURLE_OK == curl_easy_setopt(curl_, option, std::forward<_Args>(args)...); - } - - public: - static inline bool GetRequest( - HttpClient* client, - Vector const& headers, - ByteString const& url, - long* response_code, - ByteString* response_data, - ByteString* response_header, - char* error_buffer) - { - Curl curl; - return curl.Init(client, headers, url, response_data, response_header, error_buffer) - && curl.SetOption(CURLOPT_FOLLOWLOCATION, true) - && curl.Perform(response_code); - } - - static inline bool PostRequest( - HttpClient* client, - Vector const& headers, - ByteString const& url, - ByteString const& request_data, - long* response_code, - ByteString* response_data, - ByteString* response_header, - char* error_buffer) - { - Curl curl; - return curl.Init(client, headers, url, response_data, response_header, error_buffer) - && curl.SetOption(CURLOPT_POST, 1) - && curl.SetOption(CURLOPT_POSTFIELDS, request_data.c_str()) - && curl.SetOption(CURLOPT_POSTFIELDSIZE, request_data.size()) - && curl.Perform(response_code); - } - - static inline bool PutRequest( - HttpClient* client, - Vector const& headers, - ByteString const& url, - ByteString const& request_data, - long* response_code, - ByteString* response_data, - ByteString* response_header, - char* error_buffer) - { - Curl curl; - return curl.Init(client, headers, url, response_data, response_header, error_buffer) - && curl.SetOption(CURLOPT_CUSTOMREQUEST, "PUT") - && curl.SetOption(CURLOPT_POSTFIELDS, request_data.c_str()) - && curl.SetOption(CURLOPT_POSTFIELDSIZE, request_data.size()) - && curl.Perform(response_code); - } - - static inline bool DeleteRequest( - HttpClient* client, - Vector const& headers, - ByteString const& url, - long* response_code, - ByteString* response_data, - ByteString* response_header, - char* error_buffer) - { - Curl curl; - return curl.Init(client, headers, url, response_data, response_header, error_buffer) - && curl.SetOption(CURLOPT_CUSTOMREQUEST, "DELETE") - && curl.SetOption(CURLOPT_FOLLOWLOCATION, true) - && curl.Perform(response_code); - } - - private: - CURL* curl_; - curl_slist* curl_headers_; - }; + return total; } +ByteString convert_to_utf8(String const& str) +{ + std::wstring_convert> utf8_conv; + ByteString result; + + try + { + result = utf8_conv.to_bytes(str.c_str()); + } + catch (std::range_error&) + { + // bad conversion + result = WideToMultiByte(str); + } + return result; +} + +String convert_from_utf8(ByteString const& str) +{ + oc::string_convert> utf8_conv; + String result; + + try + { + result = utf8_conv.from_bytes(str); + } + catch (std::range_error&) + { + // bad conversion + result = MultiByteToWide(str); + } + return result; +} + +class Curl +{ +public: + Curl() + : curl_(curl_easy_init()) + , curl_headers_(nullptr) + { + } + + ~Curl() + { + if (curl_) + { + curl_easy_cleanup(curl_); + curl_ = nullptr; + } + + if (curl_headers_) + { + curl_slist_free_all(curl_headers_); + curl_headers_ = nullptr; + } + } + + bool Init(HttpClient* client, Vector const& headers, ByteString const& url, ByteString* response_data, + ByteString* response_header, char* error_buffer) + { + if (!SetOption(CURLOPT_ERRORBUFFER, error_buffer)) + return false; + if (!SetOption(CURLOPT_TIMEOUT, client->GetTimeoutForRead())) + return false; + if (!SetOption(CURLOPT_CONNECTTIMEOUT, client->GetTimeoutForConnect())) + return false; + + const auto ssl_ca_file = wide_to_string(client->GetSSLVerification()); + if (ssl_ca_file.empty()) + { + if (!SetOption(CURLOPT_SSL_VERIFYPEER, 0L)) + return false; + if (!SetOption(CURLOPT_SSL_VERIFYHOST, 0L)) + return false; + } + else + { + if (!SetOption(CURLOPT_SSL_VERIFYPEER, 1L)) + return false; + if (!SetOption(CURLOPT_SSL_VERIFYHOST, 2L)) + return false; + if (!SetOption(CURLOPT_CAINFO, ssl_ca_file.c_str())) + return false; + } + + if (!SetOption(CURLOPT_NOSIGNAL, 1L)) + return false; + if (!SetOption(CURLOPT_ACCEPT_ENCODING, "")) + return false; + + // set request headers + if (!headers.empty()) + { + for (const auto& header : headers) + { + curl_headers_ = curl_slist_append(curl_headers_, header.c_str()); + } + if (!SetOption(CURLOPT_HTTPHEADER, curl_headers_)) + return false; + } + + return SetOption(CURLOPT_URL, url.c_str()) && SetOption(CURLOPT_WRITEFUNCTION, write_data) + && SetOption(CURLOPT_WRITEDATA, response_data) && SetOption(CURLOPT_HEADERFUNCTION, write_data) + && SetOption(CURLOPT_HEADERDATA, response_header); + } + + bool Perform(long* response_code) + { + if (CURLE_OK != curl_easy_perform(curl_)) + return false; + + CURLcode code = curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, response_code); + return code == CURLE_OK && (*response_code >= 200 && *response_code < 300); + } + + template + bool SetOption(CURLoption option, _Args&&... args) + { + return CURLE_OK == curl_easy_setopt(curl_, option, std::forward<_Args>(args)...); + } + +public: + static inline bool GetRequest(HttpClient* client, Vector const& headers, ByteString const& url, + long* response_code, ByteString* response_data, ByteString* response_header, + char* error_buffer) + { + Curl curl; + return curl.Init(client, headers, url, response_data, response_header, error_buffer) + && curl.SetOption(CURLOPT_FOLLOWLOCATION, true) && curl.Perform(response_code); + } + + static inline bool PostRequest(HttpClient* client, Vector const& headers, ByteString const& url, + ByteString const& request_data, long* response_code, ByteString* response_data, + ByteString* response_header, char* error_buffer) + { + Curl curl; + return curl.Init(client, headers, url, response_data, response_header, error_buffer) + && curl.SetOption(CURLOPT_POST, 1) && curl.SetOption(CURLOPT_POSTFIELDS, request_data.c_str()) + && curl.SetOption(CURLOPT_POSTFIELDSIZE, request_data.size()) && curl.Perform(response_code); + } + + static inline bool PutRequest(HttpClient* client, Vector const& headers, ByteString const& url, + ByteString const& request_data, long* response_code, ByteString* response_data, + ByteString* response_header, char* error_buffer) + { + Curl curl; + return curl.Init(client, headers, url, response_data, response_header, error_buffer) + && curl.SetOption(CURLOPT_CUSTOMREQUEST, "PUT") + && curl.SetOption(CURLOPT_POSTFIELDS, request_data.c_str()) + && curl.SetOption(CURLOPT_POSTFIELDSIZE, request_data.size()) && curl.Perform(response_code); + } + + static inline bool DeleteRequest(HttpClient* client, Vector const& headers, ByteString const& url, + long* response_code, ByteString* response_data, ByteString* response_header, + char* error_buffer) + { + Curl curl; + return curl.Init(client, headers, url, response_data, response_header, error_buffer) + && curl.SetOption(CURLOPT_CUSTOMREQUEST, "DELETE") && curl.SetOption(CURLOPT_FOLLOWLOCATION, true) + && curl.Perform(response_code); + } + +private: + CURL* curl_; + curl_slist* curl_headers_; +}; +} // namespace + namespace kiwano { - namespace network - { - HttpClient::HttpClient() - : timeout_for_connect_(30000 /* 30 seconds */) - , timeout_for_read_(60000 /* 60 seconds */) - { - } - - void HttpClient::SetupComponent() - { - ::curl_global_init(CURL_GLOBAL_ALL); - - std::thread thread(Closure(this, &HttpClient::NetworkThread)); - thread.detach(); - } - - void HttpClient::DestroyComponent() - { - ::curl_global_cleanup(); - } - - void HttpClient::Send(HttpRequestPtr request) - { - if (!request) - return; - - request_mutex_.lock(); - request_queue_.push(request); - request_mutex_.unlock(); - - sleep_condition_.notify_one(); - } - - void HttpClient::NetworkThread() - { - while (true) - { - HttpRequestPtr request; - { - std::lock_guard lock(request_mutex_); - while (request_queue_.empty()) - { - sleep_condition_.wait(request_mutex_); - } - request = request_queue_.front(); - request_queue_.pop(); - } - - HttpResponsePtr response = new (std::nothrow) HttpResponse(request); - Perform(request, response); - - response_mutex_.lock(); - response_queue_.push(response); - response_mutex_.unlock(); - - Application::PreformInMainThread(Closure(this, &HttpClient::DispatchResponseCallback)); - } - } - - void HttpClient::Perform(HttpRequestPtr request, HttpResponsePtr response) - { - bool ok = false; - long response_code = 0; - char error_message[256] = { 0 }; - ByteString response_header; - ByteString response_data; - - ByteString url = convert_to_utf8(request->GetUrl()); - ByteString data = convert_to_utf8(request->GetData()); - - Vector headers; - headers.reserve(request->GetHeaders().size()); - for (const auto& pair : request->GetHeaders()) - { - headers.push_back(wide_to_string(pair.first) + ":" + wide_to_string(pair.second)); - } - - switch (request->GetType()) - { - case HttpRequest::Type::Get: - ok = Curl::GetRequest(this, headers, url, &response_code, &response_data, &response_header, error_message); - break; - case HttpRequest::Type::Post: - ok = Curl::PostRequest(this, headers, url, data, &response_code, &response_data, &response_header, error_message); - break; - case HttpRequest::Type::Put: - ok = Curl::PutRequest(this, headers, url, data, &response_code, &response_data, &response_header, error_message); - break; - case HttpRequest::Type::Delete: - ok = Curl::DeleteRequest(this, headers, url, &response_code, &response_data, &response_header, error_message); - break; - default: - KGE_ERROR(L"HttpClient: unknown request type, only GET, POST, PUT or DELETE is supported"); - return; - } - - response->SetResponseCode(response_code); - response->SetHeader(MultiByteToWide(response_header)); - response->SetData(convert_from_utf8(response_data)); - if (!ok) - { - response->SetSucceed(false); - response->SetError(MultiByteToWide(error_message)); - } - else - { - response->SetSucceed(true); - } - } - - void HttpClient::DispatchResponseCallback() - { - HttpResponsePtr response; - - response_mutex_.lock(); - if (!response_queue_.empty()) - { - response = response_queue_.front(); - response_queue_.pop(); - } - response_mutex_.unlock(); - - if (response) - { - HttpRequestPtr request = response->GetRequest(); - const auto& callback = request->GetResponseCallback(); - - if (callback) - { - callback(request.get(), response.get()); - } - } - } - - } +namespace network +{ +HttpClient::HttpClient() + : timeout_for_connect_(30000 /* 30 seconds */) + , timeout_for_read_(60000 /* 60 seconds */) +{ } + +void HttpClient::SetupComponent() +{ + ::curl_global_init(CURL_GLOBAL_ALL); + + std::thread thread(Closure(this, &HttpClient::NetworkThread)); + thread.detach(); +} + +void HttpClient::DestroyComponent() +{ + ::curl_global_cleanup(); +} + +void HttpClient::Send(HttpRequestPtr request) +{ + if (!request) + return; + + request_mutex_.lock(); + request_queue_.push(request); + request_mutex_.unlock(); + + sleep_condition_.notify_one(); +} + +void HttpClient::NetworkThread() +{ + while (true) + { + HttpRequestPtr request; + { + std::lock_guard lock(request_mutex_); + while (request_queue_.empty()) + { + sleep_condition_.wait(request_mutex_); + } + request = request_queue_.front(); + request_queue_.pop(); + } + + HttpResponsePtr response = new (std::nothrow) HttpResponse(request); + Perform(request, response); + + response_mutex_.lock(); + response_queue_.push(response); + response_mutex_.unlock(); + + Application::PreformInMainThread(Closure(this, &HttpClient::DispatchResponseCallback)); + } +} + +void HttpClient::Perform(HttpRequestPtr request, HttpResponsePtr response) +{ + bool ok = false; + long response_code = 0; + char error_message[256] = { 0 }; + ByteString response_header; + ByteString response_data; + + ByteString url = convert_to_utf8(request->GetUrl()); + ByteString data = convert_to_utf8(request->GetData()); + + Vector headers; + headers.reserve(request->GetHeaders().size()); + for (const auto& pair : request->GetHeaders()) + { + headers.push_back(wide_to_string(pair.first) + ":" + wide_to_string(pair.second)); + } + + switch (request->GetType()) + { + case HttpRequest::Type::Get: + ok = Curl::GetRequest(this, headers, url, &response_code, &response_data, &response_header, error_message); + break; + case HttpRequest::Type::Post: + ok = Curl::PostRequest(this, headers, url, data, &response_code, &response_data, &response_header, + error_message); + break; + case HttpRequest::Type::Put: + ok = + Curl::PutRequest(this, headers, url, data, &response_code, &response_data, &response_header, error_message); + break; + case HttpRequest::Type::Delete: + ok = Curl::DeleteRequest(this, headers, url, &response_code, &response_data, &response_header, error_message); + break; + default: + KGE_ERROR(L"HttpClient: unknown request type, only GET, POST, PUT or DELETE is supported"); + return; + } + + response->SetResponseCode(response_code); + response->SetHeader(MultiByteToWide(response_header)); + response->SetData(convert_from_utf8(response_data)); + if (!ok) + { + response->SetSucceed(false); + response->SetError(MultiByteToWide(error_message)); + } + else + { + response->SetSucceed(true); + } +} + +void HttpClient::DispatchResponseCallback() +{ + HttpResponsePtr response; + + response_mutex_.lock(); + if (!response_queue_.empty()) + { + response = response_queue_.front(); + response_queue_.pop(); + } + response_mutex_.unlock(); + + if (response) + { + HttpRequestPtr request = response->GetRequest(); + const auto& callback = request->GetResponseCallback(); + + if (callback) + { + callback(request.get(), response.get()); + } + } +} + +} // namespace network +} // namespace kiwano diff --git a/src/kiwano-network/HttpClient.h b/src/kiwano-network/HttpClient.h index 600252ae..c1a29b9b 100644 --- a/src/kiwano-network/HttpClient.h +++ b/src/kiwano-network/HttpClient.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -19,127 +19,126 @@ // THE SOFTWARE. #pragma once -#include +#include +#include #include #include -#include namespace kiwano { - namespace network - { - /** - * \~chinese - * \defgroup Network ͨ - */ +namespace network +{ +/** + * \~chinese + * \defgroup Network ͨ + */ - /** - * \addtogroup Network - * @{ - */ +/** + * \addtogroup Network + * @{ + */ - /** - * \~chinese - * @brief HTTPͻ - */ - class KGE_API HttpClient - : public Singleton - , public ComponentBase - { - friend Singleton; +/** + * \~chinese + * @brief HTTPͻ + */ +class KGE_API HttpClient + : public Singleton + , public ComponentBase +{ + friend Singleton; - public: - /// \~chinese - /// @brief HTTP - /// @param[in] request HTTP - /// @details ۽ʧܶӦص - void Send(HttpRequestPtr request); +public: + /// \~chinese + /// @brief HTTP + /// @param[in] request HTTP + /// @details ۽ʧܶӦص + void Send(HttpRequestPtr request); - /// \~chinese - /// @brief ӳʱʱ - void SetTimeoutForConnect(Duration timeout); + /// \~chinese + /// @brief ӳʱʱ + void SetTimeoutForConnect(Duration timeout); - /// \~chinese - /// @brief ȡӳʱʱ - Duration GetTimeoutForConnect() const; + /// \~chinese + /// @brief ȡӳʱʱ + Duration GetTimeoutForConnect() const; - /// \~chinese - /// @brief öȡʱʱ - void SetTimeoutForRead(Duration timeout); + /// \~chinese + /// @brief öȡʱʱ + void SetTimeoutForRead(Duration timeout); - /// \~chinese - /// @brief ȡȡʱʱ - Duration GetTimeoutForRead() const; + /// \~chinese + /// @brief ȡȡʱʱ + Duration GetTimeoutForRead() const; - /// \~chinese - /// @brief SSLַ֤ - void SetSSLVerification(String const& root_certificate_path); + /// \~chinese + /// @brief SSLַ֤ + void SetSSLVerification(String const& root_certificate_path); - /// \~chinese - /// @brief ȡSSLַ֤ - String const& GetSSLVerification() const; + /// \~chinese + /// @brief ȡSSLַ֤ + String const& GetSSLVerification() const; - public: - virtual void SetupComponent() override; +public: + virtual void SetupComponent() override; - virtual void DestroyComponent() override; + virtual void DestroyComponent() override; - private: - HttpClient(); +private: + HttpClient(); - void NetworkThread(); + void NetworkThread(); - void Perform(HttpRequestPtr request, HttpResponsePtr response); + void Perform(HttpRequestPtr request, HttpResponsePtr response); - void DispatchResponseCallback(); + void DispatchResponseCallback(); - private: - Duration timeout_for_connect_; - Duration timeout_for_read_; +private: + Duration timeout_for_connect_; + Duration timeout_for_read_; - String ssl_verification_; + String ssl_verification_; - std::mutex request_mutex_; - Queue request_queue_; + std::mutex request_mutex_; + Queue request_queue_; - std::mutex response_mutex_; - Queue response_queue_; + std::mutex response_mutex_; + Queue response_queue_; - std::condition_variable_any sleep_condition_; - }; + std::condition_variable_any sleep_condition_; +}; - /** @} */ +/** @} */ - - inline void HttpClient::SetTimeoutForConnect(Duration timeout) - { - timeout_for_connect_ = timeout; - } - - inline Duration HttpClient::GetTimeoutForConnect() const - { - return timeout_for_connect_; - } - - inline void HttpClient::SetTimeoutForRead(Duration timeout) - { - timeout_for_read_ = timeout; - } - - inline Duration HttpClient::GetTimeoutForRead() const - { - return timeout_for_read_; - } - - inline void HttpClient::SetSSLVerification(String const& root_certificate_path) - { - ssl_verification_ = root_certificate_path; - } - - inline String const& HttpClient::GetSSLVerification() const - { - return ssl_verification_; - } - - } +inline void HttpClient::SetTimeoutForConnect(Duration timeout) +{ + timeout_for_connect_ = timeout; } + +inline Duration HttpClient::GetTimeoutForConnect() const +{ + return timeout_for_connect_; +} + +inline void HttpClient::SetTimeoutForRead(Duration timeout) +{ + timeout_for_read_ = timeout; +} + +inline Duration HttpClient::GetTimeoutForRead() const +{ + return timeout_for_read_; +} + +inline void HttpClient::SetSSLVerification(String const& root_certificate_path) +{ + ssl_verification_ = root_certificate_path; +} + +inline String const& HttpClient::GetSSLVerification() const +{ + return ssl_verification_; +} + +} // namespace network +} // namespace kiwano diff --git a/src/kiwano-network/HttpRequest.cpp b/src/kiwano-network/HttpRequest.cpp index 610df352..6aacca61 100644 --- a/src/kiwano-network/HttpRequest.cpp +++ b/src/kiwano-network/HttpRequest.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -23,12 +23,12 @@ namespace kiwano { - namespace network - { - void HttpRequest::SetJsonData(Json const& json) - { - SetHeader(L"Content-Type", L"application/json;charset=UTF-8"); - data_ = json.dump(); - } - } +namespace network +{ +void HttpRequest::SetJsonData(Json const& json) +{ + SetHeader(L"Content-Type", L"application/json;charset=UTF-8"); + data_ = json.dump(); } +} // namespace network +} // namespace kiwano diff --git a/src/kiwano-network/HttpRequest.h b/src/kiwano-network/HttpRequest.h index 89dae6fa..cc01a9cb 100644 --- a/src/kiwano-network/HttpRequest.h +++ b/src/kiwano-network/HttpRequest.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -19,138 +19,179 @@ // THE SOFTWARE. #pragma once -#include +#include #include #include namespace kiwano { - namespace network - { - class HttpResponse; +namespace network +{ +class HttpResponse; - KGE_DECLARE_SMART_PTR(HttpRequest); +KGE_DECLARE_SMART_PTR(HttpRequest); - /** - * \addtogroup Network - * @{ - */ +/** + * \addtogroup Network + * @{ + */ - /** - * \~chinese - * @brief HTTP - */ - class KGE_API HttpRequest - : public ObjectBase - { - public: - /// \~chinese - /// @brief Ӧص - using ResponseCallback = Function; +/** + * \~chinese + * @brief HTTP + */ +class KGE_API HttpRequest : public virtual ObjectBase +{ +public: + /// \~chinese + /// @brief Ӧص + using ResponseCallback = Function; - /// \~chinese - /// @brief - enum class Type - { - Unknown, ///< δ֪ - Get, ///< HTTP GET - Post, ///< HTTP POST - Put, ///< HTTP PUT - Delete ///< HTTP DELETE - }; + /// \~chinese + /// @brief + enum class Type + { + Unknown, ///< δ֪ + Get, ///< HTTP GET + Post, ///< HTTP POST + Put, ///< HTTP PUT + Delete ///< HTTP DELETE + }; - HttpRequest(); + HttpRequest(); - HttpRequest(Type type); + HttpRequest(Type type); - /// \~chinese - /// @brief ַ - void SetUrl(String const& url); + /// \~chinese + /// @brief ַ + void SetUrl(String const& url); - /// \~chinese - /// @brief - void SetType(Type type); + /// \~chinese + /// @brief + void SetType(Type type); - /// \~chinese - /// @brief Я - void SetData(String const& data); + /// \~chinese + /// @brief Я + void SetData(String const& data); - /// \~chinese - /// @brief ЯJSON - void SetJsonData(Json const& json); + /// \~chinese + /// @brief ЯJSON + void SetJsonData(Json const& json); - /// \~chinese - /// @brief HTTPͷ - void SetHeaders(Map const& headers); + /// \~chinese + /// @brief HTTPͷ + void SetHeaders(Map const& headers); - /// \~chinese - /// @brief HTTPͷ - void SetHeader(String const& field, String const& content); + /// \~chinese + /// @brief HTTPͷ + void SetHeader(String const& field, String const& content); - /// \~chinese - /// @brief Ӧص - void SetResponseCallback(ResponseCallback const& callback); + /// \~chinese + /// @brief Ӧص + void SetResponseCallback(ResponseCallback const& callback); - /// \~chinese - /// @brief ȡַ - String const& GetUrl() const; + /// \~chinese + /// @brief ȡַ + String const& GetUrl() const; - /// \~chinese - /// @brief ȡ - Type GetType() const; + /// \~chinese + /// @brief ȡ + Type GetType() const; - /// \~chinese - /// @brief ȡ - String const& GetData() const; + /// \~chinese + /// @brief ȡ + String const& GetData() const; - /// \~chinese - /// @brief ȡHTTPͷ - Map& GetHeaders(); + /// \~chinese + /// @brief ȡHTTPͷ + Map& GetHeaders(); - /// \~chinese - /// @brief ȡHTTPͷ - String const& GetHeader(String const& header) const; + /// \~chinese + /// @brief ȡHTTPͷ + String const& GetHeader(String const& header) const; - /// \~chinese - /// @brief ȡӦص - ResponseCallback const& GetResponseCallback() const; + /// \~chinese + /// @brief ȡӦص + ResponseCallback const& GetResponseCallback() const; - private: - Type type_; - String url_; - String data_; - Map headers_; - ResponseCallback response_cb_; - }; +private: + Type type_; + String url_; + String data_; + Map headers_; + ResponseCallback response_cb_; +}; - /** @} */ +/** @} */ - inline HttpRequest::HttpRequest() : type_(Type::Unknown) {} - - inline HttpRequest::HttpRequest(Type type) : type_(type) {} - - inline void HttpRequest::SetUrl(String const& url) { url_ = url; } - - inline String const& HttpRequest::GetUrl() const { return url_; } - - inline void HttpRequest::SetType(Type type) { type_ = type; } - - inline HttpRequest::Type HttpRequest::GetType() const { return type_; } - - inline void HttpRequest::SetData(String const& data) { data_ = data; } - - inline String const& HttpRequest::GetData() const { return data_; } - - inline void HttpRequest::SetHeaders(Map const& headers) { headers_ = headers; } - - inline void HttpRequest::SetHeader(String const& field, String const& content) { headers_[field] = content; } - - inline Map& HttpRequest::GetHeaders() { return headers_; } - - inline String const& HttpRequest::GetHeader(String const& header) const { return headers_.at(header); } - - inline void HttpRequest::SetResponseCallback(ResponseCallback const& callback) { response_cb_ = callback; } - - inline HttpRequest::ResponseCallback const& HttpRequest::GetResponseCallback() const { return response_cb_; } - } +inline HttpRequest::HttpRequest() + : type_(Type::Unknown) +{ } + +inline HttpRequest::HttpRequest(Type type) + : type_(type) +{ +} + +inline void HttpRequest::SetUrl(String const& url) +{ + url_ = url; +} + +inline String const& HttpRequest::GetUrl() const +{ + return url_; +} + +inline void HttpRequest::SetType(Type type) +{ + type_ = type; +} + +inline HttpRequest::Type HttpRequest::GetType() const +{ + return type_; +} + +inline void HttpRequest::SetData(String const& data) +{ + data_ = data; +} + +inline String const& HttpRequest::GetData() const +{ + return data_; +} + +inline void HttpRequest::SetHeaders(Map const& headers) +{ + headers_ = headers; +} + +inline void HttpRequest::SetHeader(String const& field, String const& content) +{ + headers_[field] = content; +} + +inline Map& HttpRequest::GetHeaders() +{ + return headers_; +} + +inline String const& HttpRequest::GetHeader(String const& header) const +{ + return headers_.at(header); +} + +inline void HttpRequest::SetResponseCallback(ResponseCallback const& callback) +{ + response_cb_ = callback; +} + +inline HttpRequest::ResponseCallback const& HttpRequest::GetResponseCallback() const +{ + return response_cb_; +} +} // namespace network +} // namespace kiwano diff --git a/src/kiwano-network/HttpResponse.hpp b/src/kiwano-network/HttpResponse.hpp index 14f80831..0c91baa2 100644 --- a/src/kiwano-network/HttpResponse.hpp +++ b/src/kiwano-network/HttpResponse.hpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -23,103 +23,140 @@ namespace kiwano { - namespace network - { - KGE_DECLARE_SMART_PTR(HttpResponse); +namespace network +{ +KGE_DECLARE_SMART_PTR(HttpResponse); - /** - * \addtogroup Network - * @{ - */ +/** + * \addtogroup Network + * @{ + */ - /** - * \~chinese - * @brief HTTPӦ - */ - class KGE_API HttpResponse - : public ObjectBase - { - public: - HttpResponse(HttpRequestPtr request); +/** + * \~chinese + * @brief HTTPӦ + */ +class KGE_API HttpResponse : public virtual ObjectBase +{ +public: + HttpResponse(HttpRequestPtr request); - /// \~chinese - /// @brief ȡӦHTTP - HttpRequestPtr GetRequest() const; + /// \~chinese + /// @brief ȡӦHTTP + HttpRequestPtr GetRequest() const; - /// \~chinese - /// @brief ȡӦ״̬ - bool IsSucceed() const; + /// \~chinese + /// @brief ȡӦ״̬ + bool IsSucceed() const; - /// \~chinese - /// @brief ȡHTTP״̬ - long GetResponseCode() const; + /// \~chinese + /// @brief ȡHTTP״̬ + long GetResponseCode() const; - /// \~chinese - /// @brief ȡӦͷ - String GetHeader() const; + /// \~chinese + /// @brief ȡӦͷ + String GetHeader() const; - /// \~chinese - /// @brief ȡӦ - String const& GetData() const; + /// \~chinese + /// @brief ȡӦ + String const& GetData() const; - /// \~chinese - /// @brief ȡϢ - String const& GetError() const; + /// \~chinese + /// @brief ȡϢ + String const& GetError() const; - /// \~chinese - /// @brief Ӧ״̬ - void SetSucceed(bool succeed); + /// \~chinese + /// @brief Ӧ״̬ + void SetSucceed(bool succeed); - /// \~chinese - /// @brief HTTP״̬ - void SetResponseCode(long response_code); + /// \~chinese + /// @brief HTTP״̬ + void SetResponseCode(long response_code); - /// \~chinese - /// @brief Ӧͷ - void SetHeader(String const& response_header); + /// \~chinese + /// @brief Ӧͷ + void SetHeader(String const& response_header); - /// \~chinese - /// @brief Ӧ - void SetData(String const& response_data); + /// \~chinese + /// @brief Ӧ + void SetData(String const& response_data); - /// \~chinese - /// @brief ôϢ - void SetError(String const& error_buffer); + /// \~chinese + /// @brief ôϢ + void SetError(String const& error_buffer); - private: - bool succeed_; - long response_code_; - HttpRequestPtr request_; +private: + bool succeed_; + long response_code_; + HttpRequestPtr request_; - String response_header_; - String response_data_; - String error_buffer_; - }; + String response_header_; + String response_data_; + String error_buffer_; +}; - /** @} */ +/** @} */ - inline HttpResponse::HttpResponse(HttpRequestPtr request) : request_(request), succeed_(false), response_code_(0) {} - - inline HttpRequestPtr HttpResponse::GetRequest() const { return request_; } - - inline void HttpResponse::SetSucceed(bool succeed) { succeed_ = succeed; } - - inline bool HttpResponse::IsSucceed() const { return succeed_; } - - inline void HttpResponse::SetResponseCode(long response_code) { response_code_ = response_code; } - - inline long HttpResponse::GetResponseCode() const { return response_code_; } - - inline void HttpResponse::SetHeader(String const& response_header) { response_header_ = response_header; } - - inline String HttpResponse::GetHeader() const { return response_header_; } - - inline void HttpResponse::SetData(String const& response_data) { response_data_ = response_data; } - - inline String const& HttpResponse::GetData() const { return response_data_; } - - inline void HttpResponse::SetError(String const& error_buffer) { error_buffer_ = error_buffer; } - - inline String const& HttpResponse::GetError() const { return error_buffer_; } - } +inline HttpResponse::HttpResponse(HttpRequestPtr request) + : request_(request) + , succeed_(false) + , response_code_(0) +{ } + +inline HttpRequestPtr HttpResponse::GetRequest() const +{ + return request_; +} + +inline void HttpResponse::SetSucceed(bool succeed) +{ + succeed_ = succeed; +} + +inline bool HttpResponse::IsSucceed() const +{ + return succeed_; +} + +inline void HttpResponse::SetResponseCode(long response_code) +{ + response_code_ = response_code; +} + +inline long HttpResponse::GetResponseCode() const +{ + return response_code_; +} + +inline void HttpResponse::SetHeader(String const& response_header) +{ + response_header_ = response_header; +} + +inline String HttpResponse::GetHeader() const +{ + return response_header_; +} + +inline void HttpResponse::SetData(String const& response_data) +{ + response_data_ = response_data; +} + +inline String const& HttpResponse::GetData() const +{ + return response_data_; +} + +inline void HttpResponse::SetError(String const& error_buffer) +{ + error_buffer_ = error_buffer; +} + +inline String const& HttpResponse::GetError() const +{ + return error_buffer_; +} +} // namespace network +} // namespace kiwano diff --git a/src/kiwano-network/kiwano-network.h b/src/kiwano-network/kiwano-network.h index e1078261..eb134a7a 100644 --- a/src/kiwano-network/kiwano-network.h +++ b/src/kiwano-network/kiwano-network.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -20,6 +20,6 @@ #pragma once +#include #include #include -#include diff --git a/src/kiwano-physics/Body.cpp b/src/kiwano-physics/Body.cpp index 37cf93e4..7698824b 100644 --- a/src/kiwano-physics/Body.cpp +++ b/src/kiwano-physics/Body.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -23,291 +23,280 @@ namespace kiwano { - namespace physics - { - - Body::Body() - : body_(nullptr) - , actor_(nullptr) - , world_(nullptr) - , category_bits_(0x0001) - , mask_bits_(0xFFFF) - , group_index_(0) - { - } - - Body::Body(b2Body* body, Actor* actor) - : Body() - { - SetB2Body(body); - SetActor(actor); - } - - Body::Body(World* world, Actor* actor) - : Body() - { - Init(world, actor); - } - - Body::~Body() - { - Destroy(); - } - - void Body::Init(World* world, Actor* actor) - { - KGE_ASSERT(world); - - Destroy(); - - world_ = world; - b2BodyDef def; - b2Body* b2body = world->GetB2World()->CreateBody(&def); - - SetB2Body(b2body); - SetActor(actor); - UpdateFromActor(); - } - - Fixture Body::AddFixture(Shape* shape, const Fixture::Param& param) - { - KGE_ASSERT(body_ && world_); - return Fixture(this, shape, param); - } - - Fixture Body::AddCircleShape(float radius, float density) - { - return AddFixture(&CircleShape(radius), Fixture::Param(density)); - } - - Fixture Body::AddBoxShape(Vec2 const& size, float density) - { - return AddFixture(&BoxShape(size), Fixture::Param(density)); - } - - Fixture Body::AddPolygonShape(Vector const& vertexs, float density) - { - return AddFixture(&PolygonShape(vertexs), Fixture::Param(density)); - } - - Fixture Body::AddEdgeShape(Point const& p1, Point const& p2, float density) - { - return AddFixture(&EdgeShape(p1, p2), Fixture::Param(density)); - } - - Fixture Body::AddChainShape(Vector const& vertexs, bool loop, float density) - { - return AddFixture(&ChainShape(vertexs, loop), Fixture::Param(density)); - } - - void Body::RemoveFixture(Fixture const& fixture) - { - if (fixture.GetB2Fixture()) - { - body_->DestroyFixture(fixture.GetB2Fixture()); - } - } - - void Body::SetCategoryBits(uint16_t category_bits) - { - KGE_ASSERT(body_); - - if (category_bits != category_bits_) - { - category_bits_ = category_bits; - - b2Fixture* fixture = body_->GetFixtureList(); - while (fixture) - { - UpdateFixtureFilter(fixture); - fixture = fixture->GetNext(); - } - } - } - - void Body::SetMaskBits(uint16_t mask_bits) - { - KGE_ASSERT(body_); - - if (mask_bits != mask_bits_) - { - mask_bits_ = mask_bits; - - b2Fixture* fixture = body_->GetFixtureList(); - while (fixture) - { - UpdateFixtureFilter(fixture); - fixture = fixture->GetNext(); - } - } - } - - void Body::SetGroupIndex(int16_t index) - { - KGE_ASSERT(body_); - - if (index != group_index_) - { - group_index_ = index; - - b2Fixture* fixture = body_->GetFixtureList(); - while (fixture) - { - UpdateFixtureFilter(fixture); - fixture = fixture->GetNext(); - } - } - } - - void Body::GetMassData(float* mass, Point* center, float* inertia) const - { - KGE_ASSERT(body_ && world_); - - b2MassData data; - body_->GetMassData(&data); - - if (mass) *mass = data.mass; - if (center) *center = world_->World2Stage(data.center); - if (inertia) *inertia = data.I; - } - - void Body::SetMassData(float mass, Point const& center, float inertia) - { - KGE_ASSERT(body_ && world_); - - b2MassData data; - data.mass = mass; - data.center = world_->Stage2World(center); - data.I = inertia; - body_->SetMassData(&data); - } - - void Body::ResetMassData() - { - KGE_ASSERT(body_); - body_->ResetMassData(); - } - - Point Body::GetBodyPosition() const - { - KGE_ASSERT(body_ && world_); - return world_->World2Stage(body_->GetPosition()); - } - - void Body::SetBodyTransform(Point const& pos, float angle) - { - KGE_ASSERT(body_ && world_); - body_->SetTransform(world_->Stage2World(pos), math::Degree2Radian(angle)); - } - - Point Body::GetLocalPoint(Point const& world) const - { - KGE_ASSERT(body_ && world_); - return world_->World2Stage(body_->GetLocalPoint(world_->Stage2World(world))); - } - - Point Body::GetWorldPoint(Point const& local) const - { - KGE_ASSERT(body_ && world_); - return world_->World2Stage(body_->GetWorldPoint(world_->Stage2World(local))); - } - - Point Body::GetLocalCenter() const - { - KGE_ASSERT(body_ && world_); - return world_->World2Stage(body_->GetLocalCenter()); - } - - Point Body::GetWorldCenter() const - { - KGE_ASSERT(body_ && world_); - return world_->World2Stage(body_->GetWorldCenter()); - } - - void Body::ApplyForce(Vec2 const& force, Point const& point, bool wake) - { - KGE_ASSERT(body_ && world_); - body_->ApplyForce(b2Vec2(force.x, force.y), world_->Stage2World(point), wake); - } - - void Body::ApplyForceToCenter(Vec2 const& force, bool wake) - { - KGE_ASSERT(body_ && world_); - body_->ApplyForceToCenter(b2Vec2(force.x, force.y), wake); - } - - void Body::ApplyTorque(float torque, bool wake) - { - KGE_ASSERT(body_ && world_); - body_->ApplyTorque(torque, wake); - } - - void Body::SetB2Body(b2Body* body) - { - body_ = body; - if (body_) - { - body_->SetUserData(this); - } - } - - void Body::Destroy() - { - if (world_ && body_) - { - world_->RemoveBody(this); - } - - body_ = nullptr; - world_ = nullptr; - actor_ = nullptr; - } - - void Body::UpdateActor() - { - if (actor_ && body_) - { - if (world_) - { - actor_->SetPosition(world_->World2Stage(body_->GetPosition())); - } - else - { - actor_->SetPosition(World2Stage(body_->GetPosition())); - } - actor_->SetRotation(math::Radian2Degree(body_->GetAngle())); - } - } - - void Body::UpdateFromActor() - { - if (actor_ && body_) - { - if (world_) - { - body_->SetTransform( - world_->Stage2World(actor_->GetPosition()), - math::Degree2Radian(actor_->GetRotation()) - ); - } - else - { - body_->SetTransform( - Stage2World(actor_->GetPosition()), - math::Degree2Radian(actor_->GetRotation()) - ); - } - } - } - - void Body::UpdateFixtureFilter(b2Fixture* fixture) - { - b2Filter filter; - filter.categoryBits = category_bits_; - filter.maskBits = mask_bits_; - filter.groupIndex = group_index_; - fixture->SetFilterData(filter); - } +namespace physics +{ +Body::Body() + : body_(nullptr) + , actor_(nullptr) + , world_(nullptr) + , category_bits_(0x0001) + , mask_bits_(0xFFFF) + , group_index_(0) +{ } + +Body::~Body() +{ + Destroy(); } + +bool Body::InitBody(World* world, Actor* actor) +{ + KGE_ASSERT(world); + + Destroy(); + + world_ = world; + b2BodyDef def; + b2Body* b2body = world->GetB2World()->CreateBody(&def); + + if (b2body) + { + SetB2Body(b2body); + SetActor(actor); + UpdateFromActor(); + return true; + } + return false; +} + +Fixture Body::AddFixture(Shape* shape, const Fixture::Param& param) +{ + KGE_ASSERT(body_ && world_); + return Fixture(this, shape, param); +} + +Fixture Body::AddCircleShape(float radius, float density) +{ + return AddFixture(&CircleShape(radius), Fixture::Param(density)); +} + +Fixture Body::AddBoxShape(Vec2 const& size, float density) +{ + return AddFixture(&BoxShape(size), Fixture::Param(density)); +} + +Fixture Body::AddPolygonShape(Vector const& vertexs, float density) +{ + return AddFixture(&PolygonShape(vertexs), Fixture::Param(density)); +} + +Fixture Body::AddEdgeShape(Point const& p1, Point const& p2, float density) +{ + return AddFixture(&EdgeShape(p1, p2), Fixture::Param(density)); +} + +Fixture Body::AddChainShape(Vector const& vertexs, bool loop, float density) +{ + return AddFixture(&ChainShape(vertexs, loop), Fixture::Param(density)); +} + +void Body::RemoveFixture(Fixture const& fixture) +{ + if (fixture.GetB2Fixture()) + { + body_->DestroyFixture(fixture.GetB2Fixture()); + } +} + +void Body::SetCategoryBits(uint16_t category_bits) +{ + KGE_ASSERT(body_); + + if (category_bits != category_bits_) + { + category_bits_ = category_bits; + + b2Fixture* fixture = body_->GetFixtureList(); + while (fixture) + { + UpdateFixtureFilter(fixture); + fixture = fixture->GetNext(); + } + } +} + +void Body::SetMaskBits(uint16_t mask_bits) +{ + KGE_ASSERT(body_); + + if (mask_bits != mask_bits_) + { + mask_bits_ = mask_bits; + + b2Fixture* fixture = body_->GetFixtureList(); + while (fixture) + { + UpdateFixtureFilter(fixture); + fixture = fixture->GetNext(); + } + } +} + +void Body::SetGroupIndex(int16_t index) +{ + KGE_ASSERT(body_); + + if (index != group_index_) + { + group_index_ = index; + + b2Fixture* fixture = body_->GetFixtureList(); + while (fixture) + { + UpdateFixtureFilter(fixture); + fixture = fixture->GetNext(); + } + } +} + +void Body::GetMassData(float* mass, Point* center, float* inertia) const +{ + KGE_ASSERT(body_ && world_); + + b2MassData data; + body_->GetMassData(&data); + + if (mass) + *mass = data.mass; + if (center) + *center = world_->World2Stage(data.center); + if (inertia) + *inertia = data.I; +} + +void Body::SetMassData(float mass, Point const& center, float inertia) +{ + KGE_ASSERT(body_ && world_); + + b2MassData data; + data.mass = mass; + data.center = world_->Stage2World(center); + data.I = inertia; + body_->SetMassData(&data); +} + +void Body::ResetMassData() +{ + KGE_ASSERT(body_); + body_->ResetMassData(); +} + +Point Body::GetBodyPosition() const +{ + KGE_ASSERT(body_ && world_); + return world_->World2Stage(body_->GetPosition()); +} + +void Body::SetBodyTransform(Point const& pos, float angle) +{ + KGE_ASSERT(body_ && world_); + body_->SetTransform(world_->Stage2World(pos), math::Degree2Radian(angle)); +} + +Point Body::GetLocalPoint(Point const& world) const +{ + KGE_ASSERT(body_ && world_); + return world_->World2Stage(body_->GetLocalPoint(world_->Stage2World(world))); +} + +Point Body::GetWorldPoint(Point const& local) const +{ + KGE_ASSERT(body_ && world_); + return world_->World2Stage(body_->GetWorldPoint(world_->Stage2World(local))); +} + +Point Body::GetLocalCenter() const +{ + KGE_ASSERT(body_ && world_); + return world_->World2Stage(body_->GetLocalCenter()); +} + +Point Body::GetWorldCenter() const +{ + KGE_ASSERT(body_ && world_); + return world_->World2Stage(body_->GetWorldCenter()); +} + +void Body::ApplyForce(Vec2 const& force, Point const& point, bool wake) +{ + KGE_ASSERT(body_ && world_); + body_->ApplyForce(b2Vec2(force.x, force.y), world_->Stage2World(point), wake); +} + +void Body::ApplyForceToCenter(Vec2 const& force, bool wake) +{ + KGE_ASSERT(body_ && world_); + body_->ApplyForceToCenter(b2Vec2(force.x, force.y), wake); +} + +void Body::ApplyTorque(float torque, bool wake) +{ + KGE_ASSERT(body_ && world_); + body_->ApplyTorque(torque, wake); +} + +void Body::SetB2Body(b2Body* body) +{ + body_ = body; + if (body_) + { + body_->SetUserData(this); + } +} + +void Body::Destroy() +{ + if (world_) + { + world_->RemoveBody(this); + } + + body_ = nullptr; + world_ = nullptr; + actor_ = nullptr; +} + +void Body::UpdateActor() +{ + if (actor_ && body_) + { + if (world_) + { + actor_->SetPosition(world_->World2Stage(body_->GetPosition())); + } + else + { + actor_->SetPosition(World2Stage(body_->GetPosition())); + } + actor_->SetRotation(math::Radian2Degree(body_->GetAngle())); + } +} + +void Body::UpdateFromActor() +{ + if (actor_ && body_) + { + if (world_) + { + body_->SetTransform(world_->Stage2World(actor_->GetPosition()), math::Degree2Radian(actor_->GetRotation())); + } + else + { + body_->SetTransform(Stage2World(actor_->GetPosition()), math::Degree2Radian(actor_->GetRotation())); + } + } +} + +void Body::UpdateFixtureFilter(b2Fixture* fixture) +{ + b2Filter filter; + filter.categoryBits = category_bits_; + filter.maskBits = mask_bits_; + filter.groupIndex = group_index_; + fixture->SetFilterData(filter); +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/Body.h b/src/kiwano-physics/Body.h index b8049afd..25fd0c8e 100644 --- a/src/kiwano-physics/Body.h +++ b/src/kiwano-physics/Body.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -19,362 +19,471 @@ // THE SOFTWARE. #pragma once -#include -#include -#include #include +#include +#include +#include namespace kiwano { - namespace physics - { - class World; - - KGE_DECLARE_SMART_PTR(Body); - - /** - * \addtogroup Physics - * @{ - */ - - /// \~chinese - /// @brief - class KGE_API Body - : public virtual RefCounter - { - public: - /// \~chinese - /// @brief - enum class Type - { - Static = 0, ///< ̬ - Kinematic, ///< ѧ - Dynamic, ///< ̬ - }; - - Body(); - Body(b2Body* body, Actor* actor); - Body(World* world, Actor* actor); - Body(World* world, ActorPtr actor); - virtual ~Body(); - - /// \~chinese - /// @brief ʼ - /// @param[in] world - /// @param[in] actor 󶨵Ľɫ - void Init(World* world, Actor* actor); - - /// \~chinese - /// @brief Ӽо - /// @param shape ״ - /// @param density ܶ - Fixture AddFixture(Shape* shape, const Fixture::Param& param); - - /// \~chinese - /// @brief Բμо - /// @param radius Բΰ뾶 - /// @param density ܶ - Fixture AddCircleShape(float radius, float density = 0.f); - - /// \~chinese - /// @brief Ӻμо - /// @param size дС - /// @param density ܶ - Fixture AddBoxShape(Vec2 const& size, float density = 0.f); - - /// \~chinese - /// @brief Ӷμо - /// @param vertexs ζ˵ - /// @param density ܶ - Fixture AddPolygonShape(Vector const& vertexs, float density = 0.f); - - /// \~chinese - /// @brief ߶μо - /// @param p1 ߶ - /// @param p2 ߶յ - /// @param density ܶ - Fixture AddEdgeShape(Point const& p1, Point const& p2, float density = 0.f); - - /// \~chinese - /// @brief μо - /// @param vertexs ˵ - /// @param loop Ƿպ - /// @param density ܶ - Fixture AddChainShape(Vector const& vertexs, bool loop, float density = 0.f); - - /// \~chinese - /// @brief ȡоб - FixtureList GetFixtureList() const; - - /// \~chinese - /// @brief Ƴо - void RemoveFixture(Fixture const& fixture); - - /// \~chinese - /// @brief ȡӴб - ContactEdgeList GetContactList() const; - - /// \~chinese - /// @brief ȡ - uint16_t GetCategoryBits() const; - - /// \~chinese - /// @brief - void SetCategoryBits(uint16_t category_bits); - - /// \~chinese - /// @brief ȡײ - uint16_t GetMaskBits() const; - - /// \~chinese - /// @brief ײ - void SetMaskBits(uint16_t mask_bits); - - /// \~chinese - /// @brief ȡ - int16_t GetGroupIndex() const; - - /// \~chinese - /// @brief - void SetGroupIndex(int16_t index); - - /// \~chinese - /// @brief ȡתǶ - float GetBodyRotation() const; - - /// \~chinese - /// @brief תǶ - void SetBodyRotation(float angle); - - /// \~chinese - /// @brief ȡλ - Point GetBodyPosition() const; - - /// \~chinese - /// @brief λ - void SetBodyPosition(Point const& pos); - - /// \~chinese - /// @brief λúת任 - void SetBodyTransform(Point const& pos, float angle); - - /// \~chinese - /// @brief ȡ [kg] - float GetMass() const; - - /// \~chinese - /// @brief ȡ - float GetInertia() const; - - /// \~chinese - /// @brief ȡ - /// @param[out] mass [kg] - /// @param[out] center λ - /// @param[out] inertia - void GetMassData(float* mass, Point* center, float* inertia) const; - - /// \~chinese - /// @brief - /// @param mass [kg] - /// @param center λ - /// @param inertia - void SetMassData(float mass, Point const& center, float inertia); - - /// \~chinese - /// @brief - void ResetMassData(); - - /// \~chinese - /// @brief ȡϵϵĵϵλ - Point GetLocalPoint(Point const& world) const; - - /// \~chinese - /// @brief ȡϵĵϵλ - Point GetWorldPoint(Point const& local) const; - - /// \~chinese - /// @brief ȡλ - Point GetLocalCenter() const; - - /// \~chinese - /// @brief ȡλ - Point GetWorldCenter() const; - - /// \~chinese - /// @brief ȡ - Type GetType() const; - - /// \~chinese - /// @brief - void SetType(Type type); - - /// \~chinese - /// @brief ȡı - float GetGravityScale() const; - - /// \~chinese - /// @brief ı - void SetGravityScale(float scale); - - /// \~chinese - /// @brief ʩ - /// @param force ĴСͷ - /// @param point ʩ - /// @param wake Ƿ - void ApplyForce(Vec2 const& force, Point const& point, bool wake = true); - - /// \~chinese - /// @brief ʩ - /// @param force ĴСͷ - /// @param wake Ƿ - void ApplyForceToCenter(Vec2 const& force, bool wake = true); - - /// \~chinese - /// @brief ʩŤ - /// @param torque Ť - /// @param wake Ƿ - void ApplyTorque(float torque, bool wake = false); - - /// \~chinese - /// @brief תǶǷ̶ - bool IsIgnoreRotation() const; - - /// \~chinese - /// @brief Ƿ̶תǶ - void SetIgnoreRotation(bool flag); - - /// \~chinese - /// @brief Ƿӵ - bool IsBullet() const; - - /// \~chinese - /// @brief Ƿӵ - void SetBullet(bool flag); - - /// \~chinese - /// @brief Ƿڻ״̬ - bool IsAwake() const; - - /// \~chinese - /// @brief û״̬ - void SetAwake(bool flag); - - /// \~chinese - /// @brief Ƿ - bool IsSleepingAllowed() const; - - /// \~chinese - /// @brief Ƿ - void SetSleepingAllowed(bool flag); - - /// \~chinese - /// @brief Ƿ - bool IsActive() const; - - /// \~chinese - /// @brief ״̬ - void SetActive(bool flag); - - /// \~chinese - /// @brief ȡ - World* GetWorld() const; - - /// \~chinese - /// @brief ȡ󶨵Ľɫ - Actor* GetActor() const; - - /// \~chinese - /// @brief 󶨵Ľɫ - void SetActor(Actor* actor); - - /// \~chinese - /// @brief Ϣµɫ - void UpdateActor(); - - /// \~chinese - /// @brief ɫϢµ - void UpdateFromActor(); - - b2Body* GetB2Body() const; - void SetB2Body(b2Body* body); - - private: - /// \~chinese - /// @brief - void UpdateFixtureFilter(b2Fixture* fixture); - - /// \~chinese - /// @brief - void Destroy(); - - private: - Actor* actor_; - World* world_; - b2Body* body_; - - uint16_t category_bits_; - uint16_t mask_bits_; - int16_t group_index_; - }; - - /** @} */ - - inline Body::Body(World* world, ActorPtr actor) : Body(world, actor.get()) {} - - inline FixtureList Body::GetFixtureList() const { KGE_ASSERT(body_); return FixtureList(Fixture(body_->GetFixtureList())); } - - inline ContactEdgeList Body::GetContactList() const { KGE_ASSERT(body_); return ContactEdgeList(ContactEdge(body_->GetContactList())); } - - inline uint16_t Body::GetCategoryBits() const { return category_bits_; } - - inline uint16_t Body::GetMaskBits() const { return mask_bits_; } - - inline int16_t Body::GetGroupIndex() const { return group_index_; } - - inline float Body::GetBodyRotation() const { KGE_ASSERT(body_); return math::Radian2Degree(body_->GetAngle()); } - - inline void Body::SetBodyRotation(float angle) { SetBodyTransform(GetBodyPosition(), angle); } - - inline void Body::SetBodyPosition(Point const& pos) { SetBodyTransform(pos, GetBodyRotation()); } - - inline float Body::GetMass() const { KGE_ASSERT(body_); return body_->GetMass(); } - - inline float Body::GetInertia() const { KGE_ASSERT(body_); return body_->GetInertia(); } - - inline Body::Type Body::GetType() const { KGE_ASSERT(body_); return Type(body_->GetType()); } - - inline void Body::SetType(Type type) { KGE_ASSERT(body_); body_->SetType(static_cast(type)); } - - inline float Body::GetGravityScale() const { KGE_ASSERT(body_); return body_->GetGravityScale(); } - - inline void Body::SetGravityScale(float scale) { KGE_ASSERT(body_); body_->SetGravityScale(scale); } - - inline bool Body::IsIgnoreRotation() const { KGE_ASSERT(body_); return body_->IsFixedRotation(); } - - inline void Body::SetIgnoreRotation(bool flag) { KGE_ASSERT(body_); body_->SetFixedRotation(flag); } - - inline bool Body::IsBullet() const { KGE_ASSERT(body_); return body_->IsBullet(); } - - inline void Body::SetBullet(bool flag) { KGE_ASSERT(body_); body_->SetBullet(flag); } - - inline bool Body::IsAwake() const { KGE_ASSERT(body_); return body_->IsAwake(); } - - inline void Body::SetAwake(bool flag) { KGE_ASSERT(body_); body_->SetAwake(flag); } - - inline bool Body::IsSleepingAllowed() const { KGE_ASSERT(body_); return body_->IsSleepingAllowed(); } - - inline void Body::SetSleepingAllowed(bool flag) { KGE_ASSERT(body_); body_->SetSleepingAllowed(flag); } - - inline bool Body::IsActive() const { KGE_ASSERT(body_); return body_->IsActive(); } - - inline void Body::SetActive(bool flag) { KGE_ASSERT(body_); body_->SetActive(flag); } - - inline Actor* Body::GetActor() const { return actor_; } - - inline void Body::SetActor(Actor* actor) { actor_ = actor; } - - inline b2Body* Body::GetB2Body() const { return body_; } - - inline World* Body::GetWorld() const { return world_; } - } +namespace physics +{ +class World; + +KGE_DECLARE_SMART_PTR(Body); + +/** + * \addtogroup Physics + * @{ + */ + +/// \~chinese +/// @brief +class KGE_API Body : public virtual ObjectBase +{ +public: + /// \~chinese + /// @brief + enum class Type + { + Static = 0, ///< ̬ + Kinematic, ///< ѧ + Dynamic, ///< ̬ + }; + + Body(); + + virtual ~Body(); + + /// \~chinese + /// @brief ʼ + /// @param[in] world + /// @param[in] actor 󶨵Ľɫ + bool InitBody(World* world, ActorPtr actor); + + /// \~chinese + /// @brief ʼ + /// @param[in] world + /// @param[in] actor 󶨵Ľɫ + bool InitBody(World* world, Actor* actor); + + /// \~chinese + /// @brief Ӽо + /// @param shape ״ + /// @param density ܶ + Fixture AddFixture(Shape* shape, const Fixture::Param& param); + + /// \~chinese + /// @brief Բμо + /// @param radius Բΰ뾶 + /// @param density ܶ + Fixture AddCircleShape(float radius, float density = 0.f); + + /// \~chinese + /// @brief Ӻμо + /// @param size дС + /// @param density ܶ + Fixture AddBoxShape(Vec2 const& size, float density = 0.f); + + /// \~chinese + /// @brief Ӷμо + /// @param vertexs ζ˵ + /// @param density ܶ + Fixture AddPolygonShape(Vector const& vertexs, float density = 0.f); + + /// \~chinese + /// @brief ߶μо + /// @param p1 ߶ + /// @param p2 ߶յ + /// @param density ܶ + Fixture AddEdgeShape(Point const& p1, Point const& p2, float density = 0.f); + + /// \~chinese + /// @brief μо + /// @param vertexs ˵ + /// @param loop Ƿպ + /// @param density ܶ + Fixture AddChainShape(Vector const& vertexs, bool loop, float density = 0.f); + + /// \~chinese + /// @brief ȡоб + FixtureList GetFixtureList() const; + + /// \~chinese + /// @brief Ƴо + void RemoveFixture(Fixture const& fixture); + + /// \~chinese + /// @brief ȡӴб + ContactEdgeList GetContactList() const; + + /// \~chinese + /// @brief ȡ + uint16_t GetCategoryBits() const; + + /// \~chinese + /// @brief + void SetCategoryBits(uint16_t category_bits); + + /// \~chinese + /// @brief ȡײ + uint16_t GetMaskBits() const; + + /// \~chinese + /// @brief ײ + void SetMaskBits(uint16_t mask_bits); + + /// \~chinese + /// @brief ȡ + int16_t GetGroupIndex() const; + + /// \~chinese + /// @brief + void SetGroupIndex(int16_t index); + + /// \~chinese + /// @brief ȡתǶ + float GetBodyRotation() const; + + /// \~chinese + /// @brief תǶ + void SetBodyRotation(float angle); + + /// \~chinese + /// @brief ȡλ + Point GetBodyPosition() const; + + /// \~chinese + /// @brief λ + void SetBodyPosition(Point const& pos); + + /// \~chinese + /// @brief λúת任 + void SetBodyTransform(Point const& pos, float angle); + + /// \~chinese + /// @brief ȡ [kg] + float GetMass() const; + + /// \~chinese + /// @brief ȡ + float GetInertia() const; + + /// \~chinese + /// @brief ȡ + /// @param[out] mass [kg] + /// @param[out] center λ + /// @param[out] inertia + void GetMassData(float* mass, Point* center, float* inertia) const; + + /// \~chinese + /// @brief + /// @param mass [kg] + /// @param center λ + /// @param inertia + void SetMassData(float mass, Point const& center, float inertia); + + /// \~chinese + /// @brief + void ResetMassData(); + + /// \~chinese + /// @brief ȡϵϵĵϵλ + Point GetLocalPoint(Point const& world) const; + + /// \~chinese + /// @brief ȡϵĵϵλ + Point GetWorldPoint(Point const& local) const; + + /// \~chinese + /// @brief ȡλ + Point GetLocalCenter() const; + + /// \~chinese + /// @brief ȡλ + Point GetWorldCenter() const; + + /// \~chinese + /// @brief ȡ + Type GetType() const; + + /// \~chinese + /// @brief + void SetType(Type type); + + /// \~chinese + /// @brief ȡı + float GetGravityScale() const; + + /// \~chinese + /// @brief ı + void SetGravityScale(float scale); + + /// \~chinese + /// @brief ʩ + /// @param force ĴСͷ + /// @param point ʩ + /// @param wake Ƿ + void ApplyForce(Vec2 const& force, Point const& point, bool wake = true); + + /// \~chinese + /// @brief ʩ + /// @param force ĴСͷ + /// @param wake Ƿ + void ApplyForceToCenter(Vec2 const& force, bool wake = true); + + /// \~chinese + /// @brief ʩŤ + /// @param torque Ť + /// @param wake Ƿ + void ApplyTorque(float torque, bool wake = false); + + /// \~chinese + /// @brief תǶǷ̶ + bool IsIgnoreRotation() const; + + /// \~chinese + /// @brief Ƿ̶תǶ + void SetIgnoreRotation(bool flag); + + /// \~chinese + /// @brief Ƿӵ + bool IsBullet() const; + + /// \~chinese + /// @brief Ƿӵ + void SetBullet(bool flag); + + /// \~chinese + /// @brief Ƿڻ״̬ + bool IsAwake() const; + + /// \~chinese + /// @brief û״̬ + void SetAwake(bool flag); + + /// \~chinese + /// @brief Ƿ + bool IsSleepingAllowed() const; + + /// \~chinese + /// @brief Ƿ + void SetSleepingAllowed(bool flag); + + /// \~chinese + /// @brief Ƿ + bool IsActive() const; + + /// \~chinese + /// @brief ״̬ + void SetActive(bool flag); + + /// \~chinese + /// @brief ȡ + World* GetWorld() const; + + /// \~chinese + /// @brief ȡ󶨵Ľɫ + Actor* GetActor() const; + + /// \~chinese + /// @brief 󶨵Ľɫ + void SetActor(Actor* actor); + + /// \~chinese + /// @brief Ϣµɫ + void UpdateActor(); + + /// \~chinese + /// @brief ɫϢµ + void UpdateFromActor(); + + b2Body* GetB2Body() const; + void SetB2Body(b2Body* body); + +private: + /// \~chinese + /// @brief + void UpdateFixtureFilter(b2Fixture* fixture); + + /// \~chinese + /// @brief + void Destroy(); + +private: + Actor* actor_; + World* world_; + b2Body* body_; + + uint16_t category_bits_; + uint16_t mask_bits_; + int16_t group_index_; +}; + +/** @} */ + +inline bool Body::InitBody(World* world, ActorPtr actor) +{ + return InitBody(world, actor.get()); } + +inline FixtureList Body::GetFixtureList() const +{ + KGE_ASSERT(body_); + return FixtureList(Fixture(body_->GetFixtureList())); +} + +inline ContactEdgeList Body::GetContactList() const +{ + KGE_ASSERT(body_); + return ContactEdgeList(ContactEdge(body_->GetContactList())); +} + +inline uint16_t Body::GetCategoryBits() const +{ + return category_bits_; +} + +inline uint16_t Body::GetMaskBits() const +{ + return mask_bits_; +} + +inline int16_t Body::GetGroupIndex() const +{ + return group_index_; +} + +inline float Body::GetBodyRotation() const +{ + KGE_ASSERT(body_); + return math::Radian2Degree(body_->GetAngle()); +} + +inline void Body::SetBodyRotation(float angle) +{ + SetBodyTransform(GetBodyPosition(), angle); +} + +inline void Body::SetBodyPosition(Point const& pos) +{ + SetBodyTransform(pos, GetBodyRotation()); +} + +inline float Body::GetMass() const +{ + KGE_ASSERT(body_); + return body_->GetMass(); +} + +inline float Body::GetInertia() const +{ + KGE_ASSERT(body_); + return body_->GetInertia(); +} + +inline Body::Type Body::GetType() const +{ + KGE_ASSERT(body_); + return Type(body_->GetType()); +} + +inline void Body::SetType(Type type) +{ + KGE_ASSERT(body_); + body_->SetType(static_cast(type)); +} + +inline float Body::GetGravityScale() const +{ + KGE_ASSERT(body_); + return body_->GetGravityScale(); +} + +inline void Body::SetGravityScale(float scale) +{ + KGE_ASSERT(body_); + body_->SetGravityScale(scale); +} + +inline bool Body::IsIgnoreRotation() const +{ + KGE_ASSERT(body_); + return body_->IsFixedRotation(); +} + +inline void Body::SetIgnoreRotation(bool flag) +{ + KGE_ASSERT(body_); + body_->SetFixedRotation(flag); +} + +inline bool Body::IsBullet() const +{ + KGE_ASSERT(body_); + return body_->IsBullet(); +} + +inline void Body::SetBullet(bool flag) +{ + KGE_ASSERT(body_); + body_->SetBullet(flag); +} + +inline bool Body::IsAwake() const +{ + KGE_ASSERT(body_); + return body_->IsAwake(); +} + +inline void Body::SetAwake(bool flag) +{ + KGE_ASSERT(body_); + body_->SetAwake(flag); +} + +inline bool Body::IsSleepingAllowed() const +{ + KGE_ASSERT(body_); + return body_->IsSleepingAllowed(); +} + +inline void Body::SetSleepingAllowed(bool flag) +{ + KGE_ASSERT(body_); + body_->SetSleepingAllowed(flag); +} + +inline bool Body::IsActive() const +{ + KGE_ASSERT(body_); + return body_->IsActive(); +} + +inline void Body::SetActive(bool flag) +{ + KGE_ASSERT(body_); + body_->SetActive(flag); +} + +inline Actor* Body::GetActor() const +{ + return actor_; +} + +inline void Body::SetActor(Actor* actor) +{ + actor_ = actor; +} + +inline b2Body* Body::GetB2Body() const +{ + return body_; +} + +inline World* Body::GetWorld() const +{ + return world_; +} +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/Contact.cpp b/src/kiwano-physics/Contact.cpp index db133c94..4df8b5b5 100644 --- a/src/kiwano-physics/Contact.cpp +++ b/src/kiwano-physics/Contact.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -18,73 +18,73 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include #include +#include #include namespace kiwano { - namespace physics - { +namespace physics +{ - Contact::Contact() - : contact_(nullptr) - { - } - - Contact::Contact(b2Contact* contact) - : Contact() - { - SetB2Contact(contact); - } - - Fixture Contact::GetFixtureA() const - { - KGE_ASSERT(contact_); - return Fixture(contact_->GetFixtureA()); - } - - Fixture Contact::GetFixtureB() const - { - KGE_ASSERT(contact_); - return Fixture(contact_->GetFixtureB()); - } - - Body* Contact::GetBodyA() const - { - return GetFixtureA().GetBody(); - } - - Body* Contact::GetBodyB() const - { - return GetFixtureB().GetBody(); - } - - void Contact::SetTangentSpeed(float speed) - { - KGE_ASSERT(contact_); - - Body* body = GetFixtureA().GetBody(); - KGE_ASSERT(body); - - World* world = body->GetWorld(); - KGE_ASSERT(world); - - contact_->SetTangentSpeed(world->Stage2World(speed)); - } - - float Contact::GetTangentSpeed() const - { - KGE_ASSERT(contact_); - - const Body* body = GetFixtureA().GetBody(); - KGE_ASSERT(body); - - const World* world = body->GetWorld(); - KGE_ASSERT(world); - - return world->World2Stage(contact_->GetTangentSpeed()); - } - - } +Contact::Contact() + : contact_(nullptr) +{ } + +Contact::Contact(b2Contact* contact) + : Contact() +{ + SetB2Contact(contact); +} + +Fixture Contact::GetFixtureA() const +{ + KGE_ASSERT(contact_); + return Fixture(contact_->GetFixtureA()); +} + +Fixture Contact::GetFixtureB() const +{ + KGE_ASSERT(contact_); + return Fixture(contact_->GetFixtureB()); +} + +Body* Contact::GetBodyA() const +{ + return GetFixtureA().GetBody(); +} + +Body* Contact::GetBodyB() const +{ + return GetFixtureB().GetBody(); +} + +void Contact::SetTangentSpeed(float speed) +{ + KGE_ASSERT(contact_); + + Body* body = GetFixtureA().GetBody(); + KGE_ASSERT(body); + + World* world = body->GetWorld(); + KGE_ASSERT(world); + + contact_->SetTangentSpeed(world->Stage2World(speed)); +} + +float Contact::GetTangentSpeed() const +{ + KGE_ASSERT(contact_); + + const Body* body = GetFixtureA().GetBody(); + KGE_ASSERT(body); + + const World* world = body->GetWorld(); + KGE_ASSERT(world); + + return world->World2Stage(contact_->GetTangentSpeed()); +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/Contact.h b/src/kiwano-physics/Contact.h index 68b43fd0..5e5a9a79 100644 --- a/src/kiwano-physics/Contact.h +++ b/src/kiwano-physics/Contact.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -19,232 +19,277 @@ // THE SOFTWARE. #pragma once -#include #include +#include namespace kiwano { - namespace physics - { - class Body; +namespace physics +{ +class Body; - /** - * \addtogroup Physics - * @{ - */ +/** + * \addtogroup Physics + * @{ + */ - /// \~chinese - /// @brief Ӵ - class KGE_API Contact - { - public: - Contact(); - Contact(b2Contact* contact); +/// \~chinese +/// @brief Ӵ +class KGE_API Contact +{ +public: + Contact(); + Contact(b2Contact* contact); - /// \~chinese - /// @brief ǷЧ - bool IsValid() const; + /// \~chinese + /// @brief ǷЧ + bool IsValid() const; - /// \~chinese - /// @brief ǷǽӴ - bool IsTouching() const; + /// \~chinese + /// @brief ǷǽӴ + bool IsTouching() const; - /// \~chinese - /// @brief û (һʱ䲽) - void SetEnabled(bool flag); + /// \~chinese + /// @brief û (һʱ䲽) + void SetEnabled(bool flag); - /// \~chinese - /// @brief Ƿ - bool IsEnabled() const; + /// \~chinese + /// @brief Ƿ + bool IsEnabled() const; - /// \~chinese - /// @brief ȡAļо - Fixture GetFixtureA() const; + /// \~chinese + /// @brief ȡAļо + Fixture GetFixtureA() const; - /// \~chinese - /// @brief ȡBļо - Fixture GetFixtureB() const; + /// \~chinese + /// @brief ȡBļо + Fixture GetFixtureB() const; - /// \~chinese - /// @brief ȡA - Body* GetBodyA() const; + /// \~chinese + /// @brief ȡA + Body* GetBodyA() const; - /// \~chinese - /// @brief ȡB - Body* GetBodyB() const; + /// \~chinese + /// @brief ȡB + Body* GetBodyB() const; - /// \~chinese - /// @brief Ħ - void SetFriction(float friction); + /// \~chinese + /// @brief Ħ + void SetFriction(float friction); - /// \~chinese - /// @brief ȡĦ - float GetFriction() const; + /// \~chinese + /// @brief ȡĦ + float GetFriction() const; - /// \~chinese - /// @brief Ħ - void ResetFriction(); + /// \~chinese + /// @brief Ħ + void ResetFriction(); - /// \~chinese - /// @brief õԻָ - void SetRestitution(float restitution); + /// \~chinese + /// @brief õԻָ + void SetRestitution(float restitution); - /// \~chinese - /// @brief ȡԻָ - float GetRestitution() const; + /// \~chinese + /// @brief ȡԻָ + float GetRestitution() const; - /// \~chinese - /// @brief õԻָ - void ResetRestitution(); + /// \~chinese + /// @brief õԻָ + void ResetRestitution(); - /// \~chinese - /// @brief ٶ - void SetTangentSpeed(float speed); + /// \~chinese + /// @brief ٶ + void SetTangentSpeed(float speed); - /// \~chinese - /// @brief ȡٶ - float GetTangentSpeed() const; + /// \~chinese + /// @brief ȡٶ + float GetTangentSpeed() const; - b2Contact* GetB2Contact() const; - void SetB2Contact(b2Contact* contact); + b2Contact* GetB2Contact() const; + void SetB2Contact(b2Contact* contact); - bool operator== (const Contact& rhs) const; - bool operator!= (const Contact& rhs) const; + bool operator==(const Contact& rhs) const; + bool operator!=(const Contact& rhs) const; - private: - b2Contact* contact_; - }; +private: + b2Contact* contact_; +}; +/// \~chinese +/// @brief Ӵб +class ContactList : public List +{ + template + class IteratorImpl : public std::iterator + { + using herit = std::iterator; - /// \~chinese - /// @brief Ӵб - class ContactList - : public List - { - template - class IteratorImpl - : public std::iterator - { - using herit = std::iterator; + public: + IteratorImpl(const _Ty& elem) + : elem_(elem) + { + } - public: - IteratorImpl(const _Ty& elem) - : elem_(elem) - { - } + inline typename herit::reference operator*() const + { + return const_cast(elem_); + } - inline typename herit::reference operator*() const - { - return const_cast(elem_); - } + inline typename herit::pointer operator->() const + { + return std::pointer_traits::pointer_to(**this); + } - inline typename herit::pointer operator->() const - { - return std::pointer_traits::pointer_to(**this); - } + inline IteratorImpl& operator++() + { + elem_ = elem_.GetB2Contact()->GetNext(); + return *this; + } - inline IteratorImpl& operator++() - { - elem_ = elem_.GetB2Contact()->GetNext(); - return *this; - } + inline IteratorImpl operator++(int) + { + IteratorImpl old = *this; + operator++(); + return old; + } - inline IteratorImpl operator++(int) - { - IteratorImpl old = *this; - operator++(); - return old; - } + inline bool operator==(const IteratorImpl& rhs) const + { + return elem_ == rhs.elem_; + } - inline bool operator== (const IteratorImpl& rhs) const - { - return elem_ == rhs.elem_; - } + inline bool operator!=(const IteratorImpl& rhs) const + { + return !operator==(rhs); + } - inline bool operator!= (const IteratorImpl& rhs) const - { - return !operator==(rhs); - } + private: + _Ty elem_; + }; - private: - _Ty elem_; - }; +public: + using value_type = Contact; + using iterator = IteratorImpl; + using const_iterator = IteratorImpl; - public: - using value_type = Contact; - using iterator = IteratorImpl; - using const_iterator = IteratorImpl; + inline ContactList() {} - inline ContactList() - { - } + inline ContactList(const value_type& first) + : first_(first) + { + } - inline ContactList(const value_type& first) - : first_(first) - { - } + inline const value_type& front() const + { + return first_; + } - inline const value_type& front() const - { - return first_; - } + inline value_type& front() + { + return first_; + } - inline value_type& front() - { - return first_; - } + inline iterator begin() + { + return iterator(first_); + } - inline iterator begin() - { - return iterator(first_); - } + inline const_iterator begin() const + { + return cbegin(); + } - inline const_iterator begin() const - { - return cbegin(); - } + inline const_iterator cbegin() const + { + return const_iterator(first_); + } - inline const_iterator cbegin() const - { - return const_iterator(first_); - } + inline iterator end() + { + return iterator(nullptr); + } - inline iterator end() - { - return iterator(nullptr); - } + inline const_iterator end() const + { + return cend(); + } - inline const_iterator end() const - { - return cend(); - } + inline const_iterator cend() const + { + return const_iterator(nullptr); + } - inline const_iterator cend() const - { - return const_iterator(nullptr); - } +private: + value_type first_; +}; - private: - value_type first_; - }; +/** @} */ - /** @} */ - - - inline bool Contact::IsValid() const { return contact_ != nullptr;} - inline bool Contact::IsTouching() const { KGE_ASSERT(contact_); return contact_->IsTouching(); } - inline void Contact::SetEnabled(bool flag) { KGE_ASSERT(contact_); contact_->SetEnabled(flag); } - inline bool Contact::IsEnabled() const { KGE_ASSERT(contact_); return contact_->IsEnabled(); } - inline void Contact::SetFriction(float friction) { KGE_ASSERT(contact_); contact_->SetFriction(friction); } - inline float Contact::GetFriction() const { KGE_ASSERT(contact_); return contact_->GetFriction(); } - inline void Contact::ResetFriction() { KGE_ASSERT(contact_); contact_->ResetFriction(); } - inline void Contact::SetRestitution(float restitution) { KGE_ASSERT(contact_); contact_->SetRestitution(restitution); } - inline float Contact::GetRestitution() const { KGE_ASSERT(contact_); return contact_->GetRestitution(); } - inline void Contact::ResetRestitution() { KGE_ASSERT(contact_); contact_->ResetRestitution(); } - inline b2Contact* Contact::GetB2Contact() const { return contact_; } - inline void Contact::SetB2Contact(b2Contact* contact) { contact_ = contact; } - inline bool Contact::operator==(const Contact& rhs) const { return contact_ == rhs.contact_; } - inline bool Contact::operator!=(const Contact& rhs) const { return contact_ != rhs.contact_; } - - } +inline bool Contact::IsValid() const +{ + return contact_ != nullptr; } +inline bool Contact::IsTouching() const +{ + KGE_ASSERT(contact_); + return contact_->IsTouching(); +} +inline void Contact::SetEnabled(bool flag) +{ + KGE_ASSERT(contact_); + contact_->SetEnabled(flag); +} +inline bool Contact::IsEnabled() const +{ + KGE_ASSERT(contact_); + return contact_->IsEnabled(); +} +inline void Contact::SetFriction(float friction) +{ + KGE_ASSERT(contact_); + contact_->SetFriction(friction); +} +inline float Contact::GetFriction() const +{ + KGE_ASSERT(contact_); + return contact_->GetFriction(); +} +inline void Contact::ResetFriction() +{ + KGE_ASSERT(contact_); + contact_->ResetFriction(); +} +inline void Contact::SetRestitution(float restitution) +{ + KGE_ASSERT(contact_); + contact_->SetRestitution(restitution); +} +inline float Contact::GetRestitution() const +{ + KGE_ASSERT(contact_); + return contact_->GetRestitution(); +} +inline void Contact::ResetRestitution() +{ + KGE_ASSERT(contact_); + contact_->ResetRestitution(); +} +inline b2Contact* Contact::GetB2Contact() const +{ + return contact_; +} +inline void Contact::SetB2Contact(b2Contact* contact) +{ + contact_ = contact; +} +inline bool Contact::operator==(const Contact& rhs) const +{ + return contact_ == rhs.contact_; +} +inline bool Contact::operator!=(const Contact& rhs) const +{ + return contact_ != rhs.contact_; +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/ContactEdge.cpp b/src/kiwano-physics/ContactEdge.cpp index f902edbc..9de13ff3 100644 --- a/src/kiwano-physics/ContactEdge.cpp +++ b/src/kiwano-physics/ContactEdge.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -22,19 +22,19 @@ namespace kiwano { - namespace physics - { +namespace physics +{ - ContactEdge::ContactEdge() - : edge_(nullptr) - { - } - - ContactEdge::ContactEdge(b2ContactEdge* edge) - : ContactEdge() - { - SetB2ContactEdge(edge); - } - - } +ContactEdge::ContactEdge() + : edge_(nullptr) +{ } + +ContactEdge::ContactEdge(b2ContactEdge* edge) + : ContactEdge() +{ + SetB2ContactEdge(edge); +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/ContactEdge.h b/src/kiwano-physics/ContactEdge.h index 09f6eccc..bf500f90 100644 --- a/src/kiwano-physics/ContactEdge.h +++ b/src/kiwano-physics/ContactEdge.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -23,164 +23,183 @@ namespace kiwano { - namespace physics - { - /** - * \addtogroup Physics - * @{ - */ +namespace physics +{ +/** + * \addtogroup Physics + * @{ + */ - /// \~chinese - /// @brief Ӵ - class KGE_API ContactEdge - { - public: - ContactEdge(); - ContactEdge(b2ContactEdge* edge); +/// \~chinese +/// @brief Ӵ +class KGE_API ContactEdge +{ +public: + ContactEdge(); + ContactEdge(b2ContactEdge* edge); - /// \~chinese - /// @brief ǷЧ - bool IsValid() const; + /// \~chinese + /// @brief ǷЧ + bool IsValid() const; - /// \~chinese - /// @brief ȡӴ - Body* GetOtherBody() const; + /// \~chinese + /// @brief ȡӴ + Body* GetOtherBody() const; - /// \~chinese - /// @brief ȡӴ - Contact GetContact() const; + /// \~chinese + /// @brief ȡӴ + Contact GetContact() const; - b2ContactEdge* GetB2ContactEdge() const; - void SetB2ContactEdge(b2ContactEdge* edge); + b2ContactEdge* GetB2ContactEdge() const; + void SetB2ContactEdge(b2ContactEdge* edge); - bool operator== (const ContactEdge& rhs) const; - bool operator!= (const ContactEdge& rhs) const; + bool operator==(const ContactEdge& rhs) const; + bool operator!=(const ContactEdge& rhs) const; - private: - b2ContactEdge* edge_; - }; +private: + b2ContactEdge* edge_; +}; +/// \~chinese +/// @brief Ӵб +class ContactEdgeList +{ + template + class IteratorImpl : public std::iterator + { + using herit = std::iterator; - /// \~chinese - /// @brief Ӵб - class ContactEdgeList - { - template - class IteratorImpl - : public std::iterator - { - using herit = std::iterator; + public: + inline IteratorImpl(const _Ty& elem) + : elem_(elem) + { + } - public: - inline IteratorImpl(const _Ty& elem) - : elem_(elem) - { - } + inline typename herit::reference operator*() const + { + return const_cast(elem_); + } - inline typename herit::reference operator*() const - { - return const_cast(elem_); - } + inline typename herit::pointer operator->() const + { + return std::pointer_traits::pointer_to(**this); + } - inline typename herit::pointer operator->() const - { - return std::pointer_traits::pointer_to(**this); - } + inline IteratorImpl& operator++() + { + elem_ = elem_.GetB2ContactEdge()->next; + return *this; + } - inline IteratorImpl& operator++() - { - elem_ = elem_.GetB2ContactEdge()->next; - return *this; - } + inline IteratorImpl operator++(int) + { + IteratorImpl old = *this; + operator++(); + return old; + } - inline IteratorImpl operator++(int) - { - IteratorImpl old = *this; - operator++(); - return old; - } + inline bool operator==(const IteratorImpl& rhs) const + { + return elem_ == rhs.elem_; + } - inline bool operator== (const IteratorImpl& rhs) const - { - return elem_ == rhs.elem_; - } + inline bool operator!=(const IteratorImpl& rhs) const + { + return !operator==(rhs); + } - inline bool operator!= (const IteratorImpl& rhs) const - { - return !operator==(rhs); - } + private: + _Ty elem_; + }; - private: - _Ty elem_; - }; +public: + using value_type = ContactEdge; + using iterator = IteratorImpl; + using const_iterator = IteratorImpl; - public: - using value_type = ContactEdge; - using iterator = IteratorImpl; - using const_iterator = IteratorImpl; + inline ContactEdgeList() {} - inline ContactEdgeList() - { - } + inline ContactEdgeList(const value_type& first) + : first_(first) + { + } - inline ContactEdgeList(const value_type& first) - : first_(first) - { - } + inline const value_type& front() const + { + return first_; + } - inline const value_type& front() const - { - return first_; - } + inline value_type& front() + { + return first_; + } - inline value_type& front() - { - return first_; - } + inline iterator begin() + { + return iterator(first_); + } - inline iterator begin() - { - return iterator(first_); - } + inline const_iterator begin() const + { + return cbegin(); + } - inline const_iterator begin() const - { - return cbegin(); - } + inline const_iterator cbegin() const + { + return const_iterator(first_); + } - inline const_iterator cbegin() const - { - return const_iterator(first_); - } + inline iterator end() + { + return iterator(nullptr); + } - inline iterator end() - { - return iterator(nullptr); - } + inline const_iterator end() const + { + return cend(); + } - inline const_iterator end() const - { - return cend(); - } + inline const_iterator cend() const + { + return const_iterator(nullptr); + } - inline const_iterator cend() const - { - return const_iterator(nullptr); - } +private: + value_type first_; +}; - private: - value_type first_; - }; +/** @} */ - /** @} */ - - inline bool ContactEdge::IsValid() const { return edge_ != nullptr; } - inline Body* ContactEdge::GetOtherBody() const { KGE_ASSERT(edge_); return static_cast(edge_->other->GetUserData()); } - inline Contact ContactEdge::GetContact() const { KGE_ASSERT(edge_); return Contact(edge_->contact); } - inline b2ContactEdge* ContactEdge::GetB2ContactEdge() const { return edge_; } - inline void ContactEdge::SetB2ContactEdge(b2ContactEdge* edge) { edge_ = edge; } - inline bool ContactEdge::operator==(const ContactEdge& rhs) const { return edge_ == rhs.edge_; } - inline bool ContactEdge::operator!=(const ContactEdge& rhs) const { return edge_ != rhs.edge_; } - - } +inline bool ContactEdge::IsValid() const +{ + return edge_ != nullptr; } +inline Body* ContactEdge::GetOtherBody() const +{ + KGE_ASSERT(edge_); + return static_cast(edge_->other->GetUserData()); +} +inline Contact ContactEdge::GetContact() const +{ + KGE_ASSERT(edge_); + return Contact(edge_->contact); +} +inline b2ContactEdge* ContactEdge::GetB2ContactEdge() const +{ + return edge_; +} +inline void ContactEdge::SetB2ContactEdge(b2ContactEdge* edge) +{ + edge_ = edge; +} +inline bool ContactEdge::operator==(const ContactEdge& rhs) const +{ + return edge_ == rhs.edge_; +} +inline bool ContactEdge::operator!=(const ContactEdge& rhs) const +{ + return edge_ != rhs.edge_; +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/ContactEvent.cpp b/src/kiwano-physics/ContactEvent.cpp index 8e0be3b1..3a53912a 100644 --- a/src/kiwano-physics/ContactEvent.cpp +++ b/src/kiwano-physics/ContactEvent.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -22,29 +22,29 @@ namespace kiwano { - namespace physics - { - ContactBeginEvent::ContactBeginEvent() - : Event(KGE_EVENT(ContactBeginEvent)) - { - } - - ContactBeginEvent::ContactBeginEvent(Contact const& contact) - : ContactBeginEvent() - { - this->contact = contact; - } - - ContactEndEvent::ContactEndEvent() - : Event(KGE_EVENT(ContactEndEvent)) - { - } - - ContactEndEvent::ContactEndEvent(Contact const& contact) - : ContactEndEvent() - { - this->contact = contact; - } - +namespace physics +{ +ContactBeginEvent::ContactBeginEvent() + : Event(KGE_EVENT(ContactBeginEvent)) +{ } + +ContactBeginEvent::ContactBeginEvent(Contact const& contact) + : ContactBeginEvent() +{ + this->contact = contact; } + +ContactEndEvent::ContactEndEvent() + : Event(KGE_EVENT(ContactEndEvent)) +{ +} + +ContactEndEvent::ContactEndEvent(Contact const& contact) + : ContactEndEvent() +{ + this->contact = contact; +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/ContactEvent.h b/src/kiwano-physics/ContactEvent.h index 79a11962..72fa1710 100644 --- a/src/kiwano-physics/ContactEvent.h +++ b/src/kiwano-physics/ContactEvent.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -19,44 +19,45 @@ // THE SOFTWARE. #pragma once -#include #include +#include namespace kiwano { - namespace physics - { - /** - * \addtogroup Events - * @{ - */ +namespace physics +{ +KGE_DECLARE_SMART_PTR(ContactBeginEvent); +KGE_DECLARE_SMART_PTR(ContactEndEvent); - /// \~chinese - /// @brief Ӵʼ¼ - class KGE_API ContactBeginEvent - : public Event - { - public: - Contact contact; ///< ĽӴ +/** + * \addtogroup Events + * @{ + */ - ContactBeginEvent(); +/// \~chinese +/// @brief Ӵʼ¼ +class KGE_API ContactBeginEvent : public Event +{ +public: + Contact contact; ///< ĽӴ - ContactBeginEvent(Contact const& contact); - }; + ContactBeginEvent(); - /// \~chinese - /// @brief Ӵ¼ - class KGE_API ContactEndEvent - : public Event - { - public: - Contact contact; ///< ĽӴ + ContactBeginEvent(Contact const& contact); +}; - ContactEndEvent(); +/// \~chinese +/// @brief Ӵ¼ +class KGE_API ContactEndEvent : public Event +{ +public: + Contact contact; ///< ĽӴ - ContactEndEvent(Contact const& contact); - }; + ContactEndEvent(); - /** @} */ - } -} + ContactEndEvent(Contact const& contact); +}; + +/** @} */ +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/Fixture.cpp b/src/kiwano-physics/Fixture.cpp index 8eb2020b..7cf316fc 100644 --- a/src/kiwano-physics/Fixture.cpp +++ b/src/kiwano-physics/Fixture.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -18,88 +18,91 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include #include +#include #include namespace kiwano { - namespace physics - { +namespace physics +{ - Fixture::Fixture() - : fixture_(nullptr) - { - } - - Fixture::Fixture(b2Fixture* fixture) - : Fixture() - { - SetB2Fixture(fixture); - } - - Fixture::Fixture(Body* body, Shape* shape, const Param& param) - : Fixture() - { - KGE_ASSERT(body); - - if (shape) - { - shape->FitWorld(body->GetWorld()); - - b2Body* b2body = body->GetB2Body(); - b2FixtureDef fd; - fd.density = param.density; - fd.friction = param.friction; - fd.restitution = param.restitution; - fd.shape = shape->GetB2Shape(); - auto fixture = b2body->CreateFixture(&fd); - SetB2Fixture(fixture); - } - } - - Body* Fixture::GetBody() const - { - KGE_ASSERT(fixture_); - return static_cast(fixture_->GetBody()->GetUserData()); - } - - Shape Fixture::GetShape() const - { - KGE_ASSERT(fixture_); - return Shape(fixture_->GetShape()); - } - - void Fixture::GetMassData(float* mass, Point* center, float* inertia) const - { - KGE_ASSERT(fixture_); - - const Body* body = GetBody(); - KGE_ASSERT(body); - - const World* world = body->GetWorld(); - KGE_ASSERT(world); - - b2MassData data; - fixture_->GetMassData(&data); - - if (mass) *mass = data.mass; - if (center) *center = world->World2Stage(data.center); - if (inertia) *inertia = data.I; - } - - bool Fixture::TestPoint(const Point& p) const - { - KGE_ASSERT(fixture_); - - const Body* body = GetBody(); - KGE_ASSERT(body); - - const World* world = body->GetWorld(); - KGE_ASSERT(world); - - return fixture_->TestPoint(world->Stage2World(p)); - } - - } +Fixture::Fixture() + : fixture_(nullptr) +{ } + +Fixture::Fixture(b2Fixture* fixture) + : Fixture() +{ + SetB2Fixture(fixture); +} + +Fixture::Fixture(Body* body, Shape* shape, const Param& param) + : Fixture() +{ + KGE_ASSERT(body); + + if (shape) + { + shape->FitWorld(body->GetWorld()); + + b2Body* b2body = body->GetB2Body(); + b2FixtureDef fd; + fd.density = param.density; + fd.friction = param.friction; + fd.restitution = param.restitution; + fd.shape = shape->GetB2Shape(); + auto fixture = b2body->CreateFixture(&fd); + SetB2Fixture(fixture); + } +} + +Body* Fixture::GetBody() const +{ + KGE_ASSERT(fixture_); + return static_cast(fixture_->GetBody()->GetUserData()); +} + +Shape Fixture::GetShape() const +{ + KGE_ASSERT(fixture_); + return Shape(fixture_->GetShape()); +} + +void Fixture::GetMassData(float* mass, Point* center, float* inertia) const +{ + KGE_ASSERT(fixture_); + + const Body* body = GetBody(); + KGE_ASSERT(body); + + const World* world = body->GetWorld(); + KGE_ASSERT(world); + + b2MassData data; + fixture_->GetMassData(&data); + + if (mass) + *mass = data.mass; + if (center) + *center = world->World2Stage(data.center); + if (inertia) + *inertia = data.I; +} + +bool Fixture::TestPoint(const Point& p) const +{ + KGE_ASSERT(fixture_); + + const Body* body = GetBody(); + KGE_ASSERT(body); + + const World* world = body->GetWorld(); + KGE_ASSERT(world); + + return fixture_->TestPoint(world->Stage2World(p)); +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/Fixture.h b/src/kiwano-physics/Fixture.h index df8885ba..e7f1d52b 100644 --- a/src/kiwano-physics/Fixture.h +++ b/src/kiwano-physics/Fixture.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -19,237 +19,281 @@ // THE SOFTWARE. #pragma once -#include #include +#include namespace kiwano { - namespace physics - { - class Body; +namespace physics +{ +class Body; - /** - * \addtogroup Physics - * @{ - */ +/** + * \addtogroup Physics + * @{ + */ - /// \~chinese - /// @brief о - class Fixture - { - public: - /// \~chinese - /// @brief о߲ - struct Param - { - float density = 0.f; ///< ܶ - float friction = 0.2f; ///< Ħ - float restitution = 0.f; ///< Իָ - bool is_sensor = false; ///< ǷǽӴ +/// \~chinese +/// @brief о +class Fixture +{ +public: + /// \~chinese + /// @brief о߲ + struct Param + { + float density = 0.f; ///< ܶ + float friction = 0.2f; ///< Ħ + float restitution = 0.f; ///< Իָ + bool is_sensor = false; ///< ǷǽӴ - Param() {} + Param() {} - Param(float density, float friction = 0.2f, float restitution = 0.f, bool is_sensor = false) - : density(density) - , friction(friction) - , restitution(restitution) - , is_sensor(is_sensor) - {} - }; + Param(float density, float friction = 0.2f, float restitution = 0.f, bool is_sensor = false) + : density(density) + , friction(friction) + , restitution(restitution) + , is_sensor(is_sensor) + { + } + }; - Fixture(); - Fixture(b2Fixture* fixture); - Fixture(Body* body, Shape* shape, const Param& param); + Fixture(); + Fixture(b2Fixture* fixture); + Fixture(Body* body, Shape* shape, const Param& param); - /// \~chinese - /// @brief ǷЧ - bool IsValid() const; + /// \~chinese + /// @brief ǷЧ + bool IsValid() const; - /// \~chinese - /// @brief ȡоڵ - Body* GetBody() const; + /// \~chinese + /// @brief ȡоڵ + Body* GetBody() const; - /// \~chinese - /// @brief ״ - Shape GetShape() const; + /// \~chinese + /// @brief ״ + Shape GetShape() const; - /// \~chinese - /// @brief ǷǽӴ - bool IsSensor() const; + /// \~chinese + /// @brief ǷǽӴ + bool IsSensor() const; - /// \~chinese - /// @brief üоǷǽӴ - /// @details ӴֻӴӰ˶ - void SetSensor(bool sensor); + /// \~chinese + /// @brief üоǷǽӴ + /// @details ӴֻӴӰ˶ + void SetSensor(bool sensor); - /// \~chinese - /// @brief ȡоߵ - void GetMassData(float* mass, Point* center, float* inertia) const; + /// \~chinese + /// @brief ȡоߵ + void GetMassData(float* mass, Point* center, float* inertia) const; - /// \~chinese - /// @brief ȡܶ - float GetDensity() const; + /// \~chinese + /// @brief ȡܶ + float GetDensity() const; - /// \~chinese - /// @brief ܶ - void SetDensity(float density); + /// \~chinese + /// @brief ܶ + void SetDensity(float density); - /// \~chinese - /// @brief ȡĦ [N] - float GetFriction() const; + /// \~chinese + /// @brief ȡĦ [N] + float GetFriction() const; - /// \~chinese - /// @brief Ħ [N] - void SetFriction(float friction); + /// \~chinese + /// @brief Ħ [N] + void SetFriction(float friction); - /// \~chinese - /// @brief ȡԻָ - float GetRestitution() const; + /// \~chinese + /// @brief ȡԻָ + float GetRestitution() const; - /// \~chinese - /// @brief õԻָ - void SetRestitution(float restitution); + /// \~chinese + /// @brief õԻָ + void SetRestitution(float restitution); - /// \~chinese - /// @brief - bool TestPoint(const Point& p) const; + /// \~chinese + /// @brief + bool TestPoint(const Point& p) const; - b2Fixture* GetB2Fixture() const; - void SetB2Fixture(b2Fixture* fixture); + b2Fixture* GetB2Fixture() const; + void SetB2Fixture(b2Fixture* fixture); - bool operator== (const Fixture& rhs) const; - bool operator!= (const Fixture& rhs) const; + bool operator==(const Fixture& rhs) const; + bool operator!=(const Fixture& rhs) const; - private: - b2Fixture* fixture_; - }; +private: + b2Fixture* fixture_; +}; - /// \~chinese - /// @brief об - class FixtureList - : public List - { - template - class IteratorImpl - : public std::iterator - { - using herit = std::iterator; +/// \~chinese +/// @brief об +class FixtureList : public List +{ + template + class IteratorImpl : public std::iterator + { + using herit = std::iterator; - public: - IteratorImpl(const _Ty& elem) - : elem_(elem) - { - } + public: + IteratorImpl(const _Ty& elem) + : elem_(elem) + { + } - inline typename herit::reference operator*() const - { - return const_cast(elem_); - } + inline typename herit::reference operator*() const + { + return const_cast(elem_); + } - inline typename herit::pointer operator->() const - { - return std::pointer_traits::pointer_to(**this); - } + inline typename herit::pointer operator->() const + { + return std::pointer_traits::pointer_to(**this); + } - inline IteratorImpl& operator++() - { - elem_ = elem_.GetB2Fixture()->GetNext(); - return *this; - } + inline IteratorImpl& operator++() + { + elem_ = elem_.GetB2Fixture()->GetNext(); + return *this; + } - inline IteratorImpl operator++(int) - { - IteratorImpl old = *this; - operator++(); - return old; - } + inline IteratorImpl operator++(int) + { + IteratorImpl old = *this; + operator++(); + return old; + } - inline bool operator== (const IteratorImpl& rhs) const - { - return elem_ == rhs.elem_; - } + inline bool operator==(const IteratorImpl& rhs) const + { + return elem_ == rhs.elem_; + } - inline bool operator!= (const IteratorImpl& rhs) const - { - return !operator==(rhs); - } + inline bool operator!=(const IteratorImpl& rhs) const + { + return !operator==(rhs); + } - private: - _Ty elem_; - }; + private: + _Ty elem_; + }; - public: - using value_type = Fixture; - using iterator = IteratorImpl; - using const_iterator = IteratorImpl; +public: + using value_type = Fixture; + using iterator = IteratorImpl; + using const_iterator = IteratorImpl; - inline FixtureList() - { - } + inline FixtureList() {} - inline FixtureList(const value_type& first) - : first_(first) - { - } + inline FixtureList(const value_type& first) + : first_(first) + { + } - inline const value_type& front() const - { - return first_; - } + inline const value_type& front() const + { + return first_; + } - inline value_type& front() - { - return first_; - } + inline value_type& front() + { + return first_; + } - inline iterator begin() - { - return iterator(first_); - } + inline iterator begin() + { + return iterator(first_); + } - inline const_iterator begin() const - { - return cbegin(); - } + inline const_iterator begin() const + { + return cbegin(); + } - inline const_iterator cbegin() const - { - return const_iterator(first_); - } + inline const_iterator cbegin() const + { + return const_iterator(first_); + } - inline iterator end() - { - return iterator(nullptr); - } + inline iterator end() + { + return iterator(nullptr); + } - inline const_iterator end() const - { - return cend(); - } + inline const_iterator end() const + { + return cend(); + } - inline const_iterator cend() const - { - return const_iterator(nullptr); - } + inline const_iterator cend() const + { + return const_iterator(nullptr); + } - private: - value_type first_; - }; +private: + value_type first_; +}; - /** @} */ +/** @} */ - inline bool Fixture::IsSensor() const { KGE_ASSERT(fixture_); return fixture_->IsSensor(); } - inline void Fixture::SetSensor(bool sensor) { KGE_ASSERT(fixture_); fixture_->SetSensor(sensor); } - inline float Fixture::GetDensity() const { KGE_ASSERT(fixture_); return fixture_->GetDensity(); } - inline void Fixture::SetDensity(float density) { KGE_ASSERT(fixture_); fixture_->SetDensity(density); } - inline float Fixture::GetFriction() const { KGE_ASSERT(fixture_); return fixture_->GetFriction(); } - inline void Fixture::SetFriction(float friction) { KGE_ASSERT(fixture_); fixture_->SetFriction(friction); } - inline float Fixture::GetRestitution() const { KGE_ASSERT(fixture_); return fixture_->GetRestitution(); } - inline void Fixture::SetRestitution(float restitution) { KGE_ASSERT(fixture_); fixture_->SetRestitution(restitution); } - inline bool Fixture::IsValid() const { return fixture_ != nullptr; } - inline b2Fixture* Fixture::GetB2Fixture() const { return fixture_; } - inline void Fixture::SetB2Fixture(b2Fixture* fixture) { fixture_ = fixture; } - inline bool Fixture::operator==(const Fixture& rhs) const { return fixture_ == rhs.fixture_; } - inline bool Fixture::operator!=(const Fixture& rhs) const { return fixture_ != rhs.fixture_; } - } +inline bool Fixture::IsSensor() const +{ + KGE_ASSERT(fixture_); + return fixture_->IsSensor(); } +inline void Fixture::SetSensor(bool sensor) +{ + KGE_ASSERT(fixture_); + fixture_->SetSensor(sensor); +} +inline float Fixture::GetDensity() const +{ + KGE_ASSERT(fixture_); + return fixture_->GetDensity(); +} +inline void Fixture::SetDensity(float density) +{ + KGE_ASSERT(fixture_); + fixture_->SetDensity(density); +} +inline float Fixture::GetFriction() const +{ + KGE_ASSERT(fixture_); + return fixture_->GetFriction(); +} +inline void Fixture::SetFriction(float friction) +{ + KGE_ASSERT(fixture_); + fixture_->SetFriction(friction); +} +inline float Fixture::GetRestitution() const +{ + KGE_ASSERT(fixture_); + return fixture_->GetRestitution(); +} +inline void Fixture::SetRestitution(float restitution) +{ + KGE_ASSERT(fixture_); + fixture_->SetRestitution(restitution); +} +inline bool Fixture::IsValid() const +{ + return fixture_ != nullptr; +} +inline b2Fixture* Fixture::GetB2Fixture() const +{ + return fixture_; +} +inline void Fixture::SetB2Fixture(b2Fixture* fixture) +{ + fixture_ = fixture; +} +inline bool Fixture::operator==(const Fixture& rhs) const +{ + return fixture_ == rhs.fixture_; +} +inline bool Fixture::operator!=(const Fixture& rhs) const +{ + return fixture_ != rhs.fixture_; +} +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/Joint.cpp b/src/kiwano-physics/Joint.cpp index 4005e078..ed52c955 100644 --- a/src/kiwano-physics/Joint.cpp +++ b/src/kiwano-physics/Joint.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -23,667 +23,593 @@ namespace kiwano { - namespace physics - { - // - // Joint - // - - Joint::Joint() - : joint_(nullptr) - , world_(nullptr) - , type_(Type::Unknown) - { - } - - Joint::Joint(b2Joint* joint) - : Joint() - { - SetB2Joint(joint); - } - - Joint::Joint(World* world, b2JointDef* joint_def) - : Joint() - { - Init(world, joint_def); - } - - Joint::~Joint() - { - if (world_) - { - world_->RemoveJoint(this); - } - } - - void Joint::Init(World* world, b2JointDef* joint_def) - { - world_ = world; - if (world_) - { - world_->AddJoint(this); - - b2Joint* joint = world_->GetB2World()->CreateJoint(joint_def); - SetB2Joint(joint); - } - } - - BodyPtr Joint::GetBodyA() const - { - KGE_ASSERT(joint_); - - b2Body* body = joint_->GetBodyA(); - return BodyPtr(static_cast(body->GetUserData())); - } - - BodyPtr Joint::GetBodyB() const - { - KGE_ASSERT(joint_); - - b2Body* body = joint_->GetBodyB(); - return BodyPtr(static_cast(body->GetUserData())); - } - - void Joint::SetB2Joint(b2Joint* joint) - { - joint_ = joint; - if (joint_) - { - type_ = Joint::Type(joint_->GetType()); - } - } - - // - // DistanceJoint - // - - DistanceJoint::DistanceJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - DistanceJoint::DistanceJoint(World* world, b2DistanceJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - DistanceJoint::DistanceJoint(World* world, DistanceJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2DistanceJointDef def; - def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor_a), world->Stage2World(param.anchor_b)); - def.frequencyHz = param.frequency_hz; - def.dampingRatio = param.damping_ratio; - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - void DistanceJoint::SetLength(float length) - { - KGE_ASSERT(raw_joint_ && GetWorld()); - raw_joint_->SetLength(GetWorld()->Stage2World(length)); - } - - float DistanceJoint::GetLength() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetLength()); - } - - // - // FrictionJoint - // - - FrictionJoint::FrictionJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - FrictionJoint::FrictionJoint(World* world, b2FrictionJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - FrictionJoint::FrictionJoint(World* world, FrictionJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2FrictionJointDef def; - def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor)); - def.maxForce = param.max_force; - def.maxTorque = world->Stage2World(param.max_torque); - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - void FrictionJoint::SetMaxForce(float length) - { - KGE_ASSERT(raw_joint_); - raw_joint_->SetMaxForce(length); - } - - float FrictionJoint::GetMaxForce() const - { - KGE_ASSERT(raw_joint_); - return raw_joint_->GetMaxForce(); - } - - void FrictionJoint::SetMaxTorque(float length) - { - KGE_ASSERT(raw_joint_ && GetWorld()); - raw_joint_->SetMaxTorque(GetWorld()->Stage2World(length)); - } - - float FrictionJoint::GetMaxTorque() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetMaxTorque()); - } - - // - // GearJoint - // - - GearJoint::GearJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - GearJoint::GearJoint(World* world, b2GearJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - GearJoint::GearJoint(World* world, GearJoint::Param param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.joint_a && param.joint_b); - - b2GearJointDef def; - def.joint1 = param.joint_a->GetB2Joint(); - def.joint2 = param.joint_b->GetB2Joint(); - def.ratio = param.ratio; - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - void GearJoint::SetRatio(float ratio) - { - KGE_ASSERT(raw_joint_); - raw_joint_->SetRatio(ratio); - } - - float GearJoint::GetRatio() const - { - KGE_ASSERT(raw_joint_); - return raw_joint_->GetRatio(); - } - - // - // MotorJoint - // - - MotorJoint::MotorJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - MotorJoint::MotorJoint(World* world, b2MotorJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - MotorJoint::MotorJoint(World* world, MotorJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2MotorJointDef def; - def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body()); - def.maxForce = param.max_force; - def.maxTorque = world->Stage2World(param.max_torque); - def.correctionFactor = param.correction_factor; - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - void MotorJoint::SetMaxForce(float length) - { - KGE_ASSERT(raw_joint_); - raw_joint_->SetMaxForce(length); - } - - float MotorJoint::GetMaxForce() const - { - KGE_ASSERT(raw_joint_); - return raw_joint_->GetMaxForce(); - } - - void MotorJoint::SetMaxTorque(float length) - { - KGE_ASSERT(raw_joint_ && GetWorld()); - raw_joint_->SetMaxTorque(GetWorld()->Stage2World(length)); - } - - float MotorJoint::GetMaxTorque() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetMaxTorque()); - } - - // - // PrismaticJoint - // - - PrismaticJoint::PrismaticJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - PrismaticJoint::PrismaticJoint(World* world, b2PrismaticJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - PrismaticJoint::PrismaticJoint(World* world, PrismaticJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2PrismaticJointDef def; - def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor), Stage2World(param.axis)); - def.enableLimit = param.enable_limit; - def.lowerTranslation = world->Stage2World(param.lower_translation); - def.upperTranslation = world->Stage2World(param.upper_translation); - def.enableMotor = param.enable_motor; - def.maxMotorForce = param.max_motor_force; - def.motorSpeed = world->Stage2World(param.motor_speed); - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - float PrismaticJoint::GetJointTranslation() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetJointTranslation()); - } - - float PrismaticJoint::GetJointSpeed() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetJointSpeed()); - } - - float PrismaticJoint::GetLowerLimit() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetLowerLimit()); - } - - float PrismaticJoint::GetUpperLimit() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetUpperLimit()); - } - - void PrismaticJoint::SetLimits(float lower, float upper) - { - KGE_ASSERT(raw_joint_ && GetWorld()); - raw_joint_->SetLimits(GetWorld()->Stage2World(lower), GetWorld()->Stage2World(upper)); - } - - // - // PulleyJoint - // - - PulleyJoint::PulleyJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - PulleyJoint::PulleyJoint(World* world, b2PulleyJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - PulleyJoint::PulleyJoint(World* world, PulleyJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2PulleyJointDef def; - def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.ground_anchor_a), world->Stage2World(param.ground_anchor_b), - world->Stage2World(param.anchor_a), world->Stage2World(param.anchor_b), param.ratio); - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - Point PulleyJoint::GetGroundAnchorA() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetGroundAnchorA()); - } - - Point PulleyJoint::GetGroundAnchorB() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetGroundAnchorB()); - } - - float PulleyJoint::GetRatio() const - { - KGE_ASSERT(raw_joint_); - return raw_joint_->GetRatio(); - } - - float PulleyJoint::GetLengthA() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetLengthA()); - } - - float PulleyJoint::GetLengthB() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetLengthB()); - } - - float PulleyJoint::GetCurrentLengthA() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetCurrentLengthA()); - } - - float PulleyJoint::GetCurrentLengthB() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetCurrentLengthB()); - } - - // - // RevoluteJoint - // - - RevoluteJoint::RevoluteJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - RevoluteJoint::RevoluteJoint(World* world, b2RevoluteJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - RevoluteJoint::RevoluteJoint(World* world, RevoluteJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2RevoluteJointDef def; - def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor)); - def.enableLimit = param.enable_limit; - def.lowerAngle = math::Degree2Radian(param.lower_angle); - def.upperAngle = math::Degree2Radian(param.upper_angle); - def.enableMotor = param.enable_motor; - def.maxMotorTorque = world->Stage2World(param.max_motor_torque); - def.motorSpeed = math::Degree2Radian(param.motor_speed); - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - float RevoluteJoint::GetJointAngle() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return math::Radian2Degree(raw_joint_->GetJointAngle()); - } - - float RevoluteJoint::GetJointSpeed() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return math::Radian2Degree(raw_joint_->GetJointSpeed()); - } - - float RevoluteJoint::GetLowerLimit() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return math::Radian2Degree(raw_joint_->GetLowerLimit()); - } - - float RevoluteJoint::GetUpperLimit() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return math::Radian2Degree(raw_joint_->GetUpperLimit()); - } - - void RevoluteJoint::SetLimits(float lower, float upper) - { - KGE_ASSERT(raw_joint_ && GetWorld()); - raw_joint_->SetLimits(math::Degree2Radian(lower), math::Degree2Radian(upper)); - } - - void RevoluteJoint::SetMaxMotorTorque(float torque) - { - KGE_ASSERT(raw_joint_ && GetWorld()); - raw_joint_->SetMaxMotorTorque(GetWorld()->Stage2World(torque)); - } - - float RevoluteJoint::GetMaxMotorTorque() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetMaxMotorTorque()); - } - - // - // RopeJoint - // - - RopeJoint::RopeJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - RopeJoint::RopeJoint(World* world, b2RopeJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - RopeJoint::RopeJoint(World* world, RopeJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2RopeJointDef def; - def.bodyA = param.body_a->GetB2Body(); - def.bodyB = param.body_b->GetB2Body(); - def.localAnchorA = world->Stage2World(param.local_anchor_a); - def.localAnchorB = world->Stage2World(param.local_anchor_b); - def.maxLength = world->Stage2World(param.max_length); - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - void RopeJoint::SetMaxLength(float length) - { - KGE_ASSERT(raw_joint_ && GetWorld()); - raw_joint_->SetMaxLength(GetWorld()->Stage2World(length)); - } - - float RopeJoint::GetMaxLength() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetMaxLength()); - } - - // - // WeldJoint - // - - WeldJoint::WeldJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - WeldJoint::WeldJoint(World* world, b2WeldJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - WeldJoint::WeldJoint(World* world, WeldJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2WeldJointDef def; - def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor)); - def.frequencyHz = param.frequency_hz; - def.dampingRatio = param.damping_ratio; - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - - // - // WheelJoint - // - - WheelJoint::WheelJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - WheelJoint::WheelJoint(World* world, b2WheelJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - WheelJoint::WheelJoint(World* world, WheelJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2WheelJointDef def; - def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor), Stage2World(param.axis)); - def.enableMotor = param.enable_motor; - def.maxMotorTorque = world->Stage2World(param.max_motor_torque); - def.motorSpeed = world->Stage2World(param.motor_speed); - def.frequencyHz = param.frequency_hz; - def.dampingRatio = param.damping_ratio; - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - float WheelJoint::GetJointTranslation() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetJointTranslation()); - } - - float WheelJoint::GetJointLinearSpeed() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetJointLinearSpeed()); - } - - void WheelJoint::SetMaxMotorTorque(float torque) - { - KGE_ASSERT(raw_joint_ && GetWorld()); - raw_joint_->SetMaxMotorTorque(GetWorld()->Stage2World(torque)); - } - - float WheelJoint::GetMaxMotorTorque() const - { - KGE_ASSERT(raw_joint_ && GetWorld()); - return GetWorld()->World2Stage(raw_joint_->GetMaxMotorTorque()); - } - - // - // MouseJoint - // - - MouseJoint::MouseJoint() - : Joint() - , raw_joint_(nullptr) - { - } - - MouseJoint::MouseJoint(World* world, b2MouseJointDef* def) - : Joint(world, def) - , raw_joint_(nullptr) - { - } - - MouseJoint::MouseJoint(World* world, MouseJoint::Param const& param) - : Joint() - , raw_joint_(nullptr) - { - KGE_ASSERT(param.body_a && param.body_b); - - b2MouseJointDef def; - def.bodyA = param.body_a->GetB2Body(); - def.bodyB = param.body_b->GetB2Body(); - def.target = world->Stage2World(param.target); - def.maxForce = param.max_force; - def.frequencyHz = param.frequency_hz; - def.dampingRatio = param.damping_ratio; - - Init(world, &def); - raw_joint_ = static_cast(GetB2Joint()); - } - - void MouseJoint::SetMaxForce(float length) - { - KGE_ASSERT(raw_joint_); - raw_joint_->SetMaxForce(length); - } - - float MouseJoint::GetMaxForce() const - { - KGE_ASSERT(raw_joint_); - return raw_joint_->GetMaxForce(); - } - +namespace physics +{ +// +// Joint +// + +Joint::Joint() + : joint_(nullptr) + , world_(nullptr) + , type_(Type::Unknown) +{ } + +Joint::~Joint() +{ + Destroy(); } + +bool Joint::InitJoint(World* world, b2JointDef* joint_def) +{ + KGE_ASSERT(world); + + Destroy(); + + world_ = world; + if (world_) + { + world_->AddJoint(this); + + b2Joint* joint = world_->GetB2World()->CreateJoint(joint_def); + SetB2Joint(joint); + + return joint != nullptr; + } + return false; +} + +BodyPtr Joint::GetBodyA() const +{ + KGE_ASSERT(joint_); + + b2Body* body = joint_->GetBodyA(); + return BodyPtr(static_cast(body->GetUserData())); +} + +BodyPtr Joint::GetBodyB() const +{ + KGE_ASSERT(joint_); + + b2Body* body = joint_->GetBodyB(); + return BodyPtr(static_cast(body->GetUserData())); +} + +void Joint::SetB2Joint(b2Joint* joint) +{ + joint_ = joint; + if (joint_) + { + type_ = Joint::Type(joint_->GetType()); + } +} + +void Joint::Destroy() +{ + if (world_) + { + world_->RemoveJoint(this); + } +} + +// +// DistanceJoint +// + +DistanceJoint::DistanceJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool DistanceJoint::InitJoint(World* world, DistanceJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2DistanceJointDef def; + def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor_a), + world->Stage2World(param.anchor_b)); + def.frequencyHz = param.frequency_hz; + def.dampingRatio = param.damping_ratio; + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +void DistanceJoint::SetLength(float length) +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + raw_joint_->SetLength(GetWorld()->Stage2World(length)); +} + +float DistanceJoint::GetLength() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetLength()); +} + +// +// FrictionJoint +// + +FrictionJoint::FrictionJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool FrictionJoint::InitJoint(World* world, FrictionJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2FrictionJointDef def; + def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor)); + def.maxForce = param.max_force; + def.maxTorque = world->Stage2World(param.max_torque); + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +void FrictionJoint::SetMaxForce(float length) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetMaxForce(length); +} + +float FrictionJoint::GetMaxForce() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetMaxForce(); +} + +void FrictionJoint::SetMaxTorque(float length) +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + raw_joint_->SetMaxTorque(GetWorld()->Stage2World(length)); +} + +float FrictionJoint::GetMaxTorque() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetMaxTorque()); +} + +// +// GearJoint +// + +GearJoint::GearJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool GearJoint::InitJoint(World* world, GearJoint::Param const& param) +{ + KGE_ASSERT(param.joint_a && param.joint_b); + + b2GearJointDef def; + def.joint1 = param.joint_a->GetB2Joint(); + def.joint2 = param.joint_b->GetB2Joint(); + def.ratio = param.ratio; + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +void GearJoint::SetRatio(float ratio) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetRatio(ratio); +} + +float GearJoint::GetRatio() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetRatio(); +} + +// +// MotorJoint +// + +MotorJoint::MotorJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool MotorJoint::InitJoint(World* world, MotorJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2MotorJointDef def; + def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body()); + def.maxForce = param.max_force; + def.maxTorque = world->Stage2World(param.max_torque); + def.correctionFactor = param.correction_factor; + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +void MotorJoint::SetMaxForce(float length) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetMaxForce(length); +} + +float MotorJoint::GetMaxForce() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetMaxForce(); +} + +void MotorJoint::SetMaxTorque(float length) +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + raw_joint_->SetMaxTorque(GetWorld()->Stage2World(length)); +} + +float MotorJoint::GetMaxTorque() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetMaxTorque()); +} + +// +// PrismaticJoint +// + +PrismaticJoint::PrismaticJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool PrismaticJoint::InitJoint(World* world, PrismaticJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2PrismaticJointDef def; + def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor), + Stage2World(param.axis)); + def.enableLimit = param.enable_limit; + def.lowerTranslation = world->Stage2World(param.lower_translation); + def.upperTranslation = world->Stage2World(param.upper_translation); + def.enableMotor = param.enable_motor; + def.maxMotorForce = param.max_motor_force; + def.motorSpeed = world->Stage2World(param.motor_speed); + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +float PrismaticJoint::GetJointTranslation() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetJointTranslation()); +} + +float PrismaticJoint::GetJointSpeed() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetJointSpeed()); +} + +float PrismaticJoint::GetLowerLimit() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetLowerLimit()); +} + +float PrismaticJoint::GetUpperLimit() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetUpperLimit()); +} + +void PrismaticJoint::SetLimits(float lower, float upper) +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + raw_joint_->SetLimits(GetWorld()->Stage2World(lower), GetWorld()->Stage2World(upper)); +} + +// +// PulleyJoint +// + +PulleyJoint::PulleyJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool PulleyJoint::InitJoint(World* world, PulleyJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2PulleyJointDef def; + def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.ground_anchor_a), + world->Stage2World(param.ground_anchor_b), world->Stage2World(param.anchor_a), + world->Stage2World(param.anchor_b), param.ratio); + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +Point PulleyJoint::GetGroundAnchorA() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetGroundAnchorA()); +} + +Point PulleyJoint::GetGroundAnchorB() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetGroundAnchorB()); +} + +float PulleyJoint::GetRatio() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetRatio(); +} + +float PulleyJoint::GetLengthA() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetLengthA()); +} + +float PulleyJoint::GetLengthB() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetLengthB()); +} + +float PulleyJoint::GetCurrentLengthA() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetCurrentLengthA()); +} + +float PulleyJoint::GetCurrentLengthB() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetCurrentLengthB()); +} + +// +// RevoluteJoint +// + +RevoluteJoint::RevoluteJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool RevoluteJoint::InitJoint(World* world, RevoluteJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2RevoluteJointDef def; + def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor)); + def.enableLimit = param.enable_limit; + def.lowerAngle = math::Degree2Radian(param.lower_angle); + def.upperAngle = math::Degree2Radian(param.upper_angle); + def.enableMotor = param.enable_motor; + def.maxMotorTorque = world->Stage2World(param.max_motor_torque); + def.motorSpeed = math::Degree2Radian(param.motor_speed); + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +float RevoluteJoint::GetJointAngle() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return math::Radian2Degree(raw_joint_->GetJointAngle()); +} + +float RevoluteJoint::GetJointSpeed() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return math::Radian2Degree(raw_joint_->GetJointSpeed()); +} + +float RevoluteJoint::GetLowerLimit() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return math::Radian2Degree(raw_joint_->GetLowerLimit()); +} + +float RevoluteJoint::GetUpperLimit() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return math::Radian2Degree(raw_joint_->GetUpperLimit()); +} + +void RevoluteJoint::SetLimits(float lower, float upper) +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + raw_joint_->SetLimits(math::Degree2Radian(lower), math::Degree2Radian(upper)); +} + +void RevoluteJoint::SetMaxMotorTorque(float torque) +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + raw_joint_->SetMaxMotorTorque(GetWorld()->Stage2World(torque)); +} + +float RevoluteJoint::GetMaxMotorTorque() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetMaxMotorTorque()); +} + +// +// RopeJoint +// + +RopeJoint::RopeJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool RopeJoint::InitJoint(World* world, RopeJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2RopeJointDef def; + def.bodyA = param.body_a->GetB2Body(); + def.bodyB = param.body_b->GetB2Body(); + def.localAnchorA = world->Stage2World(param.local_anchor_a); + def.localAnchorB = world->Stage2World(param.local_anchor_b); + def.maxLength = world->Stage2World(param.max_length); + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +void RopeJoint::SetMaxLength(float length) +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + raw_joint_->SetMaxLength(GetWorld()->Stage2World(length)); +} + +float RopeJoint::GetMaxLength() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetMaxLength()); +} + +// +// WeldJoint +// + +WeldJoint::WeldJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool WeldJoint::InitJoint(World* world, WeldJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2WeldJointDef def; + def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor)); + def.frequencyHz = param.frequency_hz; + def.dampingRatio = param.damping_ratio; + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +// +// WheelJoint +// + +WheelJoint::WheelJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool WheelJoint::InitJoint(World* world, WheelJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2WheelJointDef def; + def.Initialize(param.body_a->GetB2Body(), param.body_b->GetB2Body(), world->Stage2World(param.anchor), + Stage2World(param.axis)); + def.enableMotor = param.enable_motor; + def.maxMotorTorque = world->Stage2World(param.max_motor_torque); + def.motorSpeed = world->Stage2World(param.motor_speed); + def.frequencyHz = param.frequency_hz; + def.dampingRatio = param.damping_ratio; + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +float WheelJoint::GetJointTranslation() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetJointTranslation()); +} + +float WheelJoint::GetJointLinearSpeed() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetJointLinearSpeed()); +} + +void WheelJoint::SetMaxMotorTorque(float torque) +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + raw_joint_->SetMaxMotorTorque(GetWorld()->Stage2World(torque)); +} + +float WheelJoint::GetMaxMotorTorque() const +{ + KGE_ASSERT(raw_joint_ && GetWorld()); + return GetWorld()->World2Stage(raw_joint_->GetMaxMotorTorque()); +} + +// +// MouseJoint +// + +MouseJoint::MouseJoint() + : Joint() + , raw_joint_(nullptr) +{ +} + +bool MouseJoint::InitJoint(World* world, MouseJoint::Param const& param) +{ + KGE_ASSERT(param.body_a && param.body_b); + + b2MouseJointDef def; + def.bodyA = param.body_a->GetB2Body(); + def.bodyB = param.body_b->GetB2Body(); + def.target = world->Stage2World(param.target); + def.maxForce = param.max_force; + def.frequencyHz = param.frequency_hz; + def.dampingRatio = param.damping_ratio; + + Joint::InitJoint(world, &def); + raw_joint_ = static_cast(GetB2Joint()); + return raw_joint_ != nullptr; +} + +void MouseJoint::SetMaxForce(float length) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetMaxForce(length); +} + +float MouseJoint::GetMaxForce() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetMaxForce(); +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/Joint.h b/src/kiwano-physics/Joint.h index 85881ecc..995fc876 100644 --- a/src/kiwano-physics/Joint.h +++ b/src/kiwano-physics/Joint.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -19,1037 +19,1094 @@ // THE SOFTWARE. #pragma once -#include #include +#include namespace kiwano { - namespace physics - { - KGE_DECLARE_SMART_PTR(Joint); - KGE_DECLARE_SMART_PTR(DistanceJoint); - KGE_DECLARE_SMART_PTR(FrictionJoint); - KGE_DECLARE_SMART_PTR(GearJoint); - KGE_DECLARE_SMART_PTR(MotorJoint); - KGE_DECLARE_SMART_PTR(MouseJoint); - KGE_DECLARE_SMART_PTR(PrismaticJoint); - KGE_DECLARE_SMART_PTR(PulleyJoint); - KGE_DECLARE_SMART_PTR(RevoluteJoint); - KGE_DECLARE_SMART_PTR(RopeJoint); - KGE_DECLARE_SMART_PTR(WeldJoint); - KGE_DECLARE_SMART_PTR(WheelJoint); - - /** - * \addtogroup Physics - * @{ - */ - - /// \~chinese - /// @brief ؽ - class KGE_API Joint - : public virtual RefCounter - { - public: - /// \~chinese - /// @brief ؽ - enum class Type - { - Unknown = 0, ///< δ֪ - Revolute, ///< תؽ - Prismatic, ///< ƽƹؽ - Distance, ///< ̶ؽ - Pulley, ///< ֹؽ - Mouse, ///< ؽ - Gear, ///< ֹؽ - Wheel, ///< ֹؽ - Weld, ///< ӹؽ - Friction, ///< Ħؽ - Rope, ///< ؽ - Motor ///< ؽ - }; - - /// \~chinese - /// @brief ؽڻ - struct ParamBase - { - Body* body_a; ///< ؽӵA - Body* body_b; ///< ؽӵB - - ParamBase(Body* body_a, Body* body_b) - : body_a(body_a), body_b(body_b) - { - } - - ParamBase(BodyPtr body_a, BodyPtr body_b) - : body_a(body_a.get()), body_b(body_b.get()) - { - } - }; - - Joint(); - Joint(b2Joint* joint); - Joint(World* world, b2JointDef* joint_def); - virtual ~Joint(); - - /// \~chinese - /// @brief ʼؽ - void Init(World* world, b2JointDef* joint_def); - - /// \~chinese - /// @brief ȡؽӵA - BodyPtr GetBodyA() const; - - /// \~chinese - /// @brief ȡؽӵB - BodyPtr GetBodyB() const; - - /// \~chinese - /// @brief ȡ - World* GetWorld() const; - - b2Joint* GetB2Joint() const; - void SetB2Joint(b2Joint* joint); - - private: - b2Joint* joint_; - World* world_; - Type type_; - }; - - - /// \~chinese - /// @brief ̶ؽ - class KGE_API DistanceJoint - : public Joint - { - public: - /// \~chinese - /// @brief ̶ؽڲ - struct Param : public Joint::ParamBase - { - Point anchor_a; ///< ؽAϵӵ - Point anchor_b; ///< ؽBϵӵ - float frequency_hz; ///< ӦٶȣֵԽ߹ؽӦٶԽ죬ȥԽ - float damping_ratio; ///< ʣֵԽؽ˶Խ - - Param( - Body* body_a, - Body* body_b, - Point const& anchor_a, - Point const& anchor_b, - float frequency_hz = 0.f, - float damping_ratio = 0.f - ) - : ParamBase(body_a, body_b) - , anchor_a(anchor_a) - , anchor_b(anchor_b) - , frequency_hz(frequency_hz) - , damping_ratio(damping_ratio) - {} - - Param( - BodyPtr body_a, - BodyPtr body_b, - Point const& anchor_a, - Point const& anchor_b, - float frequency_hz = 0.f, - float damping_ratio = 0.f - ) - : Param(body_a.get(), body_b.get(), anchor_a, anchor_b, frequency_hz, damping_ratio) - {} - }; - - DistanceJoint(); - DistanceJoint(World* world, b2DistanceJointDef* def); - DistanceJoint(World* world, Param const& param); - - /// \~chinese - /// @brief ùؽڳ - void SetLength(float length); - - /// \~chinese - /// @brief ȡؽڳ - float GetLength() const; - - /// \~chinese - /// @brief õӦٶ [] - void SetFrequency(float hz); - - /// \~chinese - /// @brief ȡӦٶ [] - float GetFrequency() const; - - /// \~chinese - /// @brief - void SetDampingRatio(float ratio); - - /// \~chinese - /// @brief ȡ - float GetDampingRatio() const; - - private: - b2DistanceJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief Ħؽ - class KGE_API FrictionJoint - : public Joint - { - public: - struct Param : public Joint::ParamBase - { - Point anchor; ///< Ħõ - float max_force; ///< Ħ - float max_torque; ///< Ť - - Param( - Body* body_a, - Body* body_b, - Point const& anchor, - float max_force = 0.f, - float max_torque = 0.f - ) - : ParamBase(body_a, body_b) - , anchor(anchor) - , max_force(max_force) - , max_torque(max_torque) - {} - - Param( - BodyPtr body_a, - BodyPtr body_b, - Point const& anchor, - float max_force = 0.f, - float max_torque = 0.f - ) - : Param(body_a.get(), body_b.get(), anchor, max_force, max_torque) - {} - }; - - FrictionJoint(); - FrictionJoint(World* world, b2FrictionJointDef* def); - FrictionJoint(World* world, Param const& param); - - /// \~chinese - /// @brief Ħ - void SetMaxForce(float force); - - /// \~chinese - /// @brief ȡĦ - float GetMaxForce() const; - - /// \~chinese - /// @brief ת - void SetMaxTorque(float torque); - - /// \~chinese - /// @brief ȡת - float GetMaxTorque() const; - - private: - b2FrictionJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief ֹؽ - class KGE_API GearJoint - : public Joint - { - public: - /// \~chinese - /// @brief ֹؽڲ - struct Param : public Joint::ParamBase - { - JointPtr joint_a; ///< ؽAתؽ/ƽƹؽڣ - JointPtr joint_b; ///< ؽBתؽ/ƽƹؽڣ - float ratio; ///< ִ - - Param( - Joint* joint_a, - Joint* joint_b, - float ratio = 1.f - ) - : ParamBase(nullptr, nullptr) - , joint_a(joint_a) - , joint_b(joint_b) - , ratio(ratio) - {} - - Param( - JointPtr joint_a, - JointPtr joint_b, - float ratio = 1.f - ) - : Param(joint_a.get(), joint_b.get(), ratio) - {} - }; - - GearJoint(); - GearJoint(World* world, b2GearJointDef* def); - GearJoint(World* world, Param param); - - /// \~chinese - /// @brief 趨ִ - void SetRatio(float ratio); - - /// \~chinese - /// @brief ȡִ - float GetRatio() const; - - private: - b2GearJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief ؽ - class KGE_API MotorJoint - : public Joint - { - public: - /// \~chinese - /// @brief ؽڲ - struct Param : public Joint::ParamBase - { - float max_force; ///< Ħ - float max_torque; ///< ת - float correction_factor; ///< λýӣΧ 0-1 - - Param( - Body* body_a, - Body* body_b, - float max_force = 1.f, - float max_torque = 100.f, - float correction_factor = 0.3f - ) - : ParamBase(body_a, body_b) - , max_force(max_force) - , max_torque(max_torque) - , correction_factor(correction_factor) - {} - - Param( - BodyPtr body_a, - BodyPtr body_b, - float max_force = 0.f, - float max_torque = 0.f, - float correction_factor = 0.3f - ) - : Param(body_a.get(), body_b.get(), max_force, max_torque, correction_factor) - {} - }; - - MotorJoint(); - MotorJoint(World* world, b2MotorJointDef* def); - MotorJoint(World* world, Param const& param); - - /// \~chinese - /// @brief Ħ - void SetMaxForce(float force); - - /// \~chinese - /// @brief ȡĦ - float GetMaxForce() const; - - /// \~chinese - /// @brief ת - void SetMaxTorque(float torque); - - /// \~chinese - /// @brief ȡת - float GetMaxTorque() const; - - private: - b2MotorJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief ƽƹؽ - class KGE_API PrismaticJoint - : public Joint - { - public: - /// \~chinese - /// @brief ƽƹؽڲ - struct Param : public Joint::ParamBase - { - Point anchor; ///< ؽλ - Vec2 axis; ///< Aķ - bool enable_limit; ///< Ƿ - float lower_translation; ///< ƶСƣ뷽ͬΪΪƺЧ - float upper_translation; ///< ƶƣ뷽ͬΪΪƺЧ - bool enable_motor; ///< Ƿ - float max_motor_force; ///< [N] - float motor_speed; ///< ת [degree/s] - - Param( - Body* body_a, - Body* body_b, - Point const& anchor, - Vec2 const& axis, - bool enable_limit = false, - float lower_translation = 0.0f, - float upper_translation = 0.0f, - bool enable_motor = false, - float max_motor_force = 0.0f, - float motor_speed = 0.0f - ) - : ParamBase(body_a, body_b) - , anchor(anchor) - , axis(axis) - , enable_limit(enable_limit) - , lower_translation(lower_translation) - , upper_translation(upper_translation) - , enable_motor(enable_motor) - , max_motor_force(max_motor_force) - , motor_speed(motor_speed) - {} - - Param( - BodyPtr body_a, - BodyPtr body_b, - Point const& anchor, - Vec2 const& axis, - bool enable_limit = false, - float lower_translation = 0.0f, - float upper_translation = 0.0f, - bool enable_motor = false, - float max_motor_force = 0.0f, - float motor_speed = 0.0f - ) - : Param(body_a.get(), body_b.get(), anchor, axis, enable_limit, lower_translation, upper_translation, enable_motor, max_motor_force, motor_speed) - {} - }; - - PrismaticJoint(); - PrismaticJoint(World* world, b2PrismaticJointDef* def); - PrismaticJoint(World* world, Param const& param); - - /// \~chinese - /// @brief ȡο - float GetReferenceAngle() const; - - /// \~chinese - /// @brief ȡؽת - float GetJointTranslation() const; - - /// \~chinese - /// @brief ȡؽٶ - float GetJointSpeed() const; - - /// \~chinese - /// @brief Ƿùؽ - bool IsLimitEnabled() const; - - /// \~chinese - /// @brief Ƿùؽ - void EnableLimit(bool flag); - - /// \~chinese - /// @brief ȡƽС - float GetLowerLimit() const; - - /// \~chinese - /// @brief ȡƽ - float GetUpperLimit() const; - - /// \~chinese - /// @brief ùؽ - void SetLimits(float lower, float upper); - - /// \~chinese - /// @brief Ƿ - bool IsMotorEnabled() const; - - /// \~chinese - /// @brief Ƿ - void EnableMotor(bool flag); - - /// \~chinese - /// @brief ת [degree/s] - void SetMotorSpeed(float speed); - - /// \~chinese - /// @brief ȡת [degree/s] - float GetMotorSpeed() const; - - /// \~chinese - /// @brief [N] - void SetMaxMotorForce(float force); - - /// \~chinese - /// @brief ȡ [N] - float GetMaxMotorForce() const; - - private: - b2PrismaticJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief ֹؽ - class KGE_API PulleyJoint - : public Joint - { - public: - /// \~chinese - /// @brief ֹؽڲ - struct Param : public Joint::ParamBase - { - Point anchor_a; ///< ؽAϵõ - Point anchor_b; ///< ؽBϵõ - Point ground_anchor_a; ///< AӦĻֵλ - Point ground_anchor_b; ///< BӦĻֵλ - float ratio; ///< ֱȣؽڴʱ½ͷλƱ - - Param( - Body* body_a, - Body* body_b, - Point const& anchor_a, - Point const& anchor_b, - Point const& ground_anchor_a, - Point const& ground_anchor_b, - float ratio = 1.0f - ) - : ParamBase(body_a, body_b) - , anchor_a(anchor_a) - , anchor_b(anchor_b) - , ground_anchor_a(ground_anchor_a) - , ground_anchor_b(ground_anchor_b) - , ratio(ratio) - {} - - Param( - BodyPtr body_a, - BodyPtr body_b, - Point const& anchor_a, - Point const& anchor_b, - Point const& ground_anchor_a, - Point const& ground_anchor_b, - float ratio = 1.0f - ) - : Param(body_a.get(), body_b.get(), anchor_a, anchor_b, ground_anchor_a, ground_anchor_b, ratio) - {} - }; - - PulleyJoint(); - PulleyJoint(World* world, b2PulleyJointDef* def); - PulleyJoint(World* world, Param const& param); - - /// \~chinese - /// @brief AӦĻֵλ - Point GetGroundAnchorA() const; - - /// \~chinese - /// @brief BӦĻֵλ - Point GetGroundAnchorB() const; - - /// \~chinese - /// @brief ȡִ - float GetRatio() const; - - /// \~chinese - /// @brief ȡA뻬ֵľ - float GetLengthA() const; - - /// \~chinese - /// @brief ȡB뻬ֵľ - float GetLengthB() const; - - /// \~chinese - /// @brief ȡA뻬ֵĵǰ - float GetCurrentLengthA() const; - - /// \~chinese - /// @brief ȡB뻬ֵĵǰ - float GetCurrentLengthB() const; - - private: - b2PulleyJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief תؽ - class KGE_API RevoluteJoint - : public Joint - { - public: - /// \~chinese - /// @brief תؽڲ - struct Param : public Joint::ParamBase - { - Point anchor; ///< ؽλ - bool enable_limit; ///< Ƿ - float lower_angle; ///< ƶСƣ뷽ͬΪΪƺЧ - float upper_angle; ///< ƶƣ뷽ͬΪΪƺЧ - bool enable_motor; ///< Ƿ - float max_motor_torque; ///< [N] - float motor_speed; ///< ת [degree/s] - - Param( - Body* body_a, - Body* body_b, - Point const& anchor, - bool enable_limit = false, - float lower_angle = 0.0f, - float upper_angle = 0.0f, - bool enable_motor = false, - float max_motor_torque = 0.0f, - float motor_speed = 0.0f - ) - : ParamBase(body_a, body_b) - , anchor(anchor) - , enable_limit(enable_limit) - , lower_angle(lower_angle) - , upper_angle(upper_angle) - , enable_motor(enable_motor) - , max_motor_torque(max_motor_torque) - , motor_speed(motor_speed) - {} - - Param( - BodyPtr body_a, - BodyPtr body_b, - Point const& anchor, - bool enable_limit = false, - float lower_angle = 0.0f, - float upper_angle = 0.0f, - bool enable_motor = false, - float max_motor_torque = 0.0f, - float motor_speed = 0.0f - ) - : Param(body_a.get(), body_b.get(), anchor, enable_limit, lower_angle, upper_angle, enable_motor, max_motor_torque, motor_speed) - {} - }; - - RevoluteJoint(); - RevoluteJoint(World* world, b2RevoluteJointDef* def); - RevoluteJoint(World* world, Param const& param); - - /// \~chinese - /// @brief ȡο - float GetReferenceAngle() const; - - /// \~chinese - /// @brief ȡؽڽǶ - float GetJointAngle() const; - - /// \~chinese - /// @brief ȡؽٶ - float GetJointSpeed() const; - - /// \~chinese - /// @brief Ƿùؽ - bool IsLimitEnabled() const; - - /// \~chinese - /// @brief Ƿùؽ - void EnableLimit(bool flag); - - /// \~chinese - /// @brief ȡƽС - float GetLowerLimit() const; - - /// \~chinese - /// @brief ȡƽ - float GetUpperLimit() const; - - /// \~chinese - /// @brief ùؽ - void SetLimits(float lower, float upper); - - /// \~chinese - /// @brief Ƿ - bool IsMotorEnabled() const; - - /// \~chinese - /// @brief Ƿ - void EnableMotor(bool flag); - - /// \~chinese - /// @brief ת [degree/s] - void SetMotorSpeed(float speed); - - /// \~chinese - /// @brief ȡת [degree/s] - float GetMotorSpeed() const; - - /// \~chinese - /// @brief ת [N/m] - void SetMaxMotorTorque(float torque); - - /// \~chinese - /// @brief ȡת [N/m] - float GetMaxMotorTorque() const; - - private: - b2RevoluteJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief ؽ - class KGE_API RopeJoint - : public Joint - { - public: - /// \~chinese - /// @brief ؽڲ - struct Param : public Joint::ParamBase - { - Point local_anchor_a; ///< ؽAϵӵ - Point local_anchor_b; ///< ؽBϵӵ - float max_length; ///< 󳤶 - - Param( - Body* body_a, - Body* body_b, - Point const& local_anchor_a, - Point const& local_anchor_b, - float max_length = 0.f - ) - : ParamBase(body_a, body_b) - , local_anchor_a(local_anchor_a) - , local_anchor_b(local_anchor_b) - , max_length(max_length) - {} - - Param( - BodyPtr body_a, - BodyPtr body_b, - Point const& local_anchor_a, - Point const& local_anchor_b, - float max_length = 0.f - ) - : Param(body_a.get(), body_b.get(), local_anchor_a, local_anchor_b, max_length) - {} - }; - - RopeJoint(); - RopeJoint(World* world, b2RopeJointDef* def); - RopeJoint(World* world, Param const& param); - - /// \~chinese - /// @brief ùؽ󳤶 - void SetMaxLength(float length); - - /// \~chinese - /// @brief ȡؽ󳤶 - float GetMaxLength() const; - - private: - b2RopeJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief ӹؽ - class KGE_API WeldJoint - : public Joint - { - public: - /// \~chinese - /// @brief ӹؽڲ - struct Param : public Joint::ParamBase - { - Point anchor; ///< λ - float frequency_hz; ///< ӦٶȣֵԽ߹ؽӦٶԽ죬ȥԽ - float damping_ratio; ///< ʣֵԽؽ˶Խ - - Param( - Body* body_a, - Body* body_b, - Point const& anchor, - float frequency_hz = 0.f, - float damping_ratio = 0.f - ) - : ParamBase(body_a, body_b) - , anchor(anchor) - , frequency_hz(frequency_hz) - , damping_ratio(damping_ratio) - {} - - Param( - BodyPtr body_a, - BodyPtr body_b, - Point const& anchor, - float frequency_hz = 0.f, - float damping_ratio = 0.f - ) - : Param(body_a.get(), body_b.get(), anchor, frequency_hz, damping_ratio) - {} - }; - - WeldJoint(); - WeldJoint(World* world, b2WeldJointDef* def); - WeldJoint(World* world, Param const& param); - - /// \~chinese - /// @brief ȡBAĽǶ - float GetReferenceAngle() const; - - /// \~chinese - /// @brief õӦٶ [] - void SetFrequency(float hz); - - /// \~chinese - /// @brief ȡӦٶ [] - float GetFrequency() const; - - /// \~chinese - /// @brief - void SetDampingRatio(float ratio); - - /// \~chinese - /// @brief ȡ - float GetDampingRatio() const; - - private: - b2WeldJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief ֹؽ - class KGE_API WheelJoint - : public Joint - { - public: - /// \~chinese - /// @brief ֹؽڲ - struct Param : public Joint::ParamBase - { - Point anchor; ///< ֹؽλ - Vec2 axis; ///< A - bool enable_motor; ///< Ƿ - float max_motor_torque; ///< [N] - float motor_speed; ///< ת [degree/s] - float frequency_hz; ///< ӦٶȣֵԽ߹ؽӦٶԽ죬ȥԽ - float damping_ratio; ///< ʣֵԽؽ˶Խ - - Param( - Body* body_a, - Body* body_b, - Point const& anchor, - Vec2 const& axis, - float frequency_hz = 2.0f, - float damping_ratio = 0.7f, - bool enable_motor = false, - float max_motor_torque = 0.0f, - float motor_speed = 0.0f - ) - : ParamBase(body_a, body_b) - , anchor(anchor) - , axis(axis) - , enable_motor(enable_motor) - , max_motor_torque(max_motor_torque) - , motor_speed(motor_speed) - , frequency_hz(frequency_hz) - , damping_ratio(damping_ratio) - {} - - Param( - BodyPtr body_a, - BodyPtr body_b, - Point const& anchor, - Vec2 const& axis, - float frequency_hz = 2.0f, - float damping_ratio = 0.7f, - bool enable_motor = false, - float max_motor_torque = 0.0f, - float motor_speed = 0.0f - ) - : Param(body_a.get(), body_b.get(), anchor, axis, frequency_hz, damping_ratio, enable_motor, max_motor_torque, motor_speed) - {} - }; - - WheelJoint(); - WheelJoint(World* world, b2WheelJointDef* def); - WheelJoint(World* world, Param const& param); - - /// \~chinese - /// @brief ȡؽڵǰƽƾ - float GetJointTranslation() const; - - /// \~chinese - /// @brief ȡؽڵǰٶ - float GetJointLinearSpeed() const; - - /// \~chinese - /// @brief ȡؽڵǰĽǶ - float GetJointAngle() const; - - /// \~chinese - /// @brief ȡؽڵǰתٶ - float GetJointAngularSpeed() const; - - /// \~chinese - /// @brief Ƿ - bool IsMotorEnabled() const; - - /// \~chinese - /// @brief Ƿ - void EnableMotor(bool flag); - - /// \~chinese - /// @brief ת [degree/s] - void SetMotorSpeed(float speed); - - /// \~chinese - /// @brief ȡת [degree/s] - float GetMotorSpeed() const; - - /// \~chinese - /// @brief ת [N/m] - void SetMaxMotorTorque(float torque); - - /// \~chinese - /// @brief ȡת [N/m] - float GetMaxMotorTorque() const; - - /// \~chinese - /// @brief õӦٶ - void SetSpringFrequencyHz(float hz); - - /// \~chinese - /// @brief ȡӦٶ - float GetSpringFrequencyHz() const; - - /// \~chinese - /// @brief õ - void SetSpringDampingRatio(float ratio); - - /// \~chinese - /// @brief ȡ - float GetSpringDampingRatio() const; - - private: - b2WheelJoint* raw_joint_; - }; - - - /// \~chinese - /// @brief ؽ - /// @details ʹij׷ϵָ㣬׷λ - class KGE_API MouseJoint - : public Joint - { - public: - /// \~chinese - /// @brief ؽڲ - struct Param : public Joint::ParamBase - { - Point target; ///< ؽĿλ - float max_force; ///< Aϵ - float frequency_hz; ///< ӦٶȣֵԽ߹ؽӦٶԽ죬ȥԽ - float damping_ratio; ///< ʣֵԽؽ˶Խ - - Param( - Body* body_a, - Body* body_b, - Point const& target, - float max_force, - float frequency_hz = 5.0f, - float damping_ratio = 0.7f - ) - : ParamBase(body_a, body_b) - , target(target) - , max_force(max_force) - , frequency_hz(frequency_hz) - , damping_ratio(damping_ratio) - {} - - Param(BodyPtr body_a, BodyPtr body_b, Point const& target, float max_force, float frequency_hz = 5.0f, float damping_ratio = 0.7f) - : Param(body_a.get(), body_b.get(), target, max_force, frequency_hz, damping_ratio) - {} - }; - - MouseJoint(); - MouseJoint(World* world, b2MouseJointDef* def); - MouseJoint(World* world, Param const& param); - - /// \~chinese - /// @brief 趨Ħ [N] - void SetMaxForce(float force); - - /// \~chinese - /// @brief ȡĦ [N] - float GetMaxForce() const; - - /// \~chinese - /// @brief Ӧٶ [hz] - void SetFrequency(float hz); - - /// \~chinese - /// @brief ȡӦٶ [hz] - float GetFrequency() const; - - /// \~chinese - /// @brief - void SetDampingRatio(float ratio); - - /// \~chinese - /// @brief ȡ - float GetDampingRatio() const; - - private: - b2MouseJoint* raw_joint_; - }; - - /** @} */ - - - inline b2Joint* Joint::GetB2Joint() const { return joint_; } - inline World* Joint::GetWorld() const { return world_; } - - inline void DistanceJoint::SetFrequency(float hz) { KGE_ASSERT(raw_joint_); raw_joint_->SetFrequency(hz); } - inline float DistanceJoint::GetFrequency() const { KGE_ASSERT(raw_joint_); return raw_joint_->GetFrequency(); } - inline void DistanceJoint::SetDampingRatio(float ratio) { KGE_ASSERT(raw_joint_); raw_joint_->SetDampingRatio(ratio); } - inline float DistanceJoint::GetDampingRatio() const { KGE_ASSERT(raw_joint_); return raw_joint_->GetDampingRatio(); } - - inline float PrismaticJoint::GetReferenceAngle() const { KGE_ASSERT(raw_joint_); return math::Radian2Degree(raw_joint_->GetReferenceAngle()); } - inline bool PrismaticJoint::IsLimitEnabled() const { KGE_ASSERT(raw_joint_); return raw_joint_->IsLimitEnabled(); } - inline void PrismaticJoint::EnableLimit(bool flag) { KGE_ASSERT(raw_joint_); raw_joint_->EnableLimit(flag); } - inline bool PrismaticJoint::IsMotorEnabled() const { KGE_ASSERT(raw_joint_); return raw_joint_->IsMotorEnabled(); } - inline void PrismaticJoint::EnableMotor(bool flag) { KGE_ASSERT(raw_joint_); raw_joint_->EnableMotor(flag); } - inline void PrismaticJoint::SetMotorSpeed(float speed) { KGE_ASSERT(raw_joint_); raw_joint_->SetMotorSpeed(math::Degree2Radian(speed)); } - inline float PrismaticJoint::GetMotorSpeed() const { KGE_ASSERT(raw_joint_); return math::Radian2Degree(raw_joint_->GetMotorSpeed()); } - inline void PrismaticJoint::SetMaxMotorForce(float force) { KGE_ASSERT(raw_joint_); raw_joint_->SetMaxMotorForce(force); } - inline float PrismaticJoint::GetMaxMotorForce() const { KGE_ASSERT(raw_joint_); return raw_joint_->GetMaxMotorForce(); } - - inline float RevoluteJoint::GetReferenceAngle() const { KGE_ASSERT(raw_joint_); return math::Radian2Degree(raw_joint_->GetReferenceAngle()); } - inline bool RevoluteJoint::IsLimitEnabled() const { KGE_ASSERT(raw_joint_); return raw_joint_->IsLimitEnabled(); } - inline void RevoluteJoint::EnableLimit(bool flag) { KGE_ASSERT(raw_joint_); raw_joint_->EnableLimit(flag); } - inline bool RevoluteJoint::IsMotorEnabled() const { KGE_ASSERT(raw_joint_); return raw_joint_->IsMotorEnabled(); } - inline void RevoluteJoint::EnableMotor(bool flag) { KGE_ASSERT(raw_joint_); raw_joint_->EnableMotor(flag); } - inline void RevoluteJoint::SetMotorSpeed(float speed) { KGE_ASSERT(raw_joint_); raw_joint_->SetMotorSpeed(math::Degree2Radian(speed)); } - inline float RevoluteJoint::GetMotorSpeed() const { KGE_ASSERT(raw_joint_); return math::Radian2Degree(raw_joint_->GetMotorSpeed()); } - - inline float WeldJoint::GetReferenceAngle() const { KGE_ASSERT(raw_joint_); return math::Radian2Degree(raw_joint_->GetReferenceAngle()); } - inline void WeldJoint::SetFrequency(float hz) { KGE_ASSERT(raw_joint_); raw_joint_->SetFrequency(hz); } - inline float WeldJoint::GetFrequency() const { KGE_ASSERT(raw_joint_); return raw_joint_->GetFrequency(); } - inline void WeldJoint::SetDampingRatio(float ratio) { KGE_ASSERT(raw_joint_); raw_joint_->SetDampingRatio(ratio); } - inline float WeldJoint::GetDampingRatio() const { KGE_ASSERT(raw_joint_); return raw_joint_->GetDampingRatio(); } - - inline float WheelJoint::GetJointAngle() const { KGE_ASSERT(raw_joint_); return math::Radian2Degree(raw_joint_->GetJointAngle()); } - inline float WheelJoint::GetJointAngularSpeed() const { KGE_ASSERT(raw_joint_); return math::Radian2Degree(raw_joint_->GetJointAngularSpeed()); } - inline bool WheelJoint::IsMotorEnabled() const { KGE_ASSERT(raw_joint_); return raw_joint_->IsMotorEnabled(); } - inline void WheelJoint::EnableMotor(bool flag) { KGE_ASSERT(raw_joint_); raw_joint_->EnableMotor(flag); } - inline void WheelJoint::SetMotorSpeed(float speed) { KGE_ASSERT(raw_joint_); raw_joint_->SetMotorSpeed(math::Degree2Radian(speed)); } - inline float WheelJoint::GetMotorSpeed() const { KGE_ASSERT(raw_joint_); return math::Radian2Degree(raw_joint_->GetMotorSpeed()); } - inline void WheelJoint::SetSpringFrequencyHz(float hz) { KGE_ASSERT(raw_joint_); raw_joint_->SetSpringFrequencyHz(hz); } - inline float WheelJoint::GetSpringFrequencyHz() const { KGE_ASSERT(raw_joint_); return raw_joint_->GetSpringFrequencyHz(); } - inline void WheelJoint::SetSpringDampingRatio(float ratio) { KGE_ASSERT(raw_joint_); raw_joint_->SetSpringDampingRatio(ratio); } - inline float WheelJoint::GetSpringDampingRatio() const { KGE_ASSERT(raw_joint_); return raw_joint_->GetSpringDampingRatio(); } - - inline void MouseJoint::SetFrequency(float hz) { KGE_ASSERT(raw_joint_); raw_joint_->SetFrequency(hz); } - inline float MouseJoint::GetFrequency() const { KGE_ASSERT(raw_joint_); return raw_joint_->GetFrequency(); } - inline void MouseJoint::SetDampingRatio(float ratio) { KGE_ASSERT(raw_joint_); raw_joint_->SetDampingRatio(ratio); } - inline float MouseJoint::GetDampingRatio() const { KGE_ASSERT(raw_joint_); return raw_joint_->GetDampingRatio(); } - } +namespace physics +{ +KGE_DECLARE_SMART_PTR(Joint); +KGE_DECLARE_SMART_PTR(DistanceJoint); +KGE_DECLARE_SMART_PTR(FrictionJoint); +KGE_DECLARE_SMART_PTR(GearJoint); +KGE_DECLARE_SMART_PTR(MotorJoint); +KGE_DECLARE_SMART_PTR(MouseJoint); +KGE_DECLARE_SMART_PTR(PrismaticJoint); +KGE_DECLARE_SMART_PTR(PulleyJoint); +KGE_DECLARE_SMART_PTR(RevoluteJoint); +KGE_DECLARE_SMART_PTR(RopeJoint); +KGE_DECLARE_SMART_PTR(WeldJoint); +KGE_DECLARE_SMART_PTR(WheelJoint); + +/** + * \addtogroup Physics + * @{ + */ + +/// \~chinese +/// @brief ؽ +class KGE_API Joint : public virtual ObjectBase +{ +public: + /// \~chinese + /// @brief ؽ + enum class Type + { + Unknown = 0, ///< δ֪ + Revolute, ///< תؽ + Prismatic, ///< ƽƹؽ + Distance, ///< ̶ؽ + Pulley, ///< ֹؽ + Mouse, ///< ؽ + Gear, ///< ֹؽ + Wheel, ///< ֹؽ + Weld, ///< ӹؽ + Friction, ///< Ħؽ + Rope, ///< ؽ + Motor ///< ؽ + }; + + /// \~chinese + /// @brief ؽڻ + struct ParamBase + { + Body* body_a; ///< ؽӵA + Body* body_b; ///< ؽӵB + + ParamBase(Body* body_a, Body* body_b) + : body_a(body_a) + , body_b(body_b) + { + } + + ParamBase(BodyPtr body_a, BodyPtr body_b) + : body_a(body_a.get()) + , body_b(body_b.get()) + { + } + }; + + Joint(); + + virtual ~Joint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, b2JointDef* joint_def); + + /// \~chinese + /// @brief ȡؽӵA + BodyPtr GetBodyA() const; + + /// \~chinese + /// @brief ȡؽӵB + BodyPtr GetBodyB() const; + + /// \~chinese + /// @brief ȡ + World* GetWorld() const; + + /// \~chinese + /// @brief ٹؽ + void Destroy(); + + b2Joint* GetB2Joint() const; + void SetB2Joint(b2Joint* joint); + +private: + b2Joint* joint_; + World* world_; + Type type_; +}; + +/// \~chinese +/// @brief ̶ؽ +class KGE_API DistanceJoint : public Joint +{ +public: + /// \~chinese + /// @brief ̶ؽڲ + struct Param : public Joint::ParamBase + { + Point anchor_a; ///< ؽAϵӵ + Point anchor_b; ///< ؽBϵӵ + float frequency_hz; ///< ӦٶȣֵԽ߹ؽӦٶԽ죬ȥԽ + float damping_ratio; ///< ʣֵԽؽ˶Խ + + Param(Body* body_a, Body* body_b, Point const& anchor_a, Point const& anchor_b, float frequency_hz = 0.f, + float damping_ratio = 0.f) + : ParamBase(body_a, body_b) + , anchor_a(anchor_a) + , anchor_b(anchor_b) + , frequency_hz(frequency_hz) + , damping_ratio(damping_ratio) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, Point const& anchor_a, Point const& anchor_b, float frequency_hz = 0.f, + float damping_ratio = 0.f) + : Param(body_a.get(), body_b.get(), anchor_a, anchor_b, frequency_hz, damping_ratio) + { + } + }; + + DistanceJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief ùؽڳ + void SetLength(float length); + + /// \~chinese + /// @brief ȡؽڳ + float GetLength() const; + + /// \~chinese + /// @brief õӦٶ [] + void SetFrequency(float hz); + + /// \~chinese + /// @brief ȡӦٶ [] + float GetFrequency() const; + + /// \~chinese + /// @brief + void SetDampingRatio(float ratio); + + /// \~chinese + /// @brief ȡ + float GetDampingRatio() const; + +private: + b2DistanceJoint* raw_joint_; +}; + +/// \~chinese +/// @brief Ħؽ +class KGE_API FrictionJoint : public Joint +{ +public: + struct Param : public Joint::ParamBase + { + Point anchor; ///< Ħõ + float max_force; ///< Ħ + float max_torque; ///< Ť + + Param(Body* body_a, Body* body_b, Point const& anchor, float max_force = 0.f, float max_torque = 0.f) + : ParamBase(body_a, body_b) + , anchor(anchor) + , max_force(max_force) + , max_torque(max_torque) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, Point const& anchor, float max_force = 0.f, float max_torque = 0.f) + : Param(body_a.get(), body_b.get(), anchor, max_force, max_torque) + { + } + }; + + FrictionJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief Ħ + void SetMaxForce(float force); + + /// \~chinese + /// @brief ȡĦ + float GetMaxForce() const; + + /// \~chinese + /// @brief ת + void SetMaxTorque(float torque); + + /// \~chinese + /// @brief ȡת + float GetMaxTorque() const; + +private: + b2FrictionJoint* raw_joint_; +}; + +/// \~chinese +/// @brief ֹؽ +class KGE_API GearJoint : public Joint +{ +public: + /// \~chinese + /// @brief ֹؽڲ + struct Param : public Joint::ParamBase + { + Joint* joint_a; ///< ؽAתؽ/ƽƹؽڣ + Joint* joint_b; ///< ؽBתؽ/ƽƹؽڣ + float ratio; ///< ִ + + Param(Joint* joint_a, Joint* joint_b, float ratio = 1.f) + : ParamBase(nullptr, nullptr) + , joint_a(joint_a) + , joint_b(joint_b) + , ratio(ratio) + { + } + + Param(JointPtr joint_a, JointPtr joint_b, float ratio = 1.f) + : Param(joint_a.get(), joint_b.get(), ratio) + { + } + }; + + GearJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief 趨ִ + void SetRatio(float ratio); + + /// \~chinese + /// @brief ȡִ + float GetRatio() const; + +private: + b2GearJoint* raw_joint_; +}; + +/// \~chinese +/// @brief ؽ +class KGE_API MotorJoint : public Joint +{ +public: + /// \~chinese + /// @brief ؽڲ + struct Param : public Joint::ParamBase + { + float max_force; ///< Ħ + float max_torque; ///< ת + float correction_factor; ///< λýӣΧ 0-1 + + Param(Body* body_a, Body* body_b, float max_force = 1.f, float max_torque = 100.f, + float correction_factor = 0.3f) + : ParamBase(body_a, body_b) + , max_force(max_force) + , max_torque(max_torque) + , correction_factor(correction_factor) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, float max_force = 0.f, float max_torque = 0.f, + float correction_factor = 0.3f) + : Param(body_a.get(), body_b.get(), max_force, max_torque, correction_factor) + { + } + }; + + MotorJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief Ħ + void SetMaxForce(float force); + + /// \~chinese + /// @brief ȡĦ + float GetMaxForce() const; + + /// \~chinese + /// @brief ת + void SetMaxTorque(float torque); + + /// \~chinese + /// @brief ȡת + float GetMaxTorque() const; + +private: + b2MotorJoint* raw_joint_; +}; + +/// \~chinese +/// @brief ƽƹؽ +class KGE_API PrismaticJoint : public Joint +{ +public: + /// \~chinese + /// @brief ƽƹؽڲ + struct Param : public Joint::ParamBase + { + Point anchor; ///< ؽλ + Vec2 axis; ///< Aķ + bool enable_limit; ///< Ƿ + float lower_translation; ///< ƶСƣ뷽ͬΪΪƺЧ + float upper_translation; ///< ƶƣ뷽ͬΪΪƺЧ + bool enable_motor; ///< Ƿ + float max_motor_force; ///< [N] + float motor_speed; ///< ת [degree/s] + + Param(Body* body_a, Body* body_b, Point const& anchor, Vec2 const& axis, bool enable_limit = false, + float lower_translation = 0.0f, float upper_translation = 0.0f, bool enable_motor = false, + float max_motor_force = 0.0f, float motor_speed = 0.0f) + : ParamBase(body_a, body_b) + , anchor(anchor) + , axis(axis) + , enable_limit(enable_limit) + , lower_translation(lower_translation) + , upper_translation(upper_translation) + , enable_motor(enable_motor) + , max_motor_force(max_motor_force) + , motor_speed(motor_speed) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, Point const& anchor, Vec2 const& axis, bool enable_limit = false, + float lower_translation = 0.0f, float upper_translation = 0.0f, bool enable_motor = false, + float max_motor_force = 0.0f, float motor_speed = 0.0f) + : Param(body_a.get(), body_b.get(), anchor, axis, enable_limit, lower_translation, upper_translation, + enable_motor, max_motor_force, motor_speed) + { + } + }; + + PrismaticJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief ȡο + float GetReferenceAngle() const; + + /// \~chinese + /// @brief ȡؽת + float GetJointTranslation() const; + + /// \~chinese + /// @brief ȡؽٶ + float GetJointSpeed() const; + + /// \~chinese + /// @brief Ƿùؽ + bool IsLimitEnabled() const; + + /// \~chinese + /// @brief Ƿùؽ + void EnableLimit(bool flag); + + /// \~chinese + /// @brief ȡƽС + float GetLowerLimit() const; + + /// \~chinese + /// @brief ȡƽ + float GetUpperLimit() const; + + /// \~chinese + /// @brief ùؽ + void SetLimits(float lower, float upper); + + /// \~chinese + /// @brief Ƿ + bool IsMotorEnabled() const; + + /// \~chinese + /// @brief Ƿ + void EnableMotor(bool flag); + + /// \~chinese + /// @brief ת [degree/s] + void SetMotorSpeed(float speed); + + /// \~chinese + /// @brief ȡת [degree/s] + float GetMotorSpeed() const; + + /// \~chinese + /// @brief [N] + void SetMaxMotorForce(float force); + + /// \~chinese + /// @brief ȡ [N] + float GetMaxMotorForce() const; + +private: + b2PrismaticJoint* raw_joint_; +}; + +/// \~chinese +/// @brief ֹؽ +class KGE_API PulleyJoint : public Joint +{ +public: + /// \~chinese + /// @brief ֹؽڲ + struct Param : public Joint::ParamBase + { + Point anchor_a; ///< ؽAϵõ + Point anchor_b; ///< ؽBϵõ + Point ground_anchor_a; ///< AӦĻֵλ + Point ground_anchor_b; ///< BӦĻֵλ + float ratio; ///< ֱȣؽڴʱ½ͷλƱ + + Param(Body* body_a, Body* body_b, Point const& anchor_a, Point const& anchor_b, Point const& ground_anchor_a, + Point const& ground_anchor_b, float ratio = 1.0f) + : ParamBase(body_a, body_b) + , anchor_a(anchor_a) + , anchor_b(anchor_b) + , ground_anchor_a(ground_anchor_a) + , ground_anchor_b(ground_anchor_b) + , ratio(ratio) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, Point const& anchor_a, Point const& anchor_b, + Point const& ground_anchor_a, Point const& ground_anchor_b, float ratio = 1.0f) + : Param(body_a.get(), body_b.get(), anchor_a, anchor_b, ground_anchor_a, ground_anchor_b, ratio) + { + } + }; + + PulleyJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief AӦĻֵλ + Point GetGroundAnchorA() const; + + /// \~chinese + /// @brief BӦĻֵλ + Point GetGroundAnchorB() const; + + /// \~chinese + /// @brief ȡִ + float GetRatio() const; + + /// \~chinese + /// @brief ȡA뻬ֵľ + float GetLengthA() const; + + /// \~chinese + /// @brief ȡB뻬ֵľ + float GetLengthB() const; + + /// \~chinese + /// @brief ȡA뻬ֵĵǰ + float GetCurrentLengthA() const; + + /// \~chinese + /// @brief ȡB뻬ֵĵǰ + float GetCurrentLengthB() const; + +private: + b2PulleyJoint* raw_joint_; +}; + +/// \~chinese +/// @brief תؽ +class KGE_API RevoluteJoint : public Joint +{ +public: + /// \~chinese + /// @brief תؽڲ + struct Param : public Joint::ParamBase + { + Point anchor; ///< ؽλ + bool enable_limit; ///< Ƿ + float lower_angle; ///< ƶСƣ뷽ͬΪΪƺЧ + float upper_angle; ///< ƶƣ뷽ͬΪΪƺЧ + bool enable_motor; ///< Ƿ + float max_motor_torque; ///< [N] + float motor_speed; ///< ת [degree/s] + + Param(Body* body_a, Body* body_b, Point const& anchor, bool enable_limit = false, float lower_angle = 0.0f, + float upper_angle = 0.0f, bool enable_motor = false, float max_motor_torque = 0.0f, + float motor_speed = 0.0f) + : ParamBase(body_a, body_b) + , anchor(anchor) + , enable_limit(enable_limit) + , lower_angle(lower_angle) + , upper_angle(upper_angle) + , enable_motor(enable_motor) + , max_motor_torque(max_motor_torque) + , motor_speed(motor_speed) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, Point const& anchor, bool enable_limit = false, float lower_angle = 0.0f, + float upper_angle = 0.0f, bool enable_motor = false, float max_motor_torque = 0.0f, + float motor_speed = 0.0f) + : Param(body_a.get(), body_b.get(), anchor, enable_limit, lower_angle, upper_angle, enable_motor, + max_motor_torque, motor_speed) + { + } + }; + + RevoluteJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief ȡο + float GetReferenceAngle() const; + + /// \~chinese + /// @brief ȡؽڽǶ + float GetJointAngle() const; + + /// \~chinese + /// @brief ȡؽٶ + float GetJointSpeed() const; + + /// \~chinese + /// @brief Ƿùؽ + bool IsLimitEnabled() const; + + /// \~chinese + /// @brief Ƿùؽ + void EnableLimit(bool flag); + + /// \~chinese + /// @brief ȡƽС + float GetLowerLimit() const; + + /// \~chinese + /// @brief ȡƽ + float GetUpperLimit() const; + + /// \~chinese + /// @brief ùؽ + void SetLimits(float lower, float upper); + + /// \~chinese + /// @brief Ƿ + bool IsMotorEnabled() const; + + /// \~chinese + /// @brief Ƿ + void EnableMotor(bool flag); + + /// \~chinese + /// @brief ת [degree/s] + void SetMotorSpeed(float speed); + + /// \~chinese + /// @brief ȡת [degree/s] + float GetMotorSpeed() const; + + /// \~chinese + /// @brief ת [N/m] + void SetMaxMotorTorque(float torque); + + /// \~chinese + /// @brief ȡת [N/m] + float GetMaxMotorTorque() const; + +private: + b2RevoluteJoint* raw_joint_; +}; + +/// \~chinese +/// @brief ؽ +class KGE_API RopeJoint : public Joint +{ +public: + /// \~chinese + /// @brief ؽڲ + struct Param : public Joint::ParamBase + { + Point local_anchor_a; ///< ؽAϵӵ + Point local_anchor_b; ///< ؽBϵӵ + float max_length; ///< 󳤶 + + Param(Body* body_a, Body* body_b, Point const& local_anchor_a, Point const& local_anchor_b, + float max_length = 0.f) + : ParamBase(body_a, body_b) + , local_anchor_a(local_anchor_a) + , local_anchor_b(local_anchor_b) + , max_length(max_length) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, Point const& local_anchor_a, Point const& local_anchor_b, + float max_length = 0.f) + : Param(body_a.get(), body_b.get(), local_anchor_a, local_anchor_b, max_length) + { + } + }; + + RopeJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief ùؽ󳤶 + void SetMaxLength(float length); + + /// \~chinese + /// @brief ȡؽ󳤶 + float GetMaxLength() const; + +private: + b2RopeJoint* raw_joint_; +}; + +/// \~chinese +/// @brief ӹؽ +class KGE_API WeldJoint : public Joint +{ +public: + /// \~chinese + /// @brief ӹؽڲ + struct Param : public Joint::ParamBase + { + Point anchor; ///< λ + float frequency_hz; ///< ӦٶȣֵԽ߹ؽӦٶԽ죬ȥԽ + float damping_ratio; ///< ʣֵԽؽ˶Խ + + Param(Body* body_a, Body* body_b, Point const& anchor, float frequency_hz = 0.f, float damping_ratio = 0.f) + : ParamBase(body_a, body_b) + , anchor(anchor) + , frequency_hz(frequency_hz) + , damping_ratio(damping_ratio) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, Point const& anchor, float frequency_hz = 0.f, float damping_ratio = 0.f) + : Param(body_a.get(), body_b.get(), anchor, frequency_hz, damping_ratio) + { + } + }; + + WeldJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief ȡBAĽǶ + float GetReferenceAngle() const; + + /// \~chinese + /// @brief õӦٶ [] + void SetFrequency(float hz); + + /// \~chinese + /// @brief ȡӦٶ [] + float GetFrequency() const; + + /// \~chinese + /// @brief + void SetDampingRatio(float ratio); + + /// \~chinese + /// @brief ȡ + float GetDampingRatio() const; + +private: + b2WeldJoint* raw_joint_; +}; + +/// \~chinese +/// @brief ֹؽ +class KGE_API WheelJoint : public Joint +{ +public: + /// \~chinese + /// @brief ֹؽڲ + struct Param : public Joint::ParamBase + { + Point anchor; ///< ֹؽλ + Vec2 axis; ///< A + bool enable_motor; ///< Ƿ + float max_motor_torque; ///< [N] + float motor_speed; ///< ת [degree/s] + float frequency_hz; ///< ӦٶȣֵԽ߹ؽӦٶԽ죬ȥԽ + float damping_ratio; ///< ʣֵԽؽ˶Խ + + Param(Body* body_a, Body* body_b, Point const& anchor, Vec2 const& axis, float frequency_hz = 2.0f, + float damping_ratio = 0.7f, bool enable_motor = false, float max_motor_torque = 0.0f, + float motor_speed = 0.0f) + : ParamBase(body_a, body_b) + , anchor(anchor) + , axis(axis) + , enable_motor(enable_motor) + , max_motor_torque(max_motor_torque) + , motor_speed(motor_speed) + , frequency_hz(frequency_hz) + , damping_ratio(damping_ratio) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, Point const& anchor, Vec2 const& axis, float frequency_hz = 2.0f, + float damping_ratio = 0.7f, bool enable_motor = false, float max_motor_torque = 0.0f, + float motor_speed = 0.0f) + : Param(body_a.get(), body_b.get(), anchor, axis, frequency_hz, damping_ratio, enable_motor, + max_motor_torque, motor_speed) + { + } + }; + + WheelJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief ȡؽڵǰƽƾ + float GetJointTranslation() const; + + /// \~chinese + /// @brief ȡؽڵǰٶ + float GetJointLinearSpeed() const; + + /// \~chinese + /// @brief ȡؽڵǰĽǶ + float GetJointAngle() const; + + /// \~chinese + /// @brief ȡؽڵǰתٶ + float GetJointAngularSpeed() const; + + /// \~chinese + /// @brief Ƿ + bool IsMotorEnabled() const; + + /// \~chinese + /// @brief Ƿ + void EnableMotor(bool flag); + + /// \~chinese + /// @brief ת [degree/s] + void SetMotorSpeed(float speed); + + /// \~chinese + /// @brief ȡת [degree/s] + float GetMotorSpeed() const; + + /// \~chinese + /// @brief ת [N/m] + void SetMaxMotorTorque(float torque); + + /// \~chinese + /// @brief ȡת [N/m] + float GetMaxMotorTorque() const; + + /// \~chinese + /// @brief õӦٶ + void SetSpringFrequencyHz(float hz); + + /// \~chinese + /// @brief ȡӦٶ + float GetSpringFrequencyHz() const; + + /// \~chinese + /// @brief õ + void SetSpringDampingRatio(float ratio); + + /// \~chinese + /// @brief ȡ + float GetSpringDampingRatio() const; + +private: + b2WheelJoint* raw_joint_; +}; + +/// \~chinese +/// @brief ؽ +/// @details ʹij׷ϵָ㣬׷λ +class KGE_API MouseJoint : public Joint +{ +public: + /// \~chinese + /// @brief ؽڲ + struct Param : public Joint::ParamBase + { + Point target; ///< ؽĿλ + float max_force; ///< Aϵ + float frequency_hz; ///< ӦٶȣֵԽ߹ؽӦٶԽ죬ȥԽ + float damping_ratio; ///< ʣֵԽؽ˶Խ + + Param(Body* body_a, Body* body_b, Point const& target, float max_force, float frequency_hz = 5.0f, + float damping_ratio = 0.7f) + : ParamBase(body_a, body_b) + , target(target) + , max_force(max_force) + , frequency_hz(frequency_hz) + , damping_ratio(damping_ratio) + { + } + + Param(BodyPtr body_a, BodyPtr body_b, Point const& target, float max_force, float frequency_hz = 5.0f, + float damping_ratio = 0.7f) + : Param(body_a.get(), body_b.get(), target, max_force, frequency_hz, damping_ratio) + { + } + }; + + MouseJoint(); + + /// \~chinese + /// @brief ʼؽ + bool InitJoint(World* world, Param const& param); + + /// \~chinese + /// @brief 趨Ħ [N] + void SetMaxForce(float force); + + /// \~chinese + /// @brief ȡĦ [N] + float GetMaxForce() const; + + /// \~chinese + /// @brief Ӧٶ [hz] + void SetFrequency(float hz); + + /// \~chinese + /// @brief ȡӦٶ [hz] + float GetFrequency() const; + + /// \~chinese + /// @brief + void SetDampingRatio(float ratio); + + /// \~chinese + /// @brief ȡ + float GetDampingRatio() const; + +private: + b2MouseJoint* raw_joint_; +}; + +/** @} */ + +inline b2Joint* Joint::GetB2Joint() const +{ + return joint_; } +inline World* Joint::GetWorld() const +{ + return world_; +} + +inline void DistanceJoint::SetFrequency(float hz) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetFrequency(hz); +} +inline float DistanceJoint::GetFrequency() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetFrequency(); +} +inline void DistanceJoint::SetDampingRatio(float ratio) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetDampingRatio(ratio); +} +inline float DistanceJoint::GetDampingRatio() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetDampingRatio(); +} + +inline float PrismaticJoint::GetReferenceAngle() const +{ + KGE_ASSERT(raw_joint_); + return math::Radian2Degree(raw_joint_->GetReferenceAngle()); +} +inline bool PrismaticJoint::IsLimitEnabled() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->IsLimitEnabled(); +} +inline void PrismaticJoint::EnableLimit(bool flag) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->EnableLimit(flag); +} +inline bool PrismaticJoint::IsMotorEnabled() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->IsMotorEnabled(); +} +inline void PrismaticJoint::EnableMotor(bool flag) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->EnableMotor(flag); +} +inline void PrismaticJoint::SetMotorSpeed(float speed) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetMotorSpeed(math::Degree2Radian(speed)); +} +inline float PrismaticJoint::GetMotorSpeed() const +{ + KGE_ASSERT(raw_joint_); + return math::Radian2Degree(raw_joint_->GetMotorSpeed()); +} +inline void PrismaticJoint::SetMaxMotorForce(float force) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetMaxMotorForce(force); +} +inline float PrismaticJoint::GetMaxMotorForce() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetMaxMotorForce(); +} + +inline float RevoluteJoint::GetReferenceAngle() const +{ + KGE_ASSERT(raw_joint_); + return math::Radian2Degree(raw_joint_->GetReferenceAngle()); +} +inline bool RevoluteJoint::IsLimitEnabled() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->IsLimitEnabled(); +} +inline void RevoluteJoint::EnableLimit(bool flag) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->EnableLimit(flag); +} +inline bool RevoluteJoint::IsMotorEnabled() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->IsMotorEnabled(); +} +inline void RevoluteJoint::EnableMotor(bool flag) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->EnableMotor(flag); +} +inline void RevoluteJoint::SetMotorSpeed(float speed) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetMotorSpeed(math::Degree2Radian(speed)); +} +inline float RevoluteJoint::GetMotorSpeed() const +{ + KGE_ASSERT(raw_joint_); + return math::Radian2Degree(raw_joint_->GetMotorSpeed()); +} + +inline float WeldJoint::GetReferenceAngle() const +{ + KGE_ASSERT(raw_joint_); + return math::Radian2Degree(raw_joint_->GetReferenceAngle()); +} +inline void WeldJoint::SetFrequency(float hz) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetFrequency(hz); +} +inline float WeldJoint::GetFrequency() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetFrequency(); +} +inline void WeldJoint::SetDampingRatio(float ratio) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetDampingRatio(ratio); +} +inline float WeldJoint::GetDampingRatio() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetDampingRatio(); +} + +inline float WheelJoint::GetJointAngle() const +{ + KGE_ASSERT(raw_joint_); + return math::Radian2Degree(raw_joint_->GetJointAngle()); +} +inline float WheelJoint::GetJointAngularSpeed() const +{ + KGE_ASSERT(raw_joint_); + return math::Radian2Degree(raw_joint_->GetJointAngularSpeed()); +} +inline bool WheelJoint::IsMotorEnabled() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->IsMotorEnabled(); +} +inline void WheelJoint::EnableMotor(bool flag) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->EnableMotor(flag); +} +inline void WheelJoint::SetMotorSpeed(float speed) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetMotorSpeed(math::Degree2Radian(speed)); +} +inline float WheelJoint::GetMotorSpeed() const +{ + KGE_ASSERT(raw_joint_); + return math::Radian2Degree(raw_joint_->GetMotorSpeed()); +} +inline void WheelJoint::SetSpringFrequencyHz(float hz) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetSpringFrequencyHz(hz); +} +inline float WheelJoint::GetSpringFrequencyHz() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetSpringFrequencyHz(); +} +inline void WheelJoint::SetSpringDampingRatio(float ratio) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetSpringDampingRatio(ratio); +} +inline float WheelJoint::GetSpringDampingRatio() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetSpringDampingRatio(); +} + +inline void MouseJoint::SetFrequency(float hz) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetFrequency(hz); +} +inline float MouseJoint::GetFrequency() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetFrequency(); +} +inline void MouseJoint::SetDampingRatio(float ratio) +{ + KGE_ASSERT(raw_joint_); + raw_joint_->SetDampingRatio(ratio); +} +inline float MouseJoint::GetDampingRatio() const +{ + KGE_ASSERT(raw_joint_); + return raw_joint_->GetDampingRatio(); +} +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/Shape.cpp b/src/kiwano-physics/Shape.cpp index d39a49e1..709bf2c3 100644 --- a/src/kiwano-physics/Shape.cpp +++ b/src/kiwano-physics/Shape.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -23,200 +23,200 @@ namespace kiwano { - namespace physics - { - Shape::Shape() - : shape_(nullptr) - { - } - - Shape::Shape(b2Shape* shape) - : shape_(shape) - { - } - - b2Shape* Shape::GetB2Shape() const - { - return shape_; - } - - void Shape::SetB2Shape(b2Shape* shape) - { - shape_ = shape; - } - - // - // CircleShape - // - - CircleShape::CircleShape() - : Shape(&circle_) - , circle_() - , radius_(0.f) - { - } - - CircleShape::CircleShape(float radius, Point const& offset) - : CircleShape() - { - Set(radius, offset); - } - - void CircleShape::Set(float radius, Point const& offset) - { - radius_ = radius; - offset_ = offset; - } - - void CircleShape::FitWorld(World* world) - { - KGE_ASSERT(world); - circle_.m_radius = world->Stage2World(radius_); - circle_.m_p = world->Stage2World(offset_); - } - - // - // BoxShape - // - - BoxShape::BoxShape() - : Shape(&polygon_) - , polygon_() - , rotation_(0.f) - { - } - - BoxShape::BoxShape(Vec2 const& size, Point const& offset, float rotation) - : BoxShape() - { - Set(size, offset, rotation); - } - - void BoxShape::Set(Vec2 const& size, Point const& offset, float rotation) - { - box_size_ = size; - offset_ = offset; - rotation_ = rotation; - } - - void BoxShape::FitWorld(World* world) - { - KGE_ASSERT(world); - - b2Vec2 box = world->Stage2World(box_size_); - b2Vec2 offset = world->Stage2World(offset_); - polygon_.SetAsBox(box.x / 2, box.y / 2, offset, rotation_); - } - - // - // PolygonShape - // - - PolygonShape::PolygonShape() - : Shape(&polygon_) - , polygon_() - { - } - - PolygonShape::PolygonShape(Vector const& vertexs) - : PolygonShape() - { - Set(vertexs); - } - - void PolygonShape::Set(Vector const& vertexs) - { - vertexs_ = vertexs; - } - - void PolygonShape::FitWorld(World* world) - { - KGE_ASSERT(world); - - Vector b2vertexs; - b2vertexs.reserve(vertexs_.size()); - for (const auto& v : vertexs_) - { - b2vertexs.push_back(world->Stage2World(v)); - } - - polygon_.Set(&b2vertexs[0], static_cast(b2vertexs.size())); - } - - // - // EdgeShape - // - - EdgeShape::EdgeShape() - : Shape(&edge_) - , edge_() - { - } - - EdgeShape::EdgeShape(Point const& p1, Point const& p2) - : EdgeShape() - { - Set(p1, p2); - } - - void EdgeShape::Set(Point const& p1, Point const& p2) - { - p_[0] = p1; - p_[1] = p2; - } - - void EdgeShape::FitWorld(World* world) - { - KGE_ASSERT(world); - - b2Vec2 p1 = world->Stage2World(p_[0]); - b2Vec2 p2 = world->Stage2World(p_[1]); - edge_.Set(p1, p2); - } - - // - // ChainShape - // - - ChainShape::ChainShape() - : Shape(&chain_) - , chain_() - , loop_(false) - { - } - - ChainShape::ChainShape(Vector const& vertexs, bool loop) - : ChainShape() - { - Set(vertexs, loop); - } - - void ChainShape::Set(Vector const& vertexs, bool loop) - { - vertexs_ = vertexs; - loop_ = loop; - } - - void ChainShape::FitWorld(World* world) - { - KGE_ASSERT(world); - - Vector b2vertexs; - b2vertexs.reserve(vertexs_.size()); - for (const auto& v : vertexs_) - { - b2vertexs.push_back(world->Stage2World(v)); - } - - if (loop_) - { - chain_.CreateLoop(&b2vertexs[0], static_cast(b2vertexs.size())); - } - else - { - chain_.CreateChain(&b2vertexs[0], static_cast(b2vertexs.size())); - } - } - +namespace physics +{ +Shape::Shape() + : shape_(nullptr) +{ } + +Shape::Shape(b2Shape* shape) + : shape_(shape) +{ } + +b2Shape* Shape::GetB2Shape() const +{ + return shape_; +} + +void Shape::SetB2Shape(b2Shape* shape) +{ + shape_ = shape; +} + +// +// CircleShape +// + +CircleShape::CircleShape() + : Shape(&circle_) + , circle_() + , radius_(0.f) +{ +} + +CircleShape::CircleShape(float radius, Point const& offset) + : CircleShape() +{ + Set(radius, offset); +} + +void CircleShape::Set(float radius, Point const& offset) +{ + radius_ = radius; + offset_ = offset; +} + +void CircleShape::FitWorld(World* world) +{ + KGE_ASSERT(world); + circle_.m_radius = world->Stage2World(radius_); + circle_.m_p = world->Stage2World(offset_); +} + +// +// BoxShape +// + +BoxShape::BoxShape() + : Shape(&polygon_) + , polygon_() + , rotation_(0.f) +{ +} + +BoxShape::BoxShape(Vec2 const& size, Point const& offset, float rotation) + : BoxShape() +{ + Set(size, offset, rotation); +} + +void BoxShape::Set(Vec2 const& size, Point const& offset, float rotation) +{ + box_size_ = size; + offset_ = offset; + rotation_ = rotation; +} + +void BoxShape::FitWorld(World* world) +{ + KGE_ASSERT(world); + + b2Vec2 box = world->Stage2World(box_size_); + b2Vec2 offset = world->Stage2World(offset_); + polygon_.SetAsBox(box.x / 2, box.y / 2, offset, rotation_); +} + +// +// PolygonShape +// + +PolygonShape::PolygonShape() + : Shape(&polygon_) + , polygon_() +{ +} + +PolygonShape::PolygonShape(Vector const& vertexs) + : PolygonShape() +{ + Set(vertexs); +} + +void PolygonShape::Set(Vector const& vertexs) +{ + vertexs_ = vertexs; +} + +void PolygonShape::FitWorld(World* world) +{ + KGE_ASSERT(world); + + Vector b2vertexs; + b2vertexs.reserve(vertexs_.size()); + for (const auto& v : vertexs_) + { + b2vertexs.push_back(world->Stage2World(v)); + } + + polygon_.Set(&b2vertexs[0], static_cast(b2vertexs.size())); +} + +// +// EdgeShape +// + +EdgeShape::EdgeShape() + : Shape(&edge_) + , edge_() +{ +} + +EdgeShape::EdgeShape(Point const& p1, Point const& p2) + : EdgeShape() +{ + Set(p1, p2); +} + +void EdgeShape::Set(Point const& p1, Point const& p2) +{ + p_[0] = p1; + p_[1] = p2; +} + +void EdgeShape::FitWorld(World* world) +{ + KGE_ASSERT(world); + + b2Vec2 p1 = world->Stage2World(p_[0]); + b2Vec2 p2 = world->Stage2World(p_[1]); + edge_.Set(p1, p2); +} + +// +// ChainShape +// + +ChainShape::ChainShape() + : Shape(&chain_) + , chain_() + , loop_(false) +{ +} + +ChainShape::ChainShape(Vector const& vertexs, bool loop) + : ChainShape() +{ + Set(vertexs, loop); +} + +void ChainShape::Set(Vector const& vertexs, bool loop) +{ + vertexs_ = vertexs; + loop_ = loop; +} + +void ChainShape::FitWorld(World* world) +{ + KGE_ASSERT(world); + + Vector b2vertexs; + b2vertexs.reserve(vertexs_.size()); + for (const auto& v : vertexs_) + { + b2vertexs.push_back(world->Stage2World(v)); + } + + if (loop_) + { + chain_.CreateLoop(&b2vertexs[0], static_cast(b2vertexs.size())); + } + else + { + chain_.CreateChain(&b2vertexs[0], static_cast(b2vertexs.size())); + } +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/Shape.h b/src/kiwano-physics/Shape.h index 8ee206f1..b6f7e9fa 100644 --- a/src/kiwano-physics/Shape.h +++ b/src/kiwano-physics/Shape.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -23,140 +23,135 @@ namespace kiwano { - namespace physics - { - class World; - class Fixture; +namespace physics +{ +class World; +class Fixture; - /** - * \addtogroup Physics - * @{ - */ +/** + * \addtogroup Physics + * @{ + */ - /// \~chinese - /// @brief ״ - class KGE_API Shape - { - friend class Fixture; +/// \~chinese +/// @brief ״ +class KGE_API Shape +{ + friend class Fixture; - public: - Shape(); - Shape(b2Shape* shape); +public: + Shape(); + Shape(b2Shape* shape); - b2Shape* GetB2Shape() const; - void SetB2Shape(b2Shape* shape); + b2Shape* GetB2Shape() const; + void SetB2Shape(b2Shape* shape); - private: - virtual void FitWorld(World* world) {} +private: + virtual void FitWorld(World* world) {} - private: - b2Shape* shape_; - }; +private: + b2Shape* shape_; +}; - /// \~chinese - /// @brief Բ״ - class KGE_API CircleShape - : public Shape - { - public: - CircleShape(); +/// \~chinese +/// @brief Բ״ +class KGE_API CircleShape : public Shape +{ +public: + CircleShape(); - CircleShape(float radius, Point const& offset = Point()); + CircleShape(float radius, Point const& offset = Point()); - void Set(float radius, Point const& offset = Point()); + void Set(float radius, Point const& offset = Point()); - private: - void FitWorld(World* world) override; +private: + void FitWorld(World* world) override; - private: - float radius_; - Point offset_; - b2CircleShape circle_; - }; +private: + float radius_; + Point offset_; + b2CircleShape circle_; +}; - /// \~chinese - /// @brief ״ - class KGE_API BoxShape - : public Shape - { - public: - BoxShape(); +/// \~chinese +/// @brief ״ +class KGE_API BoxShape : public Shape +{ +public: + BoxShape(); - BoxShape(Vec2 const& size, Point const& offset = Point(), float rotation = 0.f); + BoxShape(Vec2 const& size, Point const& offset = Point(), float rotation = 0.f); - void Set(Vec2 const& size, Point const& offset = Point(), float rotation = 0.f); + void Set(Vec2 const& size, Point const& offset = Point(), float rotation = 0.f); - private: - void FitWorld(World* world) override; +private: + void FitWorld(World* world) override; - private: - float rotation_; - Vec2 box_size_; - Point offset_; - b2PolygonShape polygon_; - }; +private: + float rotation_; + Vec2 box_size_; + Point offset_; + b2PolygonShape polygon_; +}; - /// \~chinese - /// @brief ״ - class KGE_API PolygonShape - : public Shape - { - public: - PolygonShape(); +/// \~chinese +/// @brief ״ +class KGE_API PolygonShape : public Shape +{ +public: + PolygonShape(); - PolygonShape(Vector const& vertexs); + PolygonShape(Vector const& vertexs); - void Set(Vector const& vertexs); + void Set(Vector const& vertexs); - private: - void FitWorld(World* world) override; +private: + void FitWorld(World* world) override; - private: - Vector vertexs_; - b2PolygonShape polygon_; - }; +private: + Vector vertexs_; + b2PolygonShape polygon_; +}; - /// \~chinese - /// @brief ߶״, ڱʾһ - class KGE_API EdgeShape - : public Shape - { - public: - EdgeShape(); +/// \~chinese +/// @brief ߶״, ڱʾһ +class KGE_API EdgeShape : public Shape +{ +public: + EdgeShape(); - EdgeShape(Point const& p1, Point const& p2); + EdgeShape(Point const& p1, Point const& p2); - void Set(Point const& p1, Point const& p2); + void Set(Point const& p1, Point const& p2); - private: - void FitWorld(World* world) override; +private: + void FitWorld(World* world) override; - private: - Point p_[2]; - b2EdgeShape edge_; - }; +private: + Point p_[2]; + b2EdgeShape edge_; +}; - /// \~chinese - /// @brief ʽ״ - class KGE_API ChainShape - : public Shape - { - public: - ChainShape(); +/// \~chinese +/// @brief ʽ״ +class KGE_API ChainShape : public Shape +{ +public: + ChainShape(); - ChainShape(Vector const& vertexs, bool loop = false); + ChainShape(Vector const& vertexs, bool loop = false); - void Set(Vector const& vertexs, bool loop = false); + void Set(Vector const& vertexs, bool loop = false); - private: - void FitWorld(World* world) override; +private: + void FitWorld(World* world) override; - private: - bool loop_; - Vector vertexs_; - b2ChainShape chain_; - }; +private: + bool loop_; + Vector vertexs_; + b2ChainShape chain_; +}; - /** @} */ - } -} +/** @} */ +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/World.cpp b/src/kiwano-physics/World.cpp index 8c47eee4..31b981da 100644 --- a/src/kiwano-physics/World.cpp +++ b/src/kiwano-physics/World.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -19,235 +19,237 @@ // THE SOFTWARE. #include "World.h" + #include namespace kiwano { - namespace physics - { - namespace - { - const float default_global_scale = 100.f; // 100 pixels per meters - } - - class World::DestructionListener : public b2DestructionListener - { - World* world_; - - public: - DestructionListener(World* world) - : world_(world) - { - } - - void SayGoodbye(b2Joint* joint) override - { - if (world_) - { - world_->JointRemoved(joint); - } - } - - void SayGoodbye(b2Fixture* fixture) override - { - - } - }; - - class World::ContactListener - : public b2ContactListener - { - World* world_; - - public: - ContactListener(World* world) - : world_(world) - { - } - - void BeginContact(b2Contact* contact) override - { - ContactBeginEvent evt(contact); - world_->Dispatch(evt); - } - - void EndContact(b2Contact* contact) override - { - ContactEndEvent evt(contact); - world_->Dispatch(evt); - } - - void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override { KGE_NOT_USED(contact); KGE_NOT_USED(oldManifold); } - void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override { KGE_NOT_USED(contact); KGE_NOT_USED(impulse); } - }; - - World::World() - : world_(b2Vec2(0, 10.0f)) - , vel_iter_(6) - , pos_iter_(2) - , global_scale_(default_global_scale) - , destruction_listener_(nullptr) - , contact_listener_(nullptr) - , removing_joint_(false) - { - destruction_listener_ = new DestructionListener(this); - world_.SetDestructionListener(destruction_listener_); - - contact_listener_ = new ContactListener(this); - world_.SetContactListener(contact_listener_); - } - - World::~World() - { - world_.SetDestructionListener(nullptr); - if (destruction_listener_) - { - delete destruction_listener_; - destruction_listener_ = nullptr; - } - - world_.SetContactListener(nullptr); - if (contact_listener_) - { - delete contact_listener_; - contact_listener_ = nullptr; - } - - // Make sure b2World was destroyed after b2Body - RemoveAllChildren(); - RemoveAllBodies(); - RemoveAllJoints(); - } - - void World::RemoveBody(Body* body) - { - if (body && body->GetB2Body()) - { - world_.DestroyBody(body->GetB2Body()); - } - } - - void World::RemoveAllBodies() - { - if (world_.GetBodyCount()) - { - b2Body* body = world_.GetBodyList(); - while (body) - { - b2Body* next = body->GetNext(); - world_.DestroyBody(body); - body = next; - } - } - } - - void World::AddJoint(Joint* joint) - { - if (joint) - { - joints_.push_back(joint); - } - } - - void World::RemoveJoint(Joint* joint) - { - if (joint) - { - auto iter = std::find(joints_.begin(), joints_.end(), joint); - if (iter != joints_.end()) - { - joints_.erase(iter); - - if (joint->GetB2Joint()) - { - removing_joint_ = true; - world_.DestroyJoint(joint->GetB2Joint()); - removing_joint_ = false; - } - } - } - } - - void World::RemoveAllJoints() - { - if (world_.GetJointCount()) - { - removing_joint_ = true; - { - b2Joint* joint = world_.GetJointList(); - while (joint) - { - b2Joint* next = joint->GetNext(); - world_.DestroyJoint(joint); - joint = next; - } - } - removing_joint_ = false; - } - joints_.clear(); - } - - void World::JointRemoved(b2Joint* joint) - { - if (!removing_joint_ && joint) - { - auto iter = std::find_if( - joints_.begin(), - joints_.end(), - [joint](Joint* j) -> bool { return j->GetB2Joint() == joint; } - ); - - if (iter != joints_.end()) - { - joints_.erase(iter); - } - } - } - - b2World* World::GetB2World() - { - return &world_; - } - - const b2World* World::GetB2World() const - { - return &world_; - } - - Vec2 World::GetGravity() const - { - b2Vec2 g = world_.GetGravity(); - return Vec2(g.x, g.y); - } - - void World::SetGravity(Vec2 gravity) - { - world_.SetGravity(b2Vec2(gravity.x, gravity.y)); - } - - ContactList World::GetContactList() - { - return ContactList(Contact(world_.GetContactList())); - } - - void World::Update(Duration dt) - { - world_.Step(dt.Seconds(), vel_iter_, pos_iter_); - - b2Body* b2body = world_.GetBodyList(); - while (b2body) - { - Body* body = static_cast(b2body->GetUserData()); - if (body && body->GetType() != Body::Type::Static) - { - body->UpdateActor(); - } - - b2body = b2body->GetNext(); - } - - Stage::Update(dt); - } - - } +namespace physics +{ +namespace +{ +const float default_global_scale = 100.f; // 100 pixels per meters } + +class World::DestructionListener : public b2DestructionListener +{ + World* world_; + +public: + DestructionListener(World* world) + : world_(world) + { + } + + void SayGoodbye(b2Joint* joint) override + { + if (world_) + { + world_->JointRemoved(joint); + } + } + + void SayGoodbye(b2Fixture* fixture) override {} +}; + +class World::ContactListener : public b2ContactListener +{ + World* world_; + +public: + ContactListener(World* world) + : world_(world) + { + } + + void BeginContact(b2Contact* contact) override + { + ContactBeginEventPtr evt = new ContactBeginEvent(contact); + world_->DispatchEvent(evt.get()); + } + + void EndContact(b2Contact* contact) override + { + ContactEndEventPtr evt = new ContactEndEvent(contact); + world_->DispatchEvent(evt.get()); + } + + void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override + { + KGE_NOT_USED(contact); + KGE_NOT_USED(oldManifold); + } + void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override + { + KGE_NOT_USED(contact); + KGE_NOT_USED(impulse); + } +}; + +World::World() + : world_(b2Vec2(0, 10.0f)) + , vel_iter_(6) + , pos_iter_(2) + , global_scale_(default_global_scale) + , destruction_listener_(nullptr) + , contact_listener_(nullptr) + , removing_joint_(false) +{ + destruction_listener_ = new DestructionListener(this); + world_.SetDestructionListener(destruction_listener_); + + contact_listener_ = new ContactListener(this); + world_.SetContactListener(contact_listener_); +} + +World::~World() +{ + world_.SetDestructionListener(nullptr); + if (destruction_listener_) + { + delete destruction_listener_; + destruction_listener_ = nullptr; + } + + world_.SetContactListener(nullptr); + if (contact_listener_) + { + delete contact_listener_; + contact_listener_ = nullptr; + } + + // Make sure b2World was destroyed after b2Body + RemoveAllChildren(); + RemoveAllBodies(); + RemoveAllJoints(); +} + +void World::RemoveBody(Body* body) +{ + if (body && body->GetB2Body()) + { + world_.DestroyBody(body->GetB2Body()); + } +} + +void World::RemoveAllBodies() +{ + if (world_.GetBodyCount()) + { + b2Body* body = world_.GetBodyList(); + while (body) + { + b2Body* next = body->GetNext(); + world_.DestroyBody(body); + body = next; + } + } +} + +void World::AddJoint(Joint* joint) +{ + if (joint) + { + joints_.push_back(joint); + } +} + +void World::RemoveJoint(Joint* joint) +{ + if (joint) + { + auto iter = std::find(joints_.begin(), joints_.end(), joint); + if (iter != joints_.end()) + { + joints_.erase(iter); + + if (joint->GetB2Joint()) + { + removing_joint_ = true; + world_.DestroyJoint(joint->GetB2Joint()); + removing_joint_ = false; + } + } + } +} + +void World::RemoveAllJoints() +{ + if (world_.GetJointCount()) + { + removing_joint_ = true; + { + b2Joint* joint = world_.GetJointList(); + while (joint) + { + b2Joint* next = joint->GetNext(); + world_.DestroyJoint(joint); + joint = next; + } + } + removing_joint_ = false; + } + joints_.clear(); +} + +void World::JointRemoved(b2Joint* joint) +{ + if (!removing_joint_ && joint) + { + auto iter = std::find_if(joints_.begin(), joints_.end(), + [joint](Joint* j) -> bool { return j->GetB2Joint() == joint; }); + + if (iter != joints_.end()) + { + joints_.erase(iter); + } + } +} + +b2World* World::GetB2World() +{ + return &world_; +} + +const b2World* World::GetB2World() const +{ + return &world_; +} + +Vec2 World::GetGravity() const +{ + b2Vec2 g = world_.GetGravity(); + return Vec2(g.x, g.y); +} + +void World::SetGravity(Vec2 gravity) +{ + world_.SetGravity(b2Vec2(gravity.x, gravity.y)); +} + +ContactList World::GetContactList() +{ + return ContactList(Contact(world_.GetContactList())); +} + +void World::Update(Duration dt) +{ + world_.Step(dt.Seconds(), vel_iter_, pos_iter_); + + b2Body* b2body = world_.GetBodyList(); + while (b2body) + { + Body* body = static_cast(b2body->GetUserData()); + if (body && body->GetType() != Body::Type::Static) + { + body->UpdateActor(); + } + + b2body = b2body->GetNext(); + } + + Stage::Update(dt); +} + +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/World.h b/src/kiwano-physics/World.h index ef5914c2..335f5069 100644 --- a/src/kiwano-physics/World.h +++ b/src/kiwano-physics/World.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -24,176 +24,174 @@ namespace kiwano { - namespace physics - { - KGE_DECLARE_SMART_PTR(World); +namespace physics +{ +KGE_DECLARE_SMART_PTR(World); - /** - * \~chinese - * \defgroup Physics - */ +/** + * \~chinese + * \defgroup Physics + */ - /** - * \addtogroup Physics - * @{ - */ +/** + * \addtogroup Physics + * @{ + */ - /** - * \~chinese - * @brief - */ - class KGE_API World - : public Stage - { - friend class Body; - friend class Joint; +/** + * \~chinese + * @brief + */ +class KGE_API World : public Stage +{ + friend class Body; + friend class Joint; - public: - World(); +public: + World(); - virtual ~World(); + virtual ~World(); - /// \~chinese - /// @brief ȡ [N] - Vec2 GetGravity() const; + /// \~chinese + /// @brief ȡ [N] + Vec2 GetGravity() const; - /// \~chinese - /// @brief [N] - void SetGravity(Vec2 gravity); + /// \~chinese + /// @brief [N] + void SetGravity(Vec2 gravity); - /// \~chinese - /// @brief ȡӴб - ContactList GetContactList(); + /// \~chinese + /// @brief ȡӴб + ContactList GetContactList(); - /// \~chinese - /// @brief ȡȫű - /// @details űָĵλתĻصıĬϱΪ1:100 - float GetGlobalScale() const; + /// \~chinese + /// @brief ȡȫű + /// @details űָĵλתĻصıĬϱΪ1:100 + float GetGlobalScale() const; - /// \~chinese - /// @brief ȫű - /// @details űָĵλתĻصıĬϱΪ1:100 - void SetGlobalScale(float scale); + /// \~chinese + /// @brief ȫű + /// @details űָĵλתĻصıĬϱΪ1:100 + void SetGlobalScale(float scale); - /// \~chinese - /// @brief Ϸ絥λתΪ絥λ - /// @details ȫűĵλתΪصλ - float World2Stage(float value) const; + /// \~chinese + /// @brief Ϸ絥λתΪ絥λ + /// @details ȫűĵλתΪصλ + float World2Stage(float value) const; - /// \~chinese - /// @brief Ϸ絥λתΪ絥λ - /// @details ȫűĵλתΪصλ - Vec2 World2Stage(const b2Vec2& pos) const; + /// \~chinese + /// @brief Ϸ絥λתΪ絥λ + /// @details ȫűĵλתΪصλ + Vec2 World2Stage(const b2Vec2& pos) const; - /// \~chinese - /// @brief 絥λתΪϷ絥λ - /// @details ȫűصλתΪĵλ - float Stage2World(float value) const; + /// \~chinese + /// @brief 絥λתΪϷ絥λ + /// @details ȫűصλתΪĵλ + float Stage2World(float value) const; - /// \~chinese - /// @brief 絥λתΪϷ絥λ - /// @details ȫűصλתΪĵλ - b2Vec2 Stage2World(const Vec2& pos) const; + /// \~chinese + /// @brief 絥λתΪϷ絥λ + /// @details ȫűصλתΪĵλ + b2Vec2 Stage2World(const Vec2& pos) const; - /// \~chinese - /// @brief ٶȵ, ĬΪ 6 - void SetVelocityIterations(int vel_iter); + /// \~chinese + /// @brief ٶȵ, ĬΪ 6 + void SetVelocityIterations(int vel_iter); - /// \~chinese - /// @brief λõ, ĬΪ 2 - void SetPositionIterations(int pos_iter); + /// \~chinese + /// @brief λõ, ĬΪ 2 + void SetPositionIterations(int pos_iter); - b2World* GetB2World(); + b2World* GetB2World(); - const b2World* GetB2World() const; + const b2World* GetB2World() const; - private: - /// \~chinese - /// @brief Ƴ - void RemoveBody(Body* body); +private: + /// \~chinese + /// @brief Ƴ + void RemoveBody(Body* body); - /// \~chinese - /// @brief Ƴ - void RemoveAllBodies(); + /// \~chinese + /// @brief Ƴ + void RemoveAllBodies(); - /// \~chinese - /// @brief ӹؽ - void AddJoint(Joint* joint); + /// \~chinese + /// @brief ӹؽ + void AddJoint(Joint* joint); - /// \~chinese - /// @brief Ƴؽ - void RemoveJoint(Joint* joint); + /// \~chinese + /// @brief Ƴؽ + void RemoveJoint(Joint* joint); - /// \~chinese - /// @brief Ƴйؽ - void RemoveAllJoints(); + /// \~chinese + /// @brief Ƴйؽ + void RemoveAllJoints(); - /// \~chinese - /// @brief ؽڱƳ - void JointRemoved(b2Joint* joint); + /// \~chinese + /// @brief ؽڱƳ + void JointRemoved(b2Joint* joint); - protected: - void Update(Duration dt) override; +protected: + void Update(Duration dt) override; - private: - b2World world_; - int vel_iter_; - int pos_iter_; - float global_scale_; +private: + b2World world_; + int vel_iter_; + int pos_iter_; + float global_scale_; - class DestructionListener; - friend DestructionListener; - DestructionListener* destruction_listener_; + class DestructionListener; + friend DestructionListener; + DestructionListener* destruction_listener_; - class ContactListener; - friend ContactListener; - ContactListener* contact_listener_; + class ContactListener; + friend ContactListener; + ContactListener* contact_listener_; - bool removing_joint_; - Vector joints_; - }; + bool removing_joint_; + Vector joints_; +}; +/** @} */ - /** @} */ - - inline float World::GetGlobalScale() const - { - return global_scale_; - } - - inline void World::SetGlobalScale(float scale) - { - global_scale_ = scale; - } - - inline float World::World2Stage(float value) const - { - return value * GetGlobalScale(); - } - - inline Vec2 World::World2Stage(const b2Vec2& pos) const - { - return Point(World2Stage(pos.x), World2Stage(pos.y)); - } - - inline float World::Stage2World(float value) const - { - return value / GetGlobalScale(); - } - - inline b2Vec2 World::Stage2World(const Vec2& pos) const - { - return b2Vec2(Stage2World(pos.x), Stage2World(pos.y)); - } - - inline void World::SetVelocityIterations(int vel_iter) - { - vel_iter_ = vel_iter; - } - - inline void World::SetPositionIterations(int pos_iter) - { - pos_iter_ = pos_iter; - } - } +inline float World::GetGlobalScale() const +{ + return global_scale_; } + +inline void World::SetGlobalScale(float scale) +{ + global_scale_ = scale; +} + +inline float World::World2Stage(float value) const +{ + return value * GetGlobalScale(); +} + +inline Vec2 World::World2Stage(const b2Vec2& pos) const +{ + return Point(World2Stage(pos.x), World2Stage(pos.y)); +} + +inline float World::Stage2World(float value) const +{ + return value / GetGlobalScale(); +} + +inline b2Vec2 World::Stage2World(const Vec2& pos) const +{ + return b2Vec2(Stage2World(pos.x), Stage2World(pos.y)); +} + +inline void World::SetVelocityIterations(int vel_iter) +{ + vel_iter_ = vel_iter; +} + +inline void World::SetPositionIterations(int pos_iter) +{ + pos_iter_ = pos_iter; +} +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/helper.h b/src/kiwano-physics/helper.h index 3436b0a1..f3be4fbf 100644 --- a/src/kiwano-physics/helper.h +++ b/src/kiwano-physics/helper.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -26,9 +26,15 @@ namespace kiwano { - namespace physics - { - inline b2Vec2 Stage2World(const Vec2& pos) { return b2Vec2(pos.x, pos.y); } - inline Vec2 World2Stage(const b2Vec2& pos) { return Vec2(pos.x, pos.y); } - } +namespace physics +{ +inline b2Vec2 Stage2World(const Vec2& pos) +{ + return b2Vec2(pos.x, pos.y); } +inline Vec2 World2Stage(const b2Vec2& pos) +{ + return Vec2(pos.x, pos.y); +} +} // namespace physics +} // namespace kiwano diff --git a/src/kiwano-physics/kiwano-physics.h b/src/kiwano-physics/kiwano-physics.h index fcc446d2..b10153d3 100644 --- a/src/kiwano-physics/kiwano-physics.h +++ b/src/kiwano-physics/kiwano-physics.h @@ -1,15 +1,15 @@ // Copyright (c) 2018-2019 Kiwano - Nomango -// +// // 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 @@ -20,10 +20,10 @@ #pragma once -#include -#include +#include #include #include -#include +#include #include +#include #include diff --git a/src/kiwano/2d/Actor.cpp b/src/kiwano/2d/Actor.cpp index 44411653..73793a26 100644 --- a/src/kiwano/2d/Actor.cpp +++ b/src/kiwano/2d/Actor.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -21,628 +21,637 @@ #include #include #include -#include +#include namespace kiwano { - namespace - { - float default_anchor_x = 0.f; - float default_anchor_y = 0.f; - } - - void Actor::SetDefaultAnchor(float anchor_x, float anchor_y) - { - default_anchor_x = anchor_x; - default_anchor_y = anchor_y; - } - - Actor::Actor() - : visible_(true) - , visible_in_rt_(true) - , update_pausing_(false) - , hover_(false) - , pressed_(false) - , responsible_(false) - , dirty_visibility_(true) - , dirty_transform_(false) - , dirty_transform_inverse_(false) - , cascade_opacity_(false) - , show_border_(false) - , is_fast_transform_(true) - , parent_(nullptr) - , stage_(nullptr) - , hash_name_(0) - , z_order_(0) - , opacity_(1.f) - , displayed_opacity_(1.f) - , anchor_(default_anchor_x, default_anchor_y) - { - } - - void Actor::Update(Duration dt) - { - UpdateActions(this, dt); - UpdateTimers(dt); - - if (!update_pausing_) - { - if (cb_update_) - cb_update_(dt); - - OnUpdate(dt); - } - - if (!children_.empty()) - { - ActorPtr next; - for (auto child = children_.first_item(); child; child = next) - { - next = child->next_item(); - child->Update(dt); - } - } - } - - void Actor::Render(RenderTarget* rt) - { - if (!visible_) - return; - - UpdateTransform(); - - if (children_.empty()) - { - if (CheckVisibilty(rt)) - { - PrepareToRender(rt); - OnRender(rt); - } - } - else - { - // render children those are less than 0 in Z-Order - Actor* child = children_.first_item().get(); - while (child) - { - if (child->GetZOrder() >= 0) - break; - - child->Render(rt); - child = child->next_item().get(); - } - - if (CheckVisibilty(rt)) - { - PrepareToRender(rt); - OnRender(rt); - } - - while (child) - { - child->Render(rt); - child = child->next_item().get(); - } - } - } - - void Actor::PrepareToRender(RenderTarget* rt) - { - rt->SetTransform(transform_matrix_); - rt->SetBrushOpacity(GetDisplayedOpacity()); - } - - void Actor::RenderBorder(RenderTarget* rt) - { - if (show_border_ && !size_.IsOrigin()) - { - Rect bounds = GetBounds(); - - rt->SetTransform(transform_matrix_); - - rt->SetCurrentBrush(GetStage()->GetBorderFillBrush()); - rt->FillRectangle(bounds); - - rt->SetCurrentBrush(GetStage()->GetBorderStrokeBrush()); - rt->DrawRectangle(bounds, 2.f); - } - - for (auto child = children_.first_item(); child; child = child->next_item()) - { - child->RenderBorder(rt); - } - } - - bool Actor::CheckVisibilty(RenderTarget* rt) const - { - if (dirty_visibility_) - { - dirty_visibility_ = false; - - if (size_.IsOrigin()) - { - visible_in_rt_ = false; - } - else - { - visible_in_rt_ = rt->CheckVisibility(GetBounds(), GetTransformMatrix()); - } - } - return visible_in_rt_; - } - - void Actor::Dispatch(Event& evt) - { - if (!visible_) - return; - - ActorPtr prev; - for (auto child = children_.last_item(); child; child = prev) - { - prev = child->prev_item(); - child->Dispatch(evt); - } - - if (responsible_) - { - if (evt.IsType()) - { - auto& mouse_evt = evt.SafeCast(); - if (!mouse_evt.target && ContainsPoint(mouse_evt.pos)) - { - mouse_evt.target = this; - - if (!hover_) - { - hover_ = true; - - MouseHoverEvent hover; - hover.pos = mouse_evt.pos; - hover.left_btn_down = mouse_evt.left_btn_down; - hover.right_btn_down = mouse_evt.right_btn_down; - hover.target = this; - EventDispatcher::Dispatch(hover); - } - } - else if (hover_) - { - hover_ = false; - pressed_ = false; - - MouseOutEvent out; - out.pos = mouse_evt.pos; - out.left_btn_down = mouse_evt.left_btn_down; - out.right_btn_down = mouse_evt.right_btn_down; - out.target = this; - EventDispatcher::Dispatch(out); - } - } - - if (evt.IsType() && hover_) - { - pressed_ = true; - evt.SafeCast().target = this; - } - - if (evt.IsType() && pressed_) - { - pressed_ = false; - - auto mouse_up_evt = evt.SafeCast(); - mouse_up_evt.target = this; - - MouseClickEvent click; - click.pos = mouse_up_evt.pos; - click.left_btn_down = mouse_up_evt.left_btn_down; - click.right_btn_down = mouse_up_evt.right_btn_down; - click.target = this; - click.button = mouse_up_evt.button; - EventDispatcher::Dispatch(click); - } - } - - EventDispatcher::Dispatch(evt); - } - - Matrix3x2 const & Actor::GetTransformMatrix() const - { - UpdateTransform(); - return transform_matrix_; - } - - Matrix3x2 const & Actor::GetTransformInverseMatrix() const - { - UpdateTransform(); - if (dirty_transform_inverse_) - { - transform_matrix_inverse_ = transform_matrix_.Invert(); - dirty_transform_inverse_ = false; - } - return transform_matrix_inverse_; - } - - void Actor::UpdateTransform() const - { - if (!dirty_transform_) - return; - - dirty_transform_ = false; - dirty_transform_inverse_ = true; - dirty_visibility_ = true; - - if (is_fast_transform_) - { - transform_matrix_ = Matrix3x2::Translation(transform_.position); - } - else - { - // matrix multiplication is optimized by expression template - transform_matrix_ = transform_.ToMatrix(); - } - - transform_matrix_.Translate(Point{ -size_.x * anchor_.x, -size_.y * anchor_.y }); - - if (parent_) - { - transform_matrix_ *= parent_->transform_matrix_; - } - - // update children's transform - for (auto child = children_.first_item().get(); child; child = child->next_item().get()) - child->dirty_transform_ = true; - } - - void Actor::UpdateOpacity() - { - if (parent_ && parent_->IsCascadeOpacityEnabled()) - { - displayed_opacity_ = opacity_ * parent_->displayed_opacity_; - } - else - { - displayed_opacity_ = opacity_; - } - - for (Actor* child = children_.first_item().get(); child; child = child->next_item().get()) - { - child->UpdateOpacity(); - } - } - - void Actor::SetStage(Stage* stage) - { - if (stage_ != stage) - { - stage_ = stage; - for (Actor* child = children_.first_item().get(); child; child = child->next_item().get()) - { - child->stage_ = stage; - } - } - } - - void Actor::Reorder() - { - if (parent_) - { - ActorPtr me = this; - - parent_->children_.remove(me); - - Actor* sibling = parent_->children_.last_item().get(); - - if (sibling && sibling->GetZOrder() > z_order_) - { - sibling = sibling->prev_item().get(); - while (sibling) - { - if (sibling->GetZOrder() <= z_order_) - break; - sibling = sibling->prev_item().get(); - } - } - - if (sibling) - { - parent_->children_.insert_after(me, sibling); - } - else - { - parent_->children_.push_front(me); - } - } - } - - void Actor::SetZOrder(int zorder) - { - if (z_order_ != zorder) - { - z_order_ = zorder; - Reorder(); - } - } - - void Actor::SetOpacity(float opacity) - { - if (opacity_ == opacity) - return; - - displayed_opacity_ = opacity_ = std::min(std::max(opacity, 0.f), 1.f); - UpdateOpacity(); - } - - void Actor::SetCascadeOpacityEnabled(bool enabled) - { - if (cascade_opacity_ == enabled) - return; - - cascade_opacity_ = enabled; - UpdateOpacity(); - } - - void Actor::SetAnchor(Vec2 const& anchor) - { - if (anchor_ == anchor) - return; - - anchor_ = anchor; - dirty_transform_ = true; - } - - void Actor::SetWidth(float width) - { - SetSize(Size{ width, size_.y }); - } - - void Actor::SetHeight(float height) - { - SetSize(Size{ size_.x, height }); - } - - void Actor::SetSize(Size const& size) - { - if (size_ == size) - return; - - size_ = size; - dirty_transform_ = true; - } - - void Actor::SetTransform(Transform const& transform) - { - transform_ = transform; - dirty_transform_ = true; - is_fast_transform_ = false; - } - - void Actor::SetVisible(bool val) - { - visible_ = val; - } - - void Actor::SetName(String const& name) - { - if (!IsName(name)) - { - ObjectBase::SetName(name); - hash_name_ = std::hash{}(name); - } - } - - void Actor::SetPosition(const Point & pos) - { - if (transform_.position == pos) - return; - - transform_.position = pos; - dirty_transform_ = true; - } - - void Actor::SetPositionX(float x) - { - SetPosition(Point{ x, transform_.position.y }); - } - - void Actor::SetPositionY(float y) - { - SetPosition(Point{ transform_.position.x, y }); - } - - void Actor::Move(Vec2 const& v) - { - this->SetPosition(Point{ transform_.position.x + v.x, transform_.position.y + v.y }); - } - - void Actor::SetScale(Vec2 const& scale) - { - if (transform_.scale == scale) - return; - - transform_.scale = scale; - dirty_transform_ = true; - is_fast_transform_ = false; - } - - void Actor::SetSkew(Vec2 const& skew) - { - if (transform_.skew == skew) - return; - - transform_.skew = skew; - dirty_transform_ = true; - is_fast_transform_ = false; - } - - void Actor::SetRotation(float angle) - { - if (transform_.rotation == angle) - return; - - transform_.rotation = angle; - dirty_transform_ = true; - is_fast_transform_ = false; - } - - void Actor::AddChild(Actor* child, int zorder) - { - KGE_ASSERT(child && "Actor::AddChild failed, NULL pointer exception"); - - if (child) - { - KGE_ASSERT(!child->parent_ && "Actor::AddChild failed, the actor to be added already has a parent"); +namespace +{ +float default_anchor_x = 0.f; +float default_anchor_y = 0.f; +} // namespace + +void Actor::SetDefaultAnchor(float anchor_x, float anchor_y) +{ + default_anchor_x = anchor_x; + default_anchor_y = anchor_y; +} + +Actor::Actor() + : visible_(true) + , visible_in_rt_(true) + , update_pausing_(false) + , hover_(false) + , pressed_(false) + , responsible_(false) + , dirty_visibility_(true) + , dirty_transform_(false) + , dirty_transform_inverse_(false) + , cascade_opacity_(false) + , show_border_(false) + , is_fast_transform_(true) + , parent_(nullptr) + , stage_(nullptr) + , hash_name_(0) + , z_order_(0) + , opacity_(1.f) + , displayed_opacity_(1.f) + , anchor_(default_anchor_x, default_anchor_y) +{ +} + +Actor::~Actor() {} + +void Actor::Update(Duration dt) +{ + UpdateActions(this, dt); + UpdateTimers(dt); + + if (!update_pausing_) + { + if (cb_update_) + cb_update_(dt); + + OnUpdate(dt); + } + + if (!children_.empty()) + { + ActorPtr next; + for (auto child = children_.first_item(); child; child = next) + { + next = child->next_item(); + child->Update(dt); + } + } +} + +void Actor::Render(RenderContext& ctx) +{ + if (!visible_) + return; + + UpdateTransform(); + + if (children_.empty()) + { + if (CheckVisibility(ctx)) + { + PrepareToRender(ctx); + OnRender(ctx); + } + } + else + { + // render children those are less than 0 in Z-Order + Actor* child = children_.first_item().get(); + while (child) + { + if (child->GetZOrder() >= 0) + break; + + child->Render(ctx); + child = child->next_item().get(); + } + + if (CheckVisibility(ctx)) + { + PrepareToRender(ctx); + OnRender(ctx); + } + + while (child) + { + child->Render(ctx); + child = child->next_item().get(); + } + } +} + +void Actor::PrepareToRender(RenderContext& ctx) +{ + ctx.SetTransform(transform_matrix_); + ctx.SetBrushOpacity(GetDisplayedOpacity()); +} + +void Actor::RenderBorder(RenderContext& ctx) +{ + if (show_border_ && !size_.IsOrigin()) + { + Rect bounds = GetBounds(); + + ctx.SetTransform(transform_matrix_); + + ctx.SetCurrentBrush(GetStage()->GetBorderFillBrush()); + ctx.FillRectangle(bounds); + + ctx.SetCurrentBrush(GetStage()->GetBorderStrokeBrush()); + ctx.DrawRectangle(bounds, 2.f); + } + + for (auto child = children_.first_item(); child; child = child->next_item()) + { + child->RenderBorder(ctx); + } +} + +bool Actor::CheckVisibility(RenderContext& ctx) const +{ + if (dirty_visibility_) + { + dirty_visibility_ = false; + + if (size_.IsOrigin()) + { + visible_in_rt_ = false; + } + else + { + visible_in_rt_ = ctx.CheckVisibility(GetBounds(), GetTransformMatrix()); + } + } + return visible_in_rt_; +} + +bool Actor::DispatchEvent(Event* evt) +{ + if (!visible_) + return true; + + // Dispatch to children those are greater than 0 in Z-Order + Actor* child = children_.last_item().get(); + while (child) + { + if (child->GetZOrder() < 0) + break; + + if (!child->DispatchEvent(evt)) + return false; + + child = child->prev_item().get(); + } + + if (!EventDispatcher::DispatchEvent(evt)) + return false; + + HandleEvent(evt); + + while (child) + { + if (!child->DispatchEvent(evt)) + return false; + + child = child->prev_item().get(); + } + return true; +} + +void Actor::HandleEvent(Event* evt) +{ + if (responsible_) + { + if (evt->IsType()) + { + auto mouse_evt = dynamic_cast(evt); + bool contains = ContainsPoint(mouse_evt->pos); + if (!hover_ && contains) + { + hover_ = true; + + MouseHoverEventPtr hover = new MouseHoverEvent; + hover->pos = mouse_evt->pos; + EventDispatcher::DispatchEvent(hover.get()); + } + else if (hover_ && !contains) + { + hover_ = false; + pressed_ = false; + + MouseOutEventPtr out = new MouseOutEvent; + out->pos = mouse_evt->pos; + EventDispatcher::DispatchEvent(out.get()); + } + } + + if (evt->IsType() && hover_) + { + pressed_ = true; + } + + if (evt->IsType() && pressed_) + { + pressed_ = false; + + auto mouse_up_evt = dynamic_cast(evt); + + MouseClickEventPtr click = new MouseClickEvent; + click->pos = mouse_up_evt->pos; + click->button = mouse_up_evt->button; + EventDispatcher::DispatchEvent(click.get()); + } + } +} + +Matrix3x2 const& Actor::GetTransformMatrix() const +{ + UpdateTransform(); + return transform_matrix_; +} + +Matrix3x2 const& Actor::GetTransformInverseMatrix() const +{ + UpdateTransform(); + if (dirty_transform_inverse_) + { + transform_matrix_inverse_ = transform_matrix_.Invert(); + dirty_transform_inverse_ = false; + } + return transform_matrix_inverse_; +} + +void Actor::UpdateTransform() const +{ + if (!dirty_transform_) + return; + + dirty_transform_ = false; + dirty_transform_inverse_ = true; + dirty_visibility_ = true; + + if (is_fast_transform_) + { + transform_matrix_ = Matrix3x2::Translation(transform_.position); + } + else + { + // matrix multiplication is optimized by expression template + transform_matrix_ = transform_.ToMatrix(); + } + + transform_matrix_.Translate(Point{ -size_.x * anchor_.x, -size_.y * anchor_.y }); + + if (parent_) + { + transform_matrix_ *= parent_->transform_matrix_; + } + + // update children's transform + for (auto child = children_.first_item().get(); child; child = child->next_item().get()) + child->dirty_transform_ = true; +} + +void Actor::UpdateOpacity() +{ + if (parent_ && parent_->IsCascadeOpacityEnabled()) + { + displayed_opacity_ = opacity_ * parent_->displayed_opacity_; + } + else + { + displayed_opacity_ = opacity_; + } + + for (Actor* child = children_.first_item().get(); child; child = child->next_item().get()) + { + child->UpdateOpacity(); + } +} + +void Actor::SetStage(Stage* stage) +{ + if (stage_ != stage) + { + stage_ = stage; + for (Actor* child = children_.first_item().get(); child; child = child->next_item().get()) + { + child->stage_ = stage; + } + } +} + +void Actor::Reorder() +{ + if (parent_) + { + ActorPtr me = this; + + parent_->children_.remove(me); + + Actor* sibling = parent_->children_.last_item().get(); + + if (sibling && sibling->GetZOrder() > z_order_) + { + sibling = sibling->prev_item().get(); + while (sibling) + { + if (sibling->GetZOrder() <= z_order_) + break; + sibling = sibling->prev_item().get(); + } + } + + if (sibling) + { + parent_->children_.insert_after(me, sibling); + } + else + { + parent_->children_.push_front(me); + } + } +} + +void Actor::SetZOrder(int zorder) +{ + if (z_order_ != zorder) + { + z_order_ = zorder; + Reorder(); + } +} + +void Actor::SetOpacity(float opacity) +{ + if (opacity_ == opacity) + return; + + displayed_opacity_ = opacity_ = std::min(std::max(opacity, 0.f), 1.f); + UpdateOpacity(); +} + +void Actor::SetCascadeOpacityEnabled(bool enabled) +{ + if (cascade_opacity_ == enabled) + return; + + cascade_opacity_ = enabled; + UpdateOpacity(); +} + +void Actor::SetAnchor(Vec2 const& anchor) +{ + if (anchor_ == anchor) + return; + + anchor_ = anchor; + dirty_transform_ = true; +} + +void Actor::SetWidth(float width) +{ + SetSize(Size{ width, size_.y }); +} + +void Actor::SetHeight(float height) +{ + SetSize(Size{ size_.x, height }); +} + +void Actor::SetSize(Size const& size) +{ + if (size_ == size) + return; + + size_ = size; + dirty_transform_ = true; +} + +void Actor::SetTransform(Transform const& transform) +{ + transform_ = transform; + dirty_transform_ = true; + is_fast_transform_ = false; +} + +void Actor::SetVisible(bool val) +{ + visible_ = val; +} + +void Actor::SetName(String const& name) +{ + if (!IsName(name)) + { + ObjectBase::SetName(name); + hash_name_ = std::hash{}(name); + } +} + +void Actor::SetPosition(const Point& pos) +{ + if (transform_.position == pos) + return; + + transform_.position = pos; + dirty_transform_ = true; +} + +void Actor::SetPositionX(float x) +{ + SetPosition(Point{ x, transform_.position.y }); +} + +void Actor::SetPositionY(float y) +{ + SetPosition(Point{ transform_.position.x, y }); +} + +void Actor::Move(Vec2 const& v) +{ + this->SetPosition(Point{ transform_.position.x + v.x, transform_.position.y + v.y }); +} + +void Actor::SetScale(Vec2 const& scale) +{ + if (transform_.scale == scale) + return; + + transform_.scale = scale; + dirty_transform_ = true; + is_fast_transform_ = false; +} + +void Actor::SetSkew(Vec2 const& skew) +{ + if (transform_.skew == skew) + return; + + transform_.skew = skew; + dirty_transform_ = true; + is_fast_transform_ = false; +} + +void Actor::SetRotation(float angle) +{ + if (transform_.rotation == angle) + return; + + transform_.rotation = angle; + dirty_transform_ = true; + is_fast_transform_ = false; +} + +void Actor::AddChild(Actor* child, int zorder) +{ + KGE_ASSERT(child && "Actor::AddChild failed, NULL pointer exception"); + + if (child) + { + KGE_ASSERT(!child->parent_ && "Actor::AddChild failed, the actor to be added already has a parent"); #ifdef KGE_DEBUG - for (Actor* parent = parent_; parent; parent = parent->parent_) - { - if (parent == child) - { - KGE_ERROR(L"A actor cannot be its own parent"); - return; - } - } + for (Actor* parent = parent_; parent; parent = parent->parent_) + { + if (parent == child) + { + KGE_ERROR(L"A actor cannot be its own parent"); + return; + } + } -#endif // KGE_DEBUG +#endif // KGE_DEBUG - children_.push_back(child); - child->parent_ = this; - child->SetStage(this->stage_); - - child->dirty_transform_ = true; - child->z_order_ = zorder; - child->Reorder(); - child->UpdateOpacity(); - } - } - - void Actor::AddChild(ActorPtr child, int zorder) - { - AddChild(child.get()); - } - - void Actor::AddChildren(Vector const& children) - { - for (const auto& actor : children) - { - this->AddChild(actor); - } - } - - Rect Actor::GetBounds() const - { - return Rect{ Point{}, size_ }; - } - - Rect Actor::GetBoundingBox() const - { - return GetTransformMatrix().Transform(GetBounds()); - } - - Vector Actor::GetChildren(String const& name) const - { - Vector children; - size_t hash_code = std::hash{}(name); - - for (auto child = children_.first_item().get(); child; child = child->next_item().get()) - { - if (child->hash_name_ == hash_code && child->IsName(name)) - { - children.push_back(const_cast(child)); - } - } - return children; - } - - Actor* Actor::GetChild(String const& name) const - { - size_t hash_code = std::hash{}(name); - - for (auto child = children_.first_item().get(); child; child = child->next_item().get()) - { - if (child->hash_name_ == hash_code && child->IsName(name)) - { - return const_cast(child); - } - } - return nullptr; - } - - Actor::Children& Actor::GetAllChildren() - { - return children_; - } - - Actor::Children const & Actor::GetAllChildren() const - { - return children_; - } - - void Actor::RemoveFromParent() - { - if (parent_) - { - parent_->RemoveChild(this); - } - } - - void Actor::RemoveChild(ActorPtr child) - { - RemoveChild(child.get()); - } - - void Actor::RemoveChild(Actor * child) - { - KGE_ASSERT(child && "Actor::RemoveChild failed, NULL pointer exception"); - - if (children_.empty()) - return; - - if (child) - { - child->parent_ = nullptr; - if (child->stage_) child->SetStage(nullptr); - children_.remove(ActorPtr(child)); - } - } - - void Actor::RemoveChildren(String const& child_name) - { - if (children_.empty()) - { - return; - } - - size_t hash_code = std::hash{}(child_name); - - Actor* next; - for (Actor* child = children_.first_item().get(); child; child = next) - { - next = child->next_item().get(); - - if (child->hash_name_ == hash_code && child->IsName(child_name)) - { - RemoveChild(child); - } - } - } - - void Actor::RemoveAllChildren() - { - children_.clear(); - } - - void Actor::SetResponsible(bool enable) - { - responsible_ = enable; - } - - bool Actor::ContainsPoint(const Point& point) const - { - if (size_.x == 0.f || size_.y == 0.f) - return false; - - Point local = GetTransformInverseMatrix().Transform(point); - return GetBounds().ContainsPoint(local); - } + children_.push_back(child); + child->parent_ = this; + child->SetStage(this->stage_); + child->dirty_transform_ = true; + child->z_order_ = zorder; + child->Reorder(); + child->UpdateOpacity(); + } } + +void Actor::AddChild(ActorPtr child, int zorder) +{ + AddChild(child.get()); +} + +void Actor::AddChildren(Vector const& children) +{ + for (const auto& actor : children) + { + this->AddChild(actor); + } +} + +Rect Actor::GetBounds() const +{ + return Rect{ Point{}, size_ }; +} + +Rect Actor::GetBoundingBox() const +{ + return GetTransformMatrix().Transform(GetBounds()); +} + +Vector Actor::GetChildren(String const& name) const +{ + Vector children; + size_t hash_code = std::hash{}(name); + + for (auto child = children_.first_item().get(); child; child = child->next_item().get()) + { + if (child->hash_name_ == hash_code && child->IsName(name)) + { + children.push_back(const_cast(child)); + } + } + return children; +} + +Actor* Actor::GetChild(String const& name) const +{ + size_t hash_code = std::hash{}(name); + + for (auto child = children_.first_item().get(); child; child = child->next_item().get()) + { + if (child->hash_name_ == hash_code && child->IsName(name)) + { + return const_cast(child); + } + } + return nullptr; +} + +Actor::Children& Actor::GetAllChildren() +{ + return children_; +} + +Actor::Children const& Actor::GetAllChildren() const +{ + return children_; +} + +void Actor::RemoveFromParent() +{ + if (parent_) + { + parent_->RemoveChild(this); + } +} + +void Actor::RemoveChild(ActorPtr child) +{ + RemoveChild(child.get()); +} + +void Actor::RemoveChild(Actor* child) +{ + KGE_ASSERT(child && "Actor::RemoveChild failed, NULL pointer exception"); + + if (children_.empty()) + return; + + if (child) + { + child->parent_ = nullptr; + if (child->stage_) + child->SetStage(nullptr); + children_.remove(ActorPtr(child)); + } +} + +void Actor::RemoveChildren(String const& child_name) +{ + if (children_.empty()) + { + return; + } + + size_t hash_code = std::hash{}(child_name); + + Actor* next; + for (Actor* child = children_.first_item().get(); child; child = next) + { + next = child->next_item().get(); + + if (child->hash_name_ == hash_code && child->IsName(child_name)) + { + RemoveChild(child); + } + } +} + +void Actor::RemoveAllChildren() +{ + children_.clear(); +} + +void Actor::SetResponsible(bool enable) +{ + responsible_ = enable; +} + +bool Actor::ContainsPoint(const Point& point) const +{ + if (size_.x == 0.f || size_.y == 0.f) + return false; + + Point local = GetTransformInverseMatrix().Transform(point); + return GetBounds().ContainsPoint(local); +} + +} // namespace kiwano diff --git a/src/kiwano/2d/Actor.h b/src/kiwano/2d/Actor.h index 30564cf0..f8fa0f2a 100644 --- a/src/kiwano/2d/Actor.h +++ b/src/kiwano/2d/Actor.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -19,667 +19,674 @@ // THE SOFTWARE. #pragma once -#include -#include -#include -#include #include #include -#include #include +#include +#include +#include +#include namespace kiwano { - class Stage; - class Director; - class RenderTarget; - - KGE_DECLARE_SMART_PTR(Actor); - - /** - * \~chinese - * \defgroup Actors ɫ - */ - - /** - * \addtogroup Actors - * @{ - */ - - /** - * \~chinese - * @brief ɫ - * @details ɫ̨ԪأȾ¡¼ַȹܵСλҲǶʱ¼ȹܵ - */ - class KGE_API Actor - : public ObjectBase - , public TimerManager - , public ActionManager - , public EventDispatcher - , public IntrusiveListItem - { - friend class Director; - friend class Transition; - friend IntrusiveList; - - public: - /// \~chinese - /// @brief ӳԱб - using Children = IntrusiveList; - - /// \~chinese - /// @brief ɫ»ص - using UpdateCallback = Function; - - Actor(); - - /// \~chinese - /// @brief ½ɫ - /// @details ÿ֡ˢǰøúظúʵֽɫĸ´ - /// @param dt һθµʱ - virtual void OnUpdate(Duration dt); - - /// \~chinese - /// @brief Ⱦɫ - /// @details ÿ֡ˢʱøúĬϲȾظúʵ־Ⱦ - /// @param rt ȾĿ - virtual void OnRender(RenderTarget* rt); - - /// \~chinese - /// @brief ȡʾ״̬ - bool IsVisible() const; - - /// \~chinese - /// @brief ȡӦ״̬ - bool IsResponsible() const; - - /// \~chinese - /// @brief Ƿü͸ - bool IsCascadeOpacityEnabled() const; - - /// \~chinese - /// @brief ȡƵ Hash ֵ - size_t GetHashName() const; - - /// \~chinese - /// @brief ȡ Z ˳ - int GetZOrder() const; - - /// \~chinese - /// @brief ȡ - Point const& GetPosition() const; - - /// \~chinese - /// @brief ȡ x - float GetPositionX() const; - - /// \~chinese - /// @brief ȡ y - float GetPositionY() const; - - /// \~chinese - /// @brief ȡű - Point const& GetScale() const; - - /// \~chinese - /// @brief ȡű - float GetScaleX() const; - - /// \~chinese - /// @brief ȡű - float GetScaleY() const; - - /// \~chinese - /// @brief ȡнǶ - Point const& GetSkew() const; - - /// \~chinese - /// @brief ȡнǶ - float GetSkewX() const; - - /// \~chinese - /// @brief ȡнǶ - float GetSkewY() const; - - /// \~chinese - /// @brief ȡתǶ - float GetRotation() const; - - /// \~chinese - /// @brief ȡ - float GetWidth() const; - - /// \~chinese - /// @brief ȡ߶ - float GetHeight() const; - - /// \~chinese - /// @brief ȡС - Size const& GetSize() const; - - /// \~chinese - /// @brief ȡźĿ - float GetScaledWidth() const; +class Stage; +class Director; +class RenderContext; + +KGE_DECLARE_SMART_PTR(Actor); + +/** + * \~chinese + * \defgroup Actors ɫ + */ + +/** + * \addtogroup Actors + * @{ + */ + +/** + * \~chinese + * @brief ɫ + * @details + * ɫ̨ԪأȾ¡¼ַȹܵСλҲǶʱ¼ȹܵ + */ +class KGE_API Actor + : public virtual ObjectBase + , public TimerManager + , public ActionManager + , public EventDispatcher + , protected IntrusiveListItem +{ + friend class Director; + friend class Transition; + friend IntrusiveList; + +public: + /// \~chinese + /// @brief ӳԱб + using Children = IntrusiveList; + + /// \~chinese + /// @brief ɫ»ص + using UpdateCallback = Function; + + Actor(); + + virtual ~Actor(); + + /// \~chinese + /// @brief ½ɫ + /// @details ÿ֡ˢǰøúظúʵֽɫĸ´ + /// @param dt һθµʱ + virtual void OnUpdate(Duration dt); + + /// \~chinese + /// @brief Ⱦɫ + /// @details + /// ÿ֡ˢʱøúĬϲȾظúʵ־Ⱦ + /// @param ctx Ⱦ + virtual void OnRender(RenderContext& ctx); + + /// \~chinese + /// @brief ȡʾ״̬ + bool IsVisible() const; + + /// \~chinese + /// @brief ȡӦ״̬ + bool IsResponsible() const; + + /// \~chinese + /// @brief Ƿü͸ + bool IsCascadeOpacityEnabled() const; + + /// \~chinese + /// @brief ȡƵ Hash ֵ + size_t GetHashName() const; + + /// \~chinese + /// @brief ȡ Z ˳ + int GetZOrder() const; + + /// \~chinese + /// @brief ȡ + Point const& GetPosition() const; + + /// \~chinese + /// @brief ȡ x + float GetPositionX() const; + + /// \~chinese + /// @brief ȡ y + float GetPositionY() const; + + /// \~chinese + /// @brief ȡ + float GetWidth() const; + + /// \~chinese + /// @brief ȡ߶ + float GetHeight() const; + + /// \~chinese + /// @brief ȡС + Size const& GetSize() const; + + /// \~chinese + /// @brief ȡźĿ + float GetScaledWidth() const; + + /// \~chinese + /// @brief ȡźĸ߶ + float GetScaledHeight() const; + + /// \~chinese + /// @brief ȡźĴС + Size GetScaledSize() const; + + /// \~chinese + /// @brief ȡê + Point const& GetAnchor() const; + + /// \~chinese + /// @brief ȡ x ê + float GetAnchorX() const; + + /// \~chinese + /// @brief ȡ y ê + float GetAnchorY() const; + + /// \~chinese + /// @brief ȡ͸ + float GetOpacity() const; - /// \~chinese - /// @brief ȡźĸ߶ - float GetScaledHeight() const; + /// \~chinese + /// @brief ȡʾ͸ + float GetDisplayedOpacity() const; - /// \~chinese - /// @brief ȡźĴС - Size GetScaledSize() const; + /// \~chinese + /// @brief ȡתǶ + float GetRotation() const; - /// \~chinese - /// @brief ȡê - Point const& GetAnchor() const; + /// \~chinese + /// @brief ȡű + Point const& GetScale() const; - /// \~chinese - /// @brief ȡ x ê - float GetAnchorX() const; + /// \~chinese + /// @brief ȡű + float GetScaleX() const; + + /// \~chinese + /// @brief ȡű + float GetScaleY() const; - /// \~chinese - /// @brief ȡ y ê - float GetAnchorY() const; + /// \~chinese + /// @brief ȡнǶ + Point const& GetSkew() const; - /// \~chinese - /// @brief ȡ͸ - float GetOpacity() const; + /// \~chinese + /// @brief ȡнǶ + float GetSkewX() const; - /// \~chinese - /// @brief ȡʾ͸ - float GetDisplayedOpacity() const; + /// \~chinese + /// @brief ȡнǶ + float GetSkewY() const; - /// \~chinese - /// @brief ȡ任 - Transform GetTransform() const; + /// \~chinese + /// @brief ȡ任 + Transform GetTransform() const; - /// \~chinese - /// @brief ȡɫ - Actor* GetParent() const; + /// \~chinese + /// @brief ȡɫ + Actor* GetParent() const; - /// \~chinese - /// @brief ȡ̨ - Stage* GetStage() const; + /// \~chinese + /// @brief ȡ̨ + Stage* GetStage() const; - /// \~chinese - /// @brief ȡ߿ - virtual Rect GetBounds() const; + /// \~chinese + /// @brief ȡ߿ + virtual Rect GetBounds() const; - /// \~chinese - /// @brief ȡаΧ - virtual Rect GetBoundingBox() const; + /// \~chinese + /// @brief ȡаΧ + virtual Rect GetBoundingBox() const; - /// \~chinese - /// @brief ȡά任 - Matrix3x2 const& GetTransformMatrix() const; + /// \~chinese + /// @brief ȡά任 + Matrix3x2 const& GetTransformMatrix() const; - /// \~chinese - /// @brief ȡά任 - Matrix3x2 const& GetTransformInverseMatrix() const; + /// \~chinese + /// @brief ȡά任 + Matrix3x2 const& GetTransformInverseMatrix() const; - /// \~chinese - /// @brief ýɫǷɼ - void SetVisible(bool val); + /// \~chinese + /// @brief ýɫǷɼ + void SetVisible(bool val); - /// \~chinese - /// @brief - void SetName(String const& name); + /// \~chinese + /// @brief + void SetName(String const& name); - /// \~chinese - /// @brief - virtual void SetPosition(Point const& point); + /// \~chinese + /// @brief + virtual void SetPosition(Point const& point); - /// \~chinese - /// @brief - void SetPosition(float x, float y); + /// \~chinese + /// @brief + void SetPosition(float x, float y); - /// \~chinese - /// @brief ú - void SetPositionX(float x); + /// \~chinese + /// @brief ú + void SetPositionX(float x); - /// \~chinese - /// @brief - void SetPositionY(float y); + /// \~chinese + /// @brief + void SetPositionY(float y); - /// \~chinese - /// @brief ƶ - void Move(Vec2 const& v); + /// \~chinese + /// @brief ƶ + void Move(Vec2 const& v); - /// \~chinese - /// @brief ƶ - void Move(float vx, float vy); + /// \~chinese + /// @brief ƶ + void Move(float vx, float vy); - /// \~chinese - /// @brief űĬΪ (1.0, 1.0) - virtual void SetScale(Vec2 const& scale); + /// \~chinese + /// @brief űĬΪ (1.0, 1.0) + virtual void SetScale(Vec2 const& scale); - /// \~chinese - /// @brief űĬΪ (1.0, 1.0) - void SetScale(float scalex, float scaley); + /// \~chinese + /// @brief űĬΪ (1.0, 1.0) + void SetScale(float scalex, float scaley); - /// \~chinese - /// @brief ôнǶȣĬΪ (0, 0) - virtual void SetSkew(Vec2 const& skew); + /// \~chinese + /// @brief ôнǶȣĬΪ (0, 0) + virtual void SetSkew(Vec2 const& skew); - /// \~chinese - /// @brief ôнǶȣĬΪ (0, 0) - void SetSkew(float skewx, float skewy); + /// \~chinese + /// @brief ôнǶȣĬΪ (0, 0) + void SetSkew(float skewx, float skewy); - /// \~chinese - /// @brief תǶȣĬΪ 0 - virtual void SetRotation(float rotation); + /// \~chinese + /// @brief תǶȣĬΪ 0 + virtual void SetRotation(float rotation); - /// \~chinese - /// @brief êλãĬΪ (0, 0), Χ [0, 1] - virtual void SetAnchor(Vec2 const& anchor); + /// \~chinese + /// @brief êλãĬΪ (0, 0), Χ [0, 1] + virtual void SetAnchor(Vec2 const& anchor); - /// \~chinese - /// @brief êλãĬΪ (0, 0), Χ [0, 1] - void SetAnchor(float anchorx, float anchory); + /// \~chinese + /// @brief êλãĬΪ (0, 0), Χ [0, 1] + void SetAnchor(float anchorx, float anchory); - /// \~chinese - /// @brief ޸Ŀ - virtual void SetWidth(float width); + /// \~chinese + /// @brief ޸Ŀ + virtual void SetWidth(float width); - /// \~chinese - /// @brief ޸ĸ߶ - virtual void SetHeight(float height); + /// \~chinese + /// @brief ޸ĸ߶ + virtual void SetHeight(float height); - /// \~chinese - /// @brief ޸ĴС - virtual void SetSize(Size const& size); + /// \~chinese + /// @brief ޸ĴС + virtual void SetSize(Size const& size); - /// \~chinese - /// @brief ޸ĴС - void SetSize(float width, float height); + /// \~chinese + /// @brief ޸ĴС + void SetSize(float width, float height); - /// \~chinese - /// @brief ͸ȣĬΪ 1.0, Χ [0, 1] - virtual void SetOpacity(float opacity); + /// \~chinese + /// @brief ͸ȣĬΪ 1.0, Χ [0, 1] + virtual void SetOpacity(float opacity); - /// \~chinese - /// @brief ûü͸ - void SetCascadeOpacityEnabled(bool enabled); + /// \~chinese + /// @brief ûü͸ + void SetCascadeOpacityEnabled(bool enabled); - /// \~chinese - /// @brief öά任 - void SetTransform(Transform const& transform); + /// \~chinese + /// @brief öά任 + void SetTransform(Transform const& transform); - /// \~chinese - /// @brief Z ˳ĬΪ 0 - void SetZOrder(int zorder); - - /// \~chinese - /// @brief ýɫǷӦĬΪ false - /// @details ӦĽɫյ Hover | Out | Click Ϣ - void SetResponsible(bool enable); - - /// \~chinese - /// @brief ӽɫ - void AddChild(ActorPtr child, int zorder = 0); - - /// \~chinese - /// @brief ӽɫ - void AddChild(Actor* child, int zorder = 0); - - /// \~chinese - /// @brief Ӷӽɫ - void AddChildren(Vector const& children); - - /// \~chinese - /// @brief ȡͬӽɫ - Actor* GetChild(String const& name) const; - - /// \~chinese - /// @brief ȡͬӽɫ - Vector GetChildren(String const& name) const; - - /// \~chinese - /// @brief ȡȫӽɫ - Children& GetAllChildren(); - - /// \~chinese - /// @brief ȡȫӽɫ - Children const& GetAllChildren() const; - - /// \~chinese - /// @brief Ƴӽɫ - void RemoveChild(ActorPtr child); - - /// \~chinese - /// @brief Ƴӽɫ - void RemoveChild(Actor* child); - - /// \~chinese - /// @brief Ƴͬӽɫ - void RemoveChildren(String const& child_name); - - /// \~chinese - /// @brief Ƴнɫ - void RemoveAllChildren(); - - /// \~chinese - /// @brief ӸɫƳ - void RemoveFromParent(); - - /// \~chinese - /// @brief жϵǷڽɫ - virtual bool ContainsPoint(const Point& point) const; - - /// \~chinese - /// @brief ͣɫ - void PauseUpdating(); - - /// \~chinese - /// @brief ɫ - void ResumeUpdating(); - - /// \~chinese - /// @brief ɫǷͣ - bool IsUpdatePausing() const; - - /// \~chinese - /// @brief øʱĻص - void SetCallbackOnUpdate(UpdateCallback const& cb); - - /// \~chinese - /// @brief ȡʱĻص - UpdateCallback GetCallbackOnUpdate() const; - - /// \~chinese - /// @brief Ⱦɫ߽ - void ShowBorder(bool show); - - /// \~chinese - /// @brief ַ¼ - void Dispatch(Event& evt) override; - - /// \~chinese - /// @brief Ĭê - static void SetDefaultAnchor(float anchor_x, float anchor_y); - - protected: - /// \~chinese - /// @brief ӽɫ - virtual void Update(Duration dt); - - /// \~chinese - /// @brief Ⱦӽɫ - virtual void Render(RenderTarget* rt); - - /// \~chinese - /// @brief ӽɫı߽ - virtual void RenderBorder(RenderTarget* rt); - - /// \~chinese - /// @brief ǷȾĿ - virtual bool CheckVisibilty(RenderTarget* rt) const; - - /// \~chinese - /// @brief ȾǰʼȾĿ״̬ CheckVisibilty ʱøú - virtual void PrepareToRender(RenderTarget* rt); - - /// \~chinese - /// @brief ԼĶά任֪ͨӽɫ - void UpdateTransform() const; - - /// \~chinese - /// @brief Լӽɫ͸ - void UpdateOpacity(); - - /// \~chinese - /// @brief ӽɫZ˳ - void Reorder(); - - /// \~chinese - /// @brief ýڵ̨ - void SetStage(Stage* stage); - - private: - bool visible_; - bool update_pausing_; - bool cascade_opacity_; - bool show_border_; - bool hover_; - bool pressed_; - bool responsible_; - int z_order_; - float opacity_; - float displayed_opacity_; - Actor* parent_; - Stage* stage_; - size_t hash_name_; - Point anchor_; - Size size_; - Children children_; - UpdateCallback cb_update_; - Transform transform_; - - bool is_fast_transform_; - mutable bool visible_in_rt_; - mutable bool dirty_visibility_; - mutable bool dirty_transform_; - mutable bool dirty_transform_inverse_; - mutable Matrix3x2 transform_matrix_; - mutable Matrix3x2 transform_matrix_inverse_; - }; - - /** @} */ - - - inline void Actor::OnUpdate(Duration dt) - { - KGE_NOT_USED(dt); - } - - inline void Actor::OnRender(RenderTarget* rt) - { - KGE_NOT_USED(rt); - } - - inline bool Actor::IsVisible() const - { - return visible_; - } - - inline bool Actor::IsResponsible() const - { - return responsible_; - } - - inline bool Actor::IsCascadeOpacityEnabled() const - { - return cascade_opacity_; - } - - inline size_t Actor::GetHashName() const - { - return hash_name_; - } - - inline int Actor::GetZOrder() const - { - return z_order_; - } - - inline Point const& Actor::GetPosition() const - { - return transform_.position; - } - - inline float Actor::GetPositionX() const - { - return GetPosition().x; - } - - inline float Actor::GetPositionY() const - { - return GetPosition().y; - } - - inline Point const& Actor::GetScale() const - { - return transform_.scale; - } - - inline float Actor::GetScaleX() const - { - return GetScale().x; - } - - inline float Actor::GetScaleY() const - { - return GetScale().y; - } - - inline Point const& Actor::GetSkew() const - { - return transform_.skew; - } - - inline float Actor::GetSkewX() const - { - return GetSkew().x; - } - - inline float Actor::GetSkewY() const - { - return GetSkew().y; - } - - inline float Actor::GetRotation() const - { - return transform_.rotation; - } - - inline float Actor::GetWidth() const - { - return GetSize().x; - } - - inline float Actor::GetHeight() const - { - return GetSize().y; - } - - inline Size const& Actor::GetSize() const - { - return size_; - } - - inline float Actor::GetScaledWidth() const - { - return GetWidth() * GetScaleX(); - } - - inline float Actor::GetScaledHeight() const - { - return GetHeight() * GetScaleY(); - } - - inline Size Actor::GetScaledSize() const - { - return Size{ GetScaledWidth(), GetScaledHeight() }; - } - - inline Point const& Actor::GetAnchor() const - { - return anchor_; - } - - inline float Actor::GetAnchorX() const - { - return GetAnchor().x; - } - - inline float Actor::GetAnchorY() const - { - return GetAnchor().y; - } - - inline float Actor::GetOpacity() const - { - return opacity_; - } - - inline float Actor::GetDisplayedOpacity() const - { - return displayed_opacity_; - } - - inline Transform Actor::GetTransform() const - { - return transform_; - } - - inline Actor* Actor::GetParent() const - { - return parent_; - } - - inline Stage* Actor::GetStage() const - { - return stage_; - } - - inline void Actor::PauseUpdating() - { - update_pausing_ = true; - } - - inline void Actor::ResumeUpdating() - { - update_pausing_ = false; - } - - inline bool Actor::IsUpdatePausing() const - { - return update_pausing_; - } - - inline void Actor::SetCallbackOnUpdate(UpdateCallback const& cb) - { - cb_update_ = cb; - } - - inline Actor::UpdateCallback Actor::GetCallbackOnUpdate() const - { - return cb_update_; - } - - inline void Actor::ShowBorder(bool show) - { - show_border_ = show; - } - - inline void Actor::SetPosition(float x, float y) - { - SetPosition(Point{ x, y }); - } - - inline void Actor::Move(float vx, float vy) - { - Move(Vec2{ vx, vy }); - } - - inline void Actor::SetScale(float scalex, float scaley) - { - SetScale(Vec2{ scalex, scaley }); - } - - inline void Actor::SetAnchor(float anchorx, float anchory) - { - SetAnchor(Vec2{ anchorx, anchory }); - } - - inline void Actor::SetSize(float width, float height) - { - SetSize(Size{ width, height }); - } - - inline void Actor::SetSkew(float skewx, float skewy) - { - SetSkew(Vec2{ skewx, skewy }); - } + /// \~chinese + /// @brief Z ˳ĬΪ 0 + void SetZOrder(int zorder); + /// \~chinese + /// @brief ýɫǷӦĬΪ false + /// @details ӦĽɫյ Hover | Out | Click Ϣ + void SetResponsible(bool enable); + + /// \~chinese + /// @brief ӽɫ + void AddChild(ActorPtr child, int zorder = 0); + + /// \~chinese + /// @brief ӽɫ + void AddChild(Actor* child, int zorder = 0); + + /// \~chinese + /// @brief Ӷӽɫ + void AddChildren(Vector const& children); + + /// \~chinese + /// @brief ȡͬӽɫ + Actor* GetChild(String const& name) const; + + /// \~chinese + /// @brief ȡͬӽɫ + Vector GetChildren(String const& name) const; + + /// \~chinese + /// @brief ȡȫӽɫ + Children& GetAllChildren(); + + /// \~chinese + /// @brief ȡȫӽɫ + Children const& GetAllChildren() const; + + /// \~chinese + /// @brief Ƴӽɫ + void RemoveChild(ActorPtr child); + + /// \~chinese + /// @brief Ƴӽɫ + void RemoveChild(Actor* child); + + /// \~chinese + /// @brief Ƴͬӽɫ + void RemoveChildren(String const& child_name); + + /// \~chinese + /// @brief Ƴнɫ + void RemoveAllChildren(); + + /// \~chinese + /// @brief ӸɫƳ + void RemoveFromParent(); + + /// \~chinese + /// @brief ͣɫ + void PauseUpdating(); + + /// \~chinese + /// @brief ɫ + void ResumeUpdating(); + + /// \~chinese + /// @brief ɫǷͣ + bool IsUpdatePausing() const; + + /// \~chinese + /// @brief øʱĻص + void SetCallbackOnUpdate(UpdateCallback const& cb); + + /// \~chinese + /// @brief ȡʱĻص + UpdateCallback GetCallbackOnUpdate() const; + + /// \~chinese + /// @brief жϵǷڽɫ + virtual bool ContainsPoint(const Point& point) const; + + /// \~chinese + /// @brief Ⱦɫ߽ + void ShowBorder(bool show); + + /// \~chinese + /// @brief ַ¼ + /// @param evt ¼ + /// @return Ƿַ¼ + virtual bool DispatchEvent(Event* evt); + + /// \~chinese + /// @brief Ĭê + static void SetDefaultAnchor(float anchor_x, float anchor_y); + +protected: + /// \~chinese + /// @brief ӽɫ + virtual void Update(Duration dt); + + /// \~chinese + /// @brief Ⱦӽɫ + virtual void Render(RenderContext& ctx); + + /// \~chinese + /// @brief ӽɫı߽ + virtual void RenderBorder(RenderContext& ctx); + + /// \~chinese + /// @brief ǷȾĵ + virtual bool CheckVisibility(RenderContext& ctx) const; + + /// \~chinese + /// @brief ȾǰʼȾ״̬ CheckVisibility ʱøú + virtual void PrepareToRender(RenderContext& ctx); + + /// \~chinese + /// @brief ԼĶά任֪ͨӽɫ + void UpdateTransform() const; + + /// \~chinese + /// @brief Լӽɫ͸ + void UpdateOpacity(); + + /// \~chinese + /// @brief ӽɫZ˳ + void Reorder(); + + /// \~chinese + /// @brief ýڵ̨ + void SetStage(Stage* stage); + + /// \~chinese + /// @brief ¼ + void HandleEvent(Event* evt); + +private: + bool visible_; + bool update_pausing_; + bool cascade_opacity_; + bool show_border_; + bool hover_; + bool pressed_; + bool responsible_; + int z_order_; + float opacity_; + float displayed_opacity_; + Actor* parent_; + Stage* stage_; + size_t hash_name_; + Point anchor_; + Size size_; + Children children_; + UpdateCallback cb_update_; + Transform transform_; + + bool is_fast_transform_; + mutable bool visible_in_rt_; + mutable bool dirty_visibility_; + mutable bool dirty_transform_; + mutable bool dirty_transform_inverse_; + mutable Matrix3x2 transform_matrix_; + mutable Matrix3x2 transform_matrix_inverse_; +}; + +/** @} */ + +inline void Actor::OnUpdate(Duration dt) +{ + KGE_NOT_USED(dt); } + +inline void Actor::OnRender(RenderContext& ctx) +{ + KGE_NOT_USED(ctx); +} + +inline bool Actor::IsVisible() const +{ + return visible_; +} + +inline bool Actor::IsResponsible() const +{ + return responsible_; +} + +inline bool Actor::IsCascadeOpacityEnabled() const +{ + return cascade_opacity_; +} + +inline size_t Actor::GetHashName() const +{ + return hash_name_; +} + +inline int Actor::GetZOrder() const +{ + return z_order_; +} + +inline Point const& Actor::GetPosition() const +{ + return transform_.position; +} + +inline float Actor::GetPositionX() const +{ + return GetPosition().x; +} + +inline float Actor::GetPositionY() const +{ + return GetPosition().y; +} + +inline Point const& Actor::GetScale() const +{ + return transform_.scale; +} + +inline float Actor::GetScaleX() const +{ + return GetScale().x; +} + +inline float Actor::GetScaleY() const +{ + return GetScale().y; +} + +inline Point const& Actor::GetSkew() const +{ + return transform_.skew; +} + +inline float Actor::GetSkewX() const +{ + return GetSkew().x; +} + +inline float Actor::GetSkewY() const +{ + return GetSkew().y; +} + +inline float Actor::GetRotation() const +{ + return transform_.rotation; +} + +inline float Actor::GetWidth() const +{ + return GetSize().x; +} + +inline float Actor::GetHeight() const +{ + return GetSize().y; +} + +inline Size const& Actor::GetSize() const +{ + return size_; +} + +inline float Actor::GetScaledWidth() const +{ + return GetWidth() * GetScaleX(); +} + +inline float Actor::GetScaledHeight() const +{ + return GetHeight() * GetScaleY(); +} + +inline Size Actor::GetScaledSize() const +{ + return Size{ GetScaledWidth(), GetScaledHeight() }; +} + +inline Point const& Actor::GetAnchor() const +{ + return anchor_; +} + +inline float Actor::GetAnchorX() const +{ + return GetAnchor().x; +} + +inline float Actor::GetAnchorY() const +{ + return GetAnchor().y; +} + +inline float Actor::GetOpacity() const +{ + return opacity_; +} + +inline float Actor::GetDisplayedOpacity() const +{ + return displayed_opacity_; +} + +inline Transform Actor::GetTransform() const +{ + return transform_; +} + +inline Actor* Actor::GetParent() const +{ + return parent_; +} + +inline Stage* Actor::GetStage() const +{ + return stage_; +} + +inline void Actor::PauseUpdating() +{ + update_pausing_ = true; +} + +inline void Actor::ResumeUpdating() +{ + update_pausing_ = false; +} + +inline bool Actor::IsUpdatePausing() const +{ + return update_pausing_; +} + +inline void Actor::SetCallbackOnUpdate(UpdateCallback const& cb) +{ + cb_update_ = cb; +} + +inline Actor::UpdateCallback Actor::GetCallbackOnUpdate() const +{ + return cb_update_; +} + +inline void Actor::ShowBorder(bool show) +{ + show_border_ = show; +} + +inline void Actor::SetPosition(float x, float y) +{ + SetPosition(Point{ x, y }); +} + +inline void Actor::Move(float vx, float vy) +{ + Move(Vec2{ vx, vy }); +} + +inline void Actor::SetScale(float scalex, float scaley) +{ + SetScale(Vec2{ scalex, scaley }); +} + +inline void Actor::SetAnchor(float anchorx, float anchory) +{ + SetAnchor(Vec2{ anchorx, anchory }); +} + +inline void Actor::SetSize(float width, float height) +{ + SetSize(Size{ width, height }); +} + +inline void Actor::SetSkew(float skewx, float skewy) +{ + SetSkew(Vec2{ skewx, skewy }); +} +} // namespace kiwano diff --git a/src/kiwano/2d/Button.cpp b/src/kiwano/2d/Button.cpp new file mode 100644 index 00000000..7206af50 --- /dev/null +++ b/src/kiwano/2d/Button.cpp @@ -0,0 +1,207 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include +#include +#include + +namespace kiwano +{ +Button::Button() + : enabled_(true) + , status_(Status::Normal) +{ +} + +Button::Button(const Callback& click) + : Button() +{ + this->SetClickCallback(click); +} + +Button::Button(Callback const& click, Callback const& pressed, Callback const& mouse_over, Callback const& mouse_out) + : Button() +{ + this->SetClickCallback(click); + this->SetPressedCallback(pressed); + this->SetMouseOverCallback(mouse_over); + this->SetMouseOutCallback(mouse_out); +} + +Button::~Button() {} + +bool Button::IsEnable() const +{ + return enabled_; +} + +void Button::SetEnabled(bool enabled) +{ + if (enabled_ != enabled) + { + enabled_ = enabled; + } +} + +void Button::SetClickCallback(const Callback& func) +{ + click_callback_ = func; +} + +void Button::SetPressedCallback(const Callback& func) +{ + pressed_callback_ = func; +} + +void Button::SetReleasedCallback(const Callback& func) +{ + released_callback_ = func; +} + +void Button::SetMouseOverCallback(const Callback& func) +{ + mouse_over_callback_ = func; +} + +void Button::SetMouseOutCallback(const Callback& func) +{ + mouse_out_callback_ = func; +} + +void Button::SetStatus(Status status) +{ + if (status_ != status) + { + Status old_status = status_; + + if (status == Status::Normal) + { + Window::Instance().SetCursor(CursorType::Arrow); + + if (mouse_out_callback_) + mouse_out_callback_(this); + } + else if (status == Status::Hover) + { + Window::Instance().SetCursor(CursorType::Hand); + + if (old_status == Status::Pressed) + { + if (released_callback_) + released_callback_(this); + } + else + { + if (mouse_over_callback_) + mouse_over_callback_(this); + } + } + else if (status == Status::Pressed) + { + if (pressed_callback_) + pressed_callback_(this); + } + + status_ = status; + } +} + +Button::Status Button::GetStatus() const +{ + return status_; +} + +void Button::UpdateStatus(Event* evt) +{ + if (!enabled_) + return; + + if (evt->IsType()) + { + SetStatus(Status::Hover); + } + else if (evt->IsType()) + { + SetStatus(Status::Normal); + } + else if (evt->IsType() && status_ == Status::Hover) + { + SetStatus(Status::Pressed); + } + else if (evt->IsType() && status_ == Status::Pressed) + { + SetStatus(Status::Hover); + } + else if (evt->IsType()) + { + if (click_callback_) + click_callback_(this); + } +} + +SpriteButton::SpriteButton() + : SpriteButton(nullptr, nullptr, nullptr, nullptr) +{ +} + +SpriteButton::SpriteButton(Callback const& click) + : SpriteButton(click, nullptr, nullptr, nullptr) +{ +} + +SpriteButton::SpriteButton(Callback const& click, Callback const& pressed, Callback const& mouse_over, + Callback const& mouse_out) + : Button(click, pressed, mouse_over, mouse_out) +{ + SetResponsible(true); + + EventListener::Callback handler = Closure(this, &SpriteButton::UpdateStatus); + AddListener(handler); + AddListener(handler); + AddListener(handler); + AddListener(handler); + AddListener(handler); +} + +TextButton::TextButton() + : TextButton(nullptr, nullptr, nullptr, nullptr) +{ +} + +TextButton::TextButton(Callback const& click) + : TextButton(click, nullptr, nullptr, nullptr) +{ +} + +TextButton::TextButton(Callback const& click, Callback const& pressed, Callback const& mouse_over, + Callback const& mouse_out) + : Button(click, pressed, mouse_over, mouse_out) +{ + SetResponsible(true); + + EventListener::Callback handler = Closure(this, &TextButton::UpdateStatus); + AddListener(handler); + AddListener(handler); + AddListener(handler); + AddListener(handler); + AddListener(handler); +} + +} // namespace kiwano diff --git a/src/kiwano/2d/Button.h b/src/kiwano/2d/Button.h new file mode 100644 index 00000000..5947afe8 --- /dev/null +++ b/src/kiwano/2d/Button.h @@ -0,0 +1,164 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include +#include + +namespace kiwano +{ +KGE_DECLARE_SMART_PTR(Button); +KGE_DECLARE_SMART_PTR(SpriteButton); +KGE_DECLARE_SMART_PTR(TextButton); + +/** + * \~chinese + * @brief ť + */ +class KGE_API Button : public virtual ObjectBase +{ +public: + /// \~chinese + /// @brief ťص + using Callback = Function; + + Button(); + + /// \~chinese + /// @brief 찴ť + /// @param click ťص + explicit Button(Callback const& click); + + /// \~chinese + /// @brief 찴ť + /// @param click ťص + /// @param pressed ť»ص + /// @param mouse_over ťص + /// @param mouse_out ťƳص + Button(Callback const& click, Callback const& pressed, Callback const& mouse_over, Callback const& mouse_out); + + virtual ~Button(); + + /// \~chinese + /// @brief ȡť״̬ûǽ + bool IsEnable() const; + + /// \~chinese + /// @brief ðťû + void SetEnabled(bool enabled); + + /// \~chinese + /// @brief ðťĻص + void SetClickCallback(const Callback& func); + + /// \~chinese + /// @brief ðťʱĻص + void SetPressedCallback(const Callback& func); + + /// \~chinese + /// @brief ðţ̌ʱĻص + void SetReleasedCallback(const Callback& func); + + /// \~chinese + /// @brief 밴ťʱĻص + void SetMouseOverCallback(const Callback& func); + + /// \~chinese + /// @brief ƳťʱĻص + void SetMouseOutCallback(const Callback& func); + + /// \~chinese + /// @brief ť״̬ + enum class Status + { + Normal, ///< ͨ + Hover, ///< ڰť + Pressed ///< + }; + + /// \~chinese + /// @brief ðť״̬ + void SetStatus(Status status); + + /// \~chinese + /// @brief ȡť״̬ + Status GetStatus() const; + +protected: + /// \~chinese + /// @brief °ť״̬ + void UpdateStatus(Event* evt); + +private: + bool enabled_; + Status status_; + Callback click_callback_; + Callback pressed_callback_; + Callback released_callback_; + Callback mouse_over_callback_; + Callback mouse_out_callback_; +}; + +/// \~chinese +/// @brief 鰴ť +class SpriteButton + : public Sprite + , public Button +{ +public: + SpriteButton(); + + /// \~chinese + /// @brief 쾫鰴ť + /// @param click ťص + explicit SpriteButton(Callback const& click); + + /// \~chinese + /// @brief 쾫鰴ť + /// @param click ťص + /// @param pressed ť»ص + /// @param mouse_over ťص + /// @param mouse_out ťƳص + SpriteButton(Callback const& click, Callback const& pressed, Callback const& mouse_over, Callback const& mouse_out); +}; + +/// \~chinese +/// @brief ְť +class TextButton + : public TextActor + , public Button +{ +public: + TextButton(); + + /// \~chinese + /// @brief ְť + /// @param click ťص + explicit TextButton(Callback const& click); + + /// \~chinese + /// @brief ְť + /// @param click ťص + /// @param pressed ť»ص + /// @param mouse_over ťص + /// @param mouse_out ťƳص + TextButton(Callback const& click, Callback const& pressed, Callback const& mouse_over, Callback const& mouse_out); +}; +} // namespace kiwano diff --git a/src/kiwano/2d/Canvas.cpp b/src/kiwano/2d/Canvas.cpp index 0f009977..e2f1fc44 100644 --- a/src/kiwano/2d/Canvas.cpp +++ b/src/kiwano/2d/Canvas.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -20,334 +20,291 @@ #include #include -#include +#include namespace kiwano { - Canvas::Canvas() - : cache_expired_(false) - , stroke_width_(1.0f) - , stroke_style_(StrokeStyle::Miter) - { - } - - Canvas::~Canvas() - { - } - - void Canvas::BeginDraw() - { - InitRenderTargetAndBrushs(); - rt_->BeginDraw(); - } - - void Canvas::EndDraw() - { - InitRenderTargetAndBrushs(); - rt_->EndDraw(); - cache_expired_ = true; - } - - void Canvas::OnRender(RenderTarget* rt) - { - UpdateCache(); - - if (texture_cached_ && texture_cached_->IsValid()) - { - PrepareToRender(rt); - - Rect bitmap_rect(0.f, 0.f, texture_cached_->GetWidth(), texture_cached_->GetHeight()); - rt->DrawTexture(*texture_cached_, bitmap_rect, bitmap_rect); - } - } - - void Canvas::SetBrush(BrushPtr brush) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(brush); - } - - float Canvas::GetStrokeWidth() const - { - return stroke_width_; - } - - void Canvas::SetBrushTransform(Transform const& transform) - { - InitRenderTargetAndBrushs(); - rt_->SetTransform(transform.ToMatrix()); - } - - void Canvas::SetBrushTransform(Matrix3x2 const & transform) - { - InitRenderTargetAndBrushs(); - rt_->SetTransform(transform); - } - - void Canvas::PushLayerArea(LayerArea& area) - { - InitRenderTargetAndBrushs(); - rt_->PushLayer(area); - } - - void Canvas::PopLayerArea() - { - InitRenderTargetAndBrushs(); - rt_->PopLayer(); - } - - void Canvas::PushClipRect(Rect const& clip_rect) - { - InitRenderTargetAndBrushs(); - rt_->PushClipRect(clip_rect); - } - - void Canvas::PopClipRect() - { - InitRenderTargetAndBrushs(); - rt_->PopClipRect(); - } - - void Canvas::DrawLine(Point const& begin, Point const& end) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(stroke_brush_); - rt_->DrawLine( - begin, - end, - stroke_width_, - stroke_style_ - ); - cache_expired_ = true; - } - - void Canvas::DrawCircle(Point const& center, float radius) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(stroke_brush_); - rt_->DrawEllipse( - center, - Vec2(radius, radius), - stroke_width_, - stroke_style_ - ); - cache_expired_ = true; - } - - void Canvas::DrawEllipse(Point const& center, Vec2 const& radius) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(stroke_brush_); - rt_->DrawEllipse( - center, - radius, - stroke_width_, - stroke_style_ - ); - cache_expired_ = true; - } - - void Canvas::DrawRect(Rect const& rect) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(stroke_brush_); - rt_->DrawRectangle( - rect, - stroke_width_, - stroke_style_ - ); - cache_expired_ = true; - } - - void Canvas::DrawRoundedRect(Rect const& rect, Vec2 const& radius) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(stroke_brush_); - rt_->DrawRoundedRectangle( - rect, - radius, - stroke_width_, - stroke_style_ - ); - cache_expired_ = true; - } - - void Canvas::FillCircle(Point const& center, float radius) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(fill_brush_); - rt_->FillEllipse( - center, - Vec2(radius, radius) - ); - cache_expired_ = true; - } - - void Canvas::FillEllipse(Point const& center, Vec2 const& radius) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(fill_brush_); - rt_->FillEllipse( - center, - radius - ); - cache_expired_ = true; - } - - void Canvas::FillRect(Rect const& rect) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(fill_brush_); - rt_->FillRectangle( - rect - ); - cache_expired_ = true; - } - - void Canvas::FillRoundedRect(Rect const& rect, Vec2 const& radius) - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(fill_brush_); - rt_->FillRoundedRectangle( - rect, - radius - ); - cache_expired_ = true; - } - - void Canvas::DrawTexture(TexturePtr texture, const Rect* src_rect, const Rect* dest_rect) - { - if (texture) - { - InitRenderTargetAndBrushs(); - rt_->DrawTexture(*texture, src_rect, dest_rect); - cache_expired_ = true; - } - } - - void Canvas::DrawTextLayout(String const& text, Point const& point) - { - if (text.empty()) - return; - - TextLayout layout; - layout.SetStyle(text_style_); - layout.SetText(text); - DrawTextLayout(layout, point); - } - - void Canvas::DrawTextLayout(TextLayout const& layout, Point const& point) - { - InitRenderTargetAndBrushs(); - rt_->DrawTextLayout(layout, point); - } - - void Canvas::BeginPath(Point const& begin_pos) - { - geo_sink_.BeginPath(begin_pos); - } - - void Canvas::EndPath(bool closed) - { - geo_sink_.EndPath(closed); - } - - void Canvas::AddLine(Point const & point) - { - geo_sink_.AddLine(point); - } - - void Canvas::AddLines(Vector const& points) - { - geo_sink_.AddLines(points); - } - - void Canvas::AddBezier(Point const & point1, Point const & point2, Point const & point3) - { - geo_sink_.AddBezier(point1, point2, point3); - } - - void Canvas::AddArc(Point const & point, Size const & radius, float rotation, bool clockwise, bool is_small) - { - geo_sink_.AddArc(point, radius, rotation, clockwise, is_small); - } - - void Canvas::StrokePath() - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(stroke_brush_); - rt_->DrawGeometry( - geo_sink_.GetGeometry(), - stroke_width_, - stroke_style_ - ); - cache_expired_ = true; - } - - void Canvas::FillPath() - { - InitRenderTargetAndBrushs(); - rt_->SetCurrentBrush(fill_brush_); - rt_->FillGeometry( - geo_sink_.GetGeometry() - ); - cache_expired_ = true; - } - - void Canvas::Clear() - { - InitRenderTargetAndBrushs(); - rt_->Clear(); - cache_expired_ = true; - } - - void Canvas::Clear(Color const& clear_color) - { - InitRenderTargetAndBrushs(); - rt_->Clear(clear_color); - cache_expired_ = true; - } - - TexturePtr Canvas::ExportToTexture() const - { - UpdateCache(); - return texture_cached_; - } - - void Canvas::InitRenderTargetAndBrushs() - { - if (!rt_) - { - Renderer::instance().CreateTextureRenderTarget(rt_); - } - - if (!stroke_brush_) - { - stroke_brush_ = new Brush; - stroke_brush_->SetColor(Color::White); - } - - if (!fill_brush_) - { - fill_brush_ = new Brush; - fill_brush_->SetColor(Color::White); - } - } - - void Canvas::UpdateCache() const - { - if (cache_expired_ && rt_) - { - if (!texture_cached_) - { - texture_cached_ = new Texture; - } - - if (rt_->GetOutput(*texture_cached_)) - { - cache_expired_ = false; - } - } - } - +Canvas::Canvas() + : cache_expired_(false) + , stroke_width_(1.0f) + , stroke_style_() +{ } + +Canvas::~Canvas() {} + +void Canvas::BeginDraw() +{ + InitRenderTargetAndBrushs(); + ctx_->BeginDraw(); +} + +void Canvas::EndDraw() +{ + InitRenderTargetAndBrushs(); + ctx_->EndDraw(); + cache_expired_ = true; +} + +void Canvas::OnRender(RenderContext& ctx) +{ + UpdateCache(); + + if (texture_cached_ && texture_cached_->IsValid()) + { + PrepareToRender(ctx); + + Rect bitmap_rect(0.f, 0.f, texture_cached_->GetWidth(), texture_cached_->GetHeight()); + ctx.DrawTexture(*texture_cached_, bitmap_rect, bitmap_rect); + } +} + +void Canvas::SetBrush(BrushPtr brush) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(brush); +} + +float Canvas::GetStrokeWidth() const +{ + return stroke_width_; +} + +void Canvas::SetBrushTransform(Transform const& transform) +{ + InitRenderTargetAndBrushs(); + ctx_->SetTransform(transform.ToMatrix()); +} + +void Canvas::SetBrushTransform(Matrix3x2 const& transform) +{ + InitRenderTargetAndBrushs(); + ctx_->SetTransform(transform); +} + +void Canvas::PushLayerArea(LayerArea& area) +{ + InitRenderTargetAndBrushs(); + ctx_->PushLayer(area); +} + +void Canvas::PopLayerArea() +{ + InitRenderTargetAndBrushs(); + ctx_->PopLayer(); +} + +void Canvas::PushClipRect(Rect const& clip_rect) +{ + InitRenderTargetAndBrushs(); + ctx_->PushClipRect(clip_rect); +} + +void Canvas::PopClipRect() +{ + InitRenderTargetAndBrushs(); + ctx_->PopClipRect(); +} + +void Canvas::DrawLine(Point const& begin, Point const& end) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(stroke_brush_); + ctx_->DrawLine(begin, end, stroke_width_, stroke_style_); + cache_expired_ = true; +} + +void Canvas::DrawCircle(Point const& center, float radius) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(stroke_brush_); + ctx_->DrawEllipse(center, Vec2(radius, radius), stroke_width_, stroke_style_); + cache_expired_ = true; +} + +void Canvas::DrawEllipse(Point const& center, Vec2 const& radius) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(stroke_brush_); + ctx_->DrawEllipse(center, radius, stroke_width_, stroke_style_); + cache_expired_ = true; +} + +void Canvas::DrawRect(Rect const& rect) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(stroke_brush_); + ctx_->DrawRectangle(rect, stroke_width_, stroke_style_); + cache_expired_ = true; +} + +void Canvas::DrawRoundedRect(Rect const& rect, Vec2 const& radius) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(stroke_brush_); + ctx_->DrawRoundedRectangle(rect, radius, stroke_width_, stroke_style_); + cache_expired_ = true; +} + +void Canvas::FillCircle(Point const& center, float radius) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(fill_brush_); + ctx_->FillEllipse(center, Vec2(radius, radius)); + cache_expired_ = true; +} + +void Canvas::FillEllipse(Point const& center, Vec2 const& radius) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(fill_brush_); + ctx_->FillEllipse(center, radius); + cache_expired_ = true; +} + +void Canvas::FillRect(Rect const& rect) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(fill_brush_); + ctx_->FillRectangle(rect); + cache_expired_ = true; +} + +void Canvas::FillRoundedRect(Rect const& rect, Vec2 const& radius) +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(fill_brush_); + ctx_->FillRoundedRectangle(rect, radius); + cache_expired_ = true; +} + +void Canvas::DrawTexture(TexturePtr texture, const Rect* src_rect, const Rect* dest_rect) +{ + if (texture) + { + InitRenderTargetAndBrushs(); + ctx_->DrawTexture(*texture, src_rect, dest_rect); + cache_expired_ = true; + } +} + +void Canvas::DrawTextLayout(String const& text, Point const& point) +{ + if (text.empty()) + return; + + TextLayout layout; + layout.SetStyle(text_style_); + layout.SetText(text); + DrawTextLayout(layout, point); +} + +void Canvas::DrawTextLayout(TextLayout const& layout, Point const& point) +{ + InitRenderTargetAndBrushs(); + ctx_->DrawTextLayout(layout, point); +} + +void Canvas::BeginPath(Point const& begin_pos) +{ + geo_sink_.BeginPath(begin_pos); +} + +void Canvas::EndPath(bool closed) +{ + geo_sink_.EndPath(closed); +} + +void Canvas::AddLine(Point const& point) +{ + geo_sink_.AddLine(point); +} + +void Canvas::AddLines(Vector const& points) +{ + geo_sink_.AddLines(points); +} + +void Canvas::AddBezier(Point const& point1, Point const& point2, Point const& point3) +{ + geo_sink_.AddBezier(point1, point2, point3); +} + +void Canvas::AddArc(Point const& point, Size const& radius, float rotation, bool clockwise, bool is_small) +{ + geo_sink_.AddArc(point, radius, rotation, clockwise, is_small); +} + +void Canvas::StrokePath() +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(stroke_brush_); + ctx_->DrawGeometry(geo_sink_.GetGeometry(), stroke_width_, stroke_style_); + cache_expired_ = true; +} + +void Canvas::FillPath() +{ + InitRenderTargetAndBrushs(); + ctx_->SetCurrentBrush(fill_brush_); + ctx_->FillGeometry(geo_sink_.GetGeometry()); + cache_expired_ = true; +} + +void Canvas::Clear() +{ + InitRenderTargetAndBrushs(); + ctx_->Clear(); + cache_expired_ = true; +} + +void Canvas::Clear(Color const& clear_color) +{ + InitRenderTargetAndBrushs(); + ctx_->Clear(clear_color); + cache_expired_ = true; +} + +TexturePtr Canvas::ExportToTexture() const +{ + UpdateCache(); + return texture_cached_; +} + +void Canvas::InitRenderTargetAndBrushs() +{ + if (!ctx_) + { + Renderer::Instance().CreateTextureRenderTarget(ctx_); + } + + if (!stroke_brush_) + { + stroke_brush_ = new Brush; + stroke_brush_->SetColor(Color::White); + } + + if (!fill_brush_) + { + fill_brush_ = new Brush; + fill_brush_->SetColor(Color::White); + } +} + +void Canvas::UpdateCache() const +{ + if (cache_expired_ && ctx_) + { + if (!texture_cached_) + { + texture_cached_ = new Texture; + } + + if (ctx_->GetOutput(*texture_cached_)) + { + cache_expired_ = false; + } + } +} + +} // namespace kiwano diff --git a/src/kiwano/2d/Canvas.h b/src/kiwano/2d/Canvas.h index d1530ba2..064b4b3d 100644 --- a/src/kiwano/2d/Canvas.h +++ b/src/kiwano/2d/Canvas.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -20,315 +20,313 @@ #pragma once #include -#include -#include +#include +#include namespace kiwano { - KGE_DECLARE_SMART_PTR(Canvas); +KGE_DECLARE_SMART_PTR(Canvas); - /** - * \addtogroup Actors - * @{ - */ +/** + * \addtogroup Actors + * @{ + */ - /** - * \~chinese - * @brief ڻͼԪ - */ - class KGE_API Canvas - : public Actor - { - public: - /// \~chinese - /// @brief ջ - Canvas(); +/** + * \~chinese + * @brief ڻͼԪ + */ +class KGE_API Canvas : public Actor +{ +public: + /// \~chinese + /// @brief ջ + Canvas(); - virtual ~Canvas(); + virtual ~Canvas(); - /// \~chinese - /// @brief ʼͼ - void BeginDraw(); + /// \~chinese + /// @brief ʼͼ + void BeginDraw(); - /// \~chinese - /// @brief ͼ - void EndDraw(); + /// \~chinese + /// @brief ͼ + void EndDraw(); - /// \~chinese - /// @brief ߶ - /// @param begin ߶ - /// @param end ߶յ - void DrawLine(Point const& begin, Point const& end); + /// \~chinese + /// @brief ߶ + /// @param begin ߶ + /// @param end ߶յ + void DrawLine(Point const& begin, Point const& end); - /// \~chinese - /// @brief Բα߿ - /// @param center Բԭ - /// @param radius Բΰ뾶 - void DrawCircle(Point const& center, float radius); + /// \~chinese + /// @brief Բα߿ + /// @param center Բԭ + /// @param radius Բΰ뾶 + void DrawCircle(Point const& center, float radius); - /// \~chinese - /// @brief Բα߿ - /// @param center Բԭ - /// @param radius Բ뾶 - void DrawEllipse(Point const& center, Vec2 const& radius); + /// \~chinese + /// @brief Բα߿ + /// @param center Բԭ + /// @param radius Բ뾶 + void DrawEllipse(Point const& center, Vec2 const& radius); - /// \~chinese - /// @brief α߿ - /// @param rect - void DrawRect(Rect const& rect); + /// \~chinese + /// @brief α߿ + /// @param rect + void DrawRect(Rect const& rect); - /// \~chinese - /// @brief ԲǾα߿ - /// @param rect - /// @param radius Բǰ뾶 - void DrawRoundedRect(Rect const& rect, Vec2 const& radius); + /// \~chinese + /// @brief ԲǾα߿ + /// @param rect + /// @param radius Բǰ뾶 + void DrawRoundedRect(Rect const& rect, Vec2 const& radius); - /// \~chinese - /// @brief Բ - /// @param center Բԭ - /// @param radius Բΰ뾶 - void FillCircle(Point const& center, float radius); + /// \~chinese + /// @brief Բ + /// @param center Բԭ + /// @param radius Բΰ뾶 + void FillCircle(Point const& center, float radius); - /// \~chinese - /// @brief Բ - /// @param center Բԭ - /// @param radius Բ뾶 - void FillEllipse(Point const& center, Vec2 const& radius); + /// \~chinese + /// @brief Բ + /// @param center Բԭ + /// @param radius Բ뾶 + void FillEllipse(Point const& center, Vec2 const& radius); - /// \~chinese - /// @brief - /// @param rect - void FillRect(Rect const& rect); + /// \~chinese + /// @brief + /// @param rect + void FillRect(Rect const& rect); - /// \~chinese - /// @brief ԲǾ - /// @param rect - /// @param radius Բǰ뾶 - void FillRoundedRect(Rect const& rect, Vec2 const& radius); + /// \~chinese + /// @brief ԲǾ + /// @param rect + /// @param radius Բǰ뾶 + void FillRoundedRect(Rect const& rect, Vec2 const& radius); - /// \~chinese - /// @brief - /// @param texture - /// @param src_rect ü - /// @param dest_rect Ŀ - void DrawTexture(TexturePtr texture, const Rect* src_rect = nullptr, const Rect* dest_rect = nullptr); + /// \~chinese + /// @brief + /// @param texture + /// @param src_rect ü + /// @param dest_rect Ŀ + void DrawTexture(TexturePtr texture, const Rect* src_rect = nullptr, const Rect* dest_rect = nullptr); - /// \~chinese - /// @brief ֲ - /// @param text - /// @param point ֵλ - void DrawTextLayout(String const& text, Point const& point); + /// \~chinese + /// @brief ֲ + /// @param text + /// @param point ֵλ + void DrawTextLayout(String const& text, Point const& point); - /// \~chinese - /// @brief ֲ - /// @param layout ֲ - /// @param point Ʋֵλ - void DrawTextLayout(TextLayout const& layout, Point const& point); + /// \~chinese + /// @brief ֲ + /// @param layout ֲ + /// @param point Ʋֵλ + void DrawTextLayout(TextLayout const& layout, Point const& point); - /// \~chinese - /// @brief ʼ· - /// @param begin_pos ·ʼ - void BeginPath(Point const& begin_pos); + /// \~chinese + /// @brief ʼ· + /// @param begin_pos ·ʼ + void BeginPath(Point const& begin_pos); - /// \~chinese - /// @brief · - /// @param closed ·Ƿպ - void EndPath(bool closed = false); + /// \~chinese + /// @brief · + /// @param closed ·Ƿպ + void EndPath(bool closed = false); - /// \~chinese - /// @brief һ߶ - /// @param point ˵ - void AddLine(Point const& point); + /// \~chinese + /// @brief һ߶ + /// @param point ˵ + void AddLine(Point const& point); - /// \~chinese - /// @brief Ӷ߶ - /// @param points ˵㼯 - void AddLines(Vector const& points); + /// \~chinese + /// @brief Ӷ߶ + /// @param points ˵㼯 + void AddLines(Vector const& points); - /// \~chinese - /// @brief һη - /// @param point1 ߵĵһƵ - /// @param point2 ߵĵڶƵ - /// @param point3 ߵյ - void AddBezier(Point const& point1, Point const& point2, Point const& point3); + /// \~chinese + /// @brief һη + /// @param point1 ߵĵһƵ + /// @param point2 ߵĵڶƵ + /// @param point3 ߵյ + void AddBezier(Point const& point1, Point const& point2, Point const& point3); - /// \~chinese - /// @brief ӻ - /// @param point յ - /// @param radius Բ뾶 - /// @param rotation ԲתǶ - /// @param clockwise ˳ʱ or ʱ - /// @param is_small ǷȡС 180 Ļ - void AddArc(Point const& point, Size const& radius, float rotation, bool clockwise = true, bool is_small = true); + /// \~chinese + /// @brief ӻ + /// @param point յ + /// @param radius Բ뾶 + /// @param rotation ԲתǶ + /// @param clockwise ˳ʱ or ʱ + /// @param is_small ǷȡС 180 Ļ + void AddArc(Point const& point, Size const& radius, float rotation, bool clockwise = true, bool is_small = true); - /// \~chinese - /// @brief ߵķʽ· - void StrokePath(); + /// \~chinese + /// @brief ߵķʽ· + void StrokePath(); - /// \~chinese - /// @brief ķʽ· - void FillPath(); + /// \~chinese + /// @brief ķʽ· + void FillPath(); - /// \~chinese - /// @brief ջ - void Clear(); + /// \~chinese + /// @brief ջ + void Clear(); - /// \~chinese - /// @brief ջ - /// @param clear_color ɫ - void Clear(Color const& clear_color); + /// \~chinese + /// @brief ջ + /// @param clear_color ɫ + void Clear(Color const& clear_color); - /// \~chinese - /// @brief ɫ - /// @param color ɫ - void SetFillColor(Color const& color); + /// \~chinese + /// @brief ɫ + /// @param color ɫ + void SetFillColor(Color const& color); - /// \~chinese - /// @brief 仭ˢ - /// @param[in] brush 仭ˢ - void SetFillBrush(BrushPtr brush); + /// \~chinese + /// @brief 仭ˢ + /// @param[in] brush 仭ˢ + void SetFillBrush(BrushPtr brush); - /// \~chinese - /// @brief ɫ - /// @param color ɫ - void SetStrokeColor(Color const& color); + /// \~chinese + /// @brief ɫ + /// @param color ɫ + void SetStrokeColor(Color const& color); - /// \~chinese - /// @brief ˢ - /// @param[in] brush ˢ - void SetStrokeBrush(BrushPtr brush); + /// \~chinese + /// @brief ˢ + /// @param[in] brush ˢ + void SetStrokeBrush(BrushPtr brush); - /// \~chinese - /// @brief - /// @param width - void SetStrokeWidth(float width); + /// \~chinese + /// @brief + /// @param width + void SetStrokeWidth(float width); - /// \~chinese - /// @brief ʽ - /// @param stroke_style ʽ - void SetStrokeStyle(StrokeStyle stroke_style); + /// \~chinese + /// @brief ʽ + /// @param stroke_style ʽ + void SetStrokeStyle(const StrokeStyle& stroke_style); - /// \~chinese - /// @brief ֻˢʽ - /// @param text_style ֻˢʽ - void SetTextStyle(TextStyle const& text_style); + /// \~chinese + /// @brief ֻˢʽ + /// @param text_style ֻˢʽ + void SetTextStyle(TextStyle const& text_style); - /// \~chinese - /// @brief ûˢ - /// @param[in] brush ˢ - void SetBrush(BrushPtr brush); + /// \~chinese + /// @brief ûˢ + /// @param[in] brush ˢ + void SetBrush(BrushPtr brush); - /// \~chinese - /// @brief ûˢά任 - /// @param transform ά任 - void SetBrushTransform(Transform const& transform); + /// \~chinese + /// @brief ûˢά任 + /// @param transform ά任 + void SetBrushTransform(Transform const& transform); - /// \~chinese - /// @brief ûˢά任 - /// @param transform ά任 - void SetBrushTransform(Matrix3x2 const& transform); + /// \~chinese + /// @brief ûˢά任 + /// @param transform ά任 + void SetBrushTransform(Matrix3x2 const& transform); - /// \~chinese - /// @brief һͼ - /// @param area ͼ - void PushLayerArea(LayerArea& area); + /// \~chinese + /// @brief һͼ + /// @param area ͼ + void PushLayerArea(LayerArea& area); - /// \~chinese - /// @brief ɾӵͼ - void PopLayerArea(); + /// \~chinese + /// @brief ɾӵͼ + void PopLayerArea(); - /// \~chinese - /// @brief һü - /// @param clip_rect ü - void PushClipRect(Rect const& clip_rect); + /// \~chinese + /// @brief һü + /// @param clip_rect ü + void PushClipRect(Rect const& clip_rect); - /// \~chinese - /// @brief ɾӵIJü - void PopClipRect(); + /// \~chinese + /// @brief ɾӵIJü + void PopClipRect(); - /// \~chinese - /// @brief ȡ - float GetStrokeWidth() const; + /// \~chinese + /// @brief ȡ + float GetStrokeWidth() const; - /// \~chinese - /// @brief ȡ仭ˢ - BrushPtr GetFillBrush() const; + /// \~chinese + /// @brief ȡ仭ˢ + BrushPtr GetFillBrush() const; - /// \~chinese - /// @brief ȡˢ - BrushPtr GetStrokeBrush() const; + /// \~chinese + /// @brief ȡˢ + BrushPtr GetStrokeBrush() const; - /// \~chinese - /// @brief - TexturePtr ExportToTexture() const; + /// \~chinese + /// @brief + TexturePtr ExportToTexture() const; - void OnRender(RenderTarget* rt) override; + void OnRender(RenderContext& ctx) override; - private: - void InitRenderTargetAndBrushs(); +private: + void InitRenderTargetAndBrushs(); - void UpdateCache() const; + void UpdateCache() const; - private: - float stroke_width_; - TextStyle text_style_; - StrokeStyle stroke_style_; - GeometrySink geo_sink_; - BrushPtr fill_brush_; - BrushPtr stroke_brush_; +private: + float stroke_width_; + TextStyle text_style_; + StrokeStyle stroke_style_; + GeometrySink geo_sink_; + BrushPtr fill_brush_; + BrushPtr stroke_brush_; - mutable bool cache_expired_; - mutable TexturePtr texture_cached_; - mutable TextureRenderTargetPtr rt_; - }; + mutable bool cache_expired_; + mutable TexturePtr texture_cached_; + mutable TextureRenderContextPtr ctx_; +}; - /** @} */ - - inline void Canvas::SetStrokeWidth(float width) - { - stroke_width_ = std::max(width, 0.f); - } - - inline void Canvas::SetStrokeStyle(StrokeStyle stroke_style) - { - stroke_style_ = stroke_style; - } - - inline void Canvas::SetTextStyle(TextStyle const& text_style) - { - text_style_ = text_style; - } - - inline void Canvas::SetStrokeColor(Color const& color) - { - InitRenderTargetAndBrushs(); - stroke_brush_->SetColor(color); - } - - inline void Canvas::SetFillColor(Color const& color) - { - InitRenderTargetAndBrushs(); - fill_brush_->SetColor(color); - } - - inline void Canvas::SetFillBrush(BrushPtr brush) - { - fill_brush_ = brush; - } - - inline void Canvas::SetStrokeBrush(BrushPtr brush) - { - stroke_brush_ = brush; - } - - inline BrushPtr Canvas::GetFillBrush() const - { - return fill_brush_; - } - - inline BrushPtr Canvas::GetStrokeBrush() const - { - return stroke_brush_; - } +/** @} */ +inline void Canvas::SetStrokeWidth(float width) +{ + stroke_width_ = std::max(width, 0.f); } + +inline void Canvas::SetStrokeStyle(const StrokeStyle& stroke_style) +{ + stroke_style_ = stroke_style; +} + +inline void Canvas::SetTextStyle(TextStyle const& text_style) +{ + text_style_ = text_style; +} + +inline void Canvas::SetStrokeColor(Color const& color) +{ + InitRenderTargetAndBrushs(); + stroke_brush_->SetColor(color); +} + +inline void Canvas::SetFillColor(Color const& color) +{ + InitRenderTargetAndBrushs(); + fill_brush_->SetColor(color); +} + +inline void Canvas::SetFillBrush(BrushPtr brush) +{ + fill_brush_ = brush; +} + +inline void Canvas::SetStrokeBrush(BrushPtr brush) +{ + stroke_brush_ = brush; +} + +inline BrushPtr Canvas::GetFillBrush() const +{ + return fill_brush_; +} + +inline BrushPtr Canvas::GetStrokeBrush() const +{ + return stroke_brush_; +} +} // namespace kiwano diff --git a/src/kiwano/2d/DebugActor.cpp b/src/kiwano/2d/DebugActor.cpp index ae47b7a5..8e40b65a 100644 --- a/src/kiwano/2d/DebugActor.cpp +++ b/src/kiwano/2d/DebugActor.cpp @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -19,127 +19,128 @@ // THE SOFTWARE. #include -#include #include +#include #include #pragma comment(lib, "psapi.lib") namespace kiwano { - namespace - { - class comma_numpunct : public std::numpunct - { - private: - virtual wchar_t do_thousands_sep() const override - { - return L','; - } +namespace +{ +class comma_numpunct : public std::numpunct +{ +private: + virtual wchar_t do_thousands_sep() const override + { + return L','; + } - virtual std::string do_grouping() const override - { - return "\03"; - } - }; + virtual std::string do_grouping() const override + { + return "\03"; + } +}; +} // namespace - std::locale comma_locale(std::locale(), new comma_numpunct); - } +DebugActor::DebugActor() +{ + SetName(L"kiwano-debug-actor"); + SetPosition(Point{ 10, 10 }); + SetResponsible(true); + SetCascadeOpacityEnabled(true); - DebugActor::DebugActor() - { - SetName(L"kiwano-debug-actor"); - SetPosition(Point{ 10, 10 }); - SetResponsible(true); - SetCascadeOpacityEnabled(true); + comma_locale_ = std::locale(std::locale(), new comma_numpunct); - background_brush_ = new Brush; - background_brush_->SetColor(Color(0.0f, 0.0f, 0.0f, 0.7f)); + background_brush_ = new Brush; + background_brush_->SetColor(Color(0.0f, 0.0f, 0.0f, 0.7f)); - debug_text_ = new TextActor; - debug_text_->SetPosition(Point{ 10, 10 }); - this->AddChild(debug_text_); + BrushPtr fill_brush = new Brush; + fill_brush->SetColor(Color::White); - TextStyle style; - style.font_family = L"Arial"; - style.font_size = 16.f; - style.font_weight = FontWeight::Normal; - style.line_spacing = 20.f; - debug_text_->SetStyle(style); - debug_text_->SetFillColor(Color::White); + TextStyle style; + style.font_family = L"Arial"; + style.font_size = 16.f; + style.font_weight = FontWeight::Normal; + style.line_spacing = 20.f; + style.fill_brush = fill_brush; + debug_text_.SetStyle(style); - AddListener([=](Event&) { SetOpacity(0.4f); }); - AddListener([=](Event&) { SetOpacity(1.f); }); - } + AddListener([=](Event*) { SetOpacity(0.4f); }); + AddListener([=](Event*) { SetOpacity(1.f); }); +} - DebugActor::~DebugActor() - { - } +DebugActor::~DebugActor() {} - void DebugActor::OnRender(RenderTarget* rt) - { - rt->SetCurrentBrush(background_brush_); - rt->FillRoundedRectangle(GetBounds(), Vec2{ 5.f, 5.f }); - } +void DebugActor::OnRender(RenderContext& ctx) +{ + ctx.SetCurrentBrush(background_brush_); + ctx.FillRoundedRectangle(GetBounds(), Vec2{ 5.f, 5.f }); + ctx.DrawTextLayout(debug_text_, Point(10, 10)); +} - void DebugActor::OnUpdate(Duration dt) - { - KGE_NOT_USED(dt); +void DebugActor::OnUpdate(Duration dt) +{ + KGE_NOT_USED(dt); - frame_time_.push_back(Time::Now()); - while (frame_time_.back() - frame_time_.front() >= Duration::Second) - { - frame_time_.erase(frame_time_.begin()); - } + frame_time_.push_back(Time::Now()); + while (frame_time_.back() - frame_time_.front() >= Duration::Second) + { + frame_time_.erase(frame_time_.begin()); + } - StringStream ss; + StringStream ss; - // For formatting integers with commas - (void)ss.imbue(comma_locale); + // For formatting integers with commas + (void)ss.imbue(comma_locale_); - ss << "Fps: " << frame_time_.size() << std::endl; + ss << "Fps: " << frame_time_.size() << std::endl; #if defined(KGE_DEBUG) - if (ObjectBase::IsTracingLeaks()) - { - ss << "Objects: " << ObjectBase::GetTracingObjects().size() << std::endl; - } + if (ObjectBase::IsTracingLeaks()) + { + ss << "Objects: " << ObjectBase::GetTracingObjects().size() << std::endl; + } #endif - ss << "Render: " << Renderer::instance().GetStatus().duration.Milliseconds() << "ms" << std::endl; + ss << "Render: " << Renderer::Instance().GetStatus().duration.Milliseconds() << "ms" << std::endl; - ss << "Primitives / sec: " << std::fixed << Renderer::instance().GetStatus().primitives * frame_time_.size() << std::endl; + ss << "Primitives / sec: " << std::fixed << Renderer::Instance().GetStatus().primitives * frame_time_.size() + << std::endl; - ss << "Memory: "; - { - PROCESS_MEMORY_COUNTERS_EX pmc; - GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)); + ss << "Memory: "; + { + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)); - if (pmc.PrivateUsage > 1024 * 1024) - { - ss << pmc.PrivateUsage / (1024 * 1024) << "Mb "; - pmc.PrivateUsage %= (1024 * 1024); - } + if (pmc.PrivateUsage > 1024 * 1024) + { + ss << pmc.PrivateUsage / (1024 * 1024) << "Mb "; + pmc.PrivateUsage %= (1024 * 1024); + } - ss << pmc.PrivateUsage / 1024 << "Kb"; - } + ss << pmc.PrivateUsage / 1024 << "Kb"; + } - debug_text_->SetText(ss.str()); + debug_text_.SetText(ss.str()); + debug_text_.Update(); - if (debug_text_->GetWidth() > GetWidth() - 20) - { - SetWidth(20 + debug_text_->GetWidth()); - } - - if (debug_text_->GetHeight() > GetHeight() - 20) - { - SetHeight(20 + debug_text_->GetHeight()); - } - } - - bool DebugActor::CheckVisibilty(RenderTarget* rt) const - { - return true; - } + Size layout_size = debug_text_.GetLayoutSize(); + if (layout_size.x > GetWidth() - 20) + { + SetWidth(20 + layout_size.x); + } + if (layout_size.y > GetHeight() - 20) + { + SetHeight(20 + layout_size.y); + } } + +bool DebugActor::CheckVisibility(RenderContext& ctx) const +{ + return true; +} + +} // namespace kiwano diff --git a/src/kiwano/2d/DebugActor.h b/src/kiwano/2d/DebugActor.h index e35f2344..e8d33e2e 100644 --- a/src/kiwano/2d/DebugActor.h +++ b/src/kiwano/2d/DebugActor.h @@ -1,15 +1,15 @@ // Copyright (c) 2016-2018 Kiwano - Nomango -// +// // 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 @@ -20,42 +20,39 @@ #pragma once #include -#include -#include -#include +#include namespace kiwano { - /** - * \addtogroup Actors - * @{ - */ +/** + * \addtogroup Actors + * @{ + */ - /** - * \~chinese - * @brief Խڵ - */ - class KGE_API DebugActor - : public Actor - { - public: - DebugActor(); +/** + * \~chinese + * @brief Խڵ + */ +class KGE_API DebugActor : public Actor +{ +public: + DebugActor(); - virtual ~DebugActor(); + virtual ~DebugActor(); - void OnRender(RenderTarget* rt) override; + void OnRender(RenderContext& ctx) override; - void OnUpdate(Duration dt) override; + void OnUpdate(Duration dt) override; - protected: - bool CheckVisibilty(RenderTarget* rt) const override; +protected: + bool CheckVisibility(RenderContext& ctx) const override; - private: - BrushPtr background_brush_; - TextActorPtr debug_text_; - Vector